from sqlalchemy import ForeignKey, Integer, Text, UniqueConstraint
from sqlalchemy.orm import Mapped, mapped_column

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


class RepoBinding(Base, TimestampMixin):
    """Maps a git repository to the Scribe project it represents.

    The SessionStart hook sends the working repo's normalized remote
    (`repo_key`, e.g. ``git.fabledsword.com/bvandeusen/fabledscribe``) and the
    server resolves it to a project — so the operator can work across many
    repos/projects without pinning a project id in plugin config. The key is a
    *stable identifier* (the git remote), not a dir-name heuristic.
    """

    __tablename__ = "repo_bindings"
    __table_args__ = (
        UniqueConstraint("user_id", "repo_key", name="uq_repo_bindings_user_repo"),
    )

    id: Mapped[int] = mapped_column(primary_key=True)
    user_id: Mapped[int] = mapped_column(
        Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False
    )
    project_id: Mapped[int] = mapped_column(
        Integer, ForeignKey("projects.id", ondelete="CASCADE"), nullable=False
    )
    repo_key: Mapped[str] = mapped_column(Text, nullable=False)
    # The branch the coverage refresh reads for this binding (#2873); NULL =
    # the forge's default branch. Chosen at bind time so a dev-first project
    # can have its ledger follow dev instead of waiting for the merge.
    ref: Mapped[str | None] = mapped_column(Text, nullable=True)

    def to_dict(self) -> dict:
        return {
            "id": self.id,
            "user_id": self.user_id,
            "project_id": self.project_id,
            "repo_key": self.repo_key,
            "ref": self.ref,
            "created_at": iso(self.created_at),
            "updated_at": iso(self.updated_at),
        }
