import uuid
from datetime import datetime

from sqlalchemy import (
    Boolean,
    DateTime,
    ForeignKey,
    Index,
    String,
    Text,
    UniqueConstraint,
)
from sqlalchemy.dialects.postgresql import JSONB, UUID
from sqlalchemy.orm import Mapped, mapped_column, relationship

from api.models.base import Base, TimestampMixin, new_uuid7


class ReviewContract(Base, TimestampMixin):
    """Replaceable projection of one authoritative review-contract revision."""

    __tablename__ = "review_contracts"

    id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True), primary_key=True, default=new_uuid7
    )
    contract_key: Mapped[str] = mapped_column(String(160), nullable=False, index=True)
    schema_version: Mapped[str] = mapped_column(String(40), nullable=False)
    contract_digest: Mapped[str] = mapped_column(
        String(64), nullable=False, unique=True
    )
    source_repo: Mapped[str] = mapped_column(String(100), nullable=False)
    source_path: Mapped[str] = mapped_column(Text, nullable=False)
    source_revision: Mapped[str] = mapped_column(String(64), nullable=False)
    document: Mapped[dict] = mapped_column(JSONB, nullable=False)
    active: Mapped[bool] = mapped_column(
        Boolean, nullable=False, default=True, index=True
    )
    required_for_decision: Mapped[bool] = mapped_column(
        Boolean, nullable=False, default=False
    )
    decision_id: Mapped[uuid.UUID | None] = mapped_column(
        UUID(as_uuid=True),
        ForeignKey("decisions.id", ondelete="RESTRICT"),
        nullable=True,
        index=True,
    )
    workplan_id: Mapped[uuid.UUID | None] = mapped_column(
        UUID(as_uuid=True),
        ForeignKey("workplans.id", ondelete="RESTRICT", onupdate="CASCADE"),
        nullable=True,
        index=True,
    )
    task_id: Mapped[uuid.UUID | None] = mapped_column(
        UUID(as_uuid=True),
        ForeignKey("tasks.id", ondelete="RESTRICT", onupdate="CASCADE"),
        nullable=True,
        index=True,
    )
    projected_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), nullable=False
    )

    receipts: Mapped[list["ReviewReceipt"]] = relationship(
        "ReviewReceipt", back_populates="contract", lazy="selectin"
    )

    __table_args__ = (
        UniqueConstraint(
            "contract_key",
            "source_repo",
            "source_path",
            "source_revision",
            name="uq_review_contract_source_revision",
        ),
        Index("ix_review_contract_key_active", "contract_key", "active"),
    )


class ReviewReceipt(Base, TimestampMixin):
    """Immutable evidence submitted against an exact contract digest."""

    __tablename__ = "review_receipts"

    id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True), primary_key=True, default=new_uuid7
    )
    contract_id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True),
        ForeignKey("review_contracts.id", ondelete="RESTRICT"),
        nullable=False,
        index=True,
    )
    owner_id: Mapped[str] = mapped_column(String(160), nullable=False, index=True)
    actor: Mapped[str] = mapped_column(String(160), nullable=False)
    disposition: Mapped[str] = mapped_column(String(32), nullable=False)
    contract_digest: Mapped[str] = mapped_column(String(64), nullable=False, index=True)
    receipt_digest: Mapped[str] = mapped_column(String(64), nullable=False, unique=True)
    artifact_hashes: Mapped[dict] = mapped_column(JSONB, nullable=False, default=dict)
    assertion_ids: Mapped[list] = mapped_column(JSONB, nullable=False, default=list)
    checks: Mapped[list] = mapped_column(JSONB, nullable=False, default=list)
    note: Mapped[str | None] = mapped_column(Text, nullable=True)
    source_repo: Mapped[str] = mapped_column(String(100), nullable=False)
    source_path: Mapped[str] = mapped_column(Text, nullable=False)
    source_revision: Mapped[str] = mapped_column(String(64), nullable=False)
    submitted_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), nullable=False, index=True
    )
    document: Mapped[dict] = mapped_column(JSONB, nullable=False)

    contract: Mapped[ReviewContract] = relationship(
        "ReviewContract", back_populates="receipts"
    )

    __table_args__ = (
        UniqueConstraint(
            "source_repo",
            "source_path",
            "source_revision",
            name="uq_review_receipt_source_revision",
        ),
        Index(
            "ix_review_receipt_contract_owner_time",
            "contract_id",
            "owner_id",
            "submitted_at",
        ),
    )
