from datetime import datetime

from sqlalchemy import BigInteger, ForeignKey, Text
from sqlalchemy.orm import Mapped, mapped_column

from scribe.models import Base
from scribe.models.base import CreatedAtMixin, iso


class RuleVersion(Base, CreatedAtMixin):
    """One snapshot of a rule's text, taken before an edit overwrote it.

    THE SIBLING NOTES ALREADY HAD. `note_versions` has existed for a long
    time, and the design-system note calls its history "the changelog". Rules
    — which BIND BEHAVIOUR on every session that loads them — had nothing, so
    an edit destroyed what the rule used to say. Rescoping rule 79 on
    2026-08-29 meant hand-copying the superseded statement into a task log to
    keep it (#3237). The more consequential record had the weaker protection.

    `CreatedAtMixin`, not `TimestampMixin`: a version is an EVENT. It is
    written once and never updated, so an `updated_at` on it would be a column
    that can only ever lie.

    WHAT IS DELIBERATELY DIFFERENT FROM NoteVersion (milestone 323):

    - `user_id` is the ACTOR — who made the edit — where NoteVersion's is the
      owner, because `update_note` passes an owner-scoped id. For an audit
      trail over a binding instruction, "who changed this" is the question
      being asked, and a rule is editable by anyone with rulebook access.
    - No `pin_kind` / `pin_label`. Those exist so a note's version can survive
      autosave pruning. Nothing prunes here, so a pin would protect a row that
      was never at risk.
    - TEXT ONLY. A rule's Systems and its typed relations are edges with their
      own lifecycle; folding them in would make one word, "version", mean two
      different things — the rule's wording, and the rule's place in the
      graph. `verified_at` is likewise absent: it is a stamp about a check,
      not a property of the text, and step 2's snapshot is taken before
      update_rule clears it precisely so the history shows the check that was
      in force when this wording was written.
    """

    __tablename__ = "rule_versions"

    id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
    rule_id: Mapped[int] = mapped_column(
        BigInteger, ForeignKey("rules.id", ondelete="CASCADE"), index=True
    )
    # The actor. SET NULL rather than CASCADE: deleting a user must not erase
    # the history of the rules they edited — the edit still happened, and the
    # rule is still binding because of it. NoteVersion cascades because a
    # note's versions belong to its owner; a rule's belong to the rule.
    user_id: Mapped[int | None] = mapped_column(
        BigInteger, ForeignKey("users.id", ondelete="SET NULL"), nullable=True
    )

    title: Mapped[str] = mapped_column(Text, default="")
    statement: Mapped[str] = mapped_column(Text, default="")
    why: Mapped[str | None] = mapped_column(Text, nullable=True)
    how_to_apply: Mapped[str | None] = mapped_column(Text, nullable=True)
    when_to_apply: Mapped[str | None] = mapped_column(Text, nullable=True)
    tier: Mapped[str | None] = mapped_column(Text, nullable=True)
    verify_with: Mapped[str | None] = mapped_column(Text, nullable=True)
    expires_when: Mapped[str | None] = mapped_column(Text, nullable=True)

    def to_dict(self, include_text: bool = True) -> dict:
        """The row. `include_text=False` gives the listing form.

        A rule's `statement` and `why` run to thousands of characters — rule
        149's `why` alone is longer than most notes — so a history LIST that
        carried every field would be unreadable and expensive. The listing
        answers "when, and by whom"; opening one answers "and what did it
        say". Same split NoteVersion makes with `include_body`.
        """
        out: dict = {
            "id": self.id,
            "rule_id": self.rule_id,
            "user_id": self.user_id,
            "title": self.title,
            "created_at": iso(self.created_at),
        }
        if include_text:
            out.update({
                "statement": self.statement,
                "why": self.why or "",
                "how_to_apply": self.how_to_apply or "",
                "when_to_apply": self.when_to_apply or "",
                "tier": self.tier or "",
                "verify_with": self.verify_with or "",
                "expires_when": self.expires_when or "",
            })
        return out
