"""Approval model — a pending human decision on a sensitive action (Section 5)."""

from __future__ import annotations

import uuid
from datetime import datetime

from sqlalchemy import JSON, DateTime, ForeignKey, String, Text, Uuid
from sqlalchemy import Enum as SAEnum
from sqlalchemy.orm import Mapped, mapped_column

from legate.agent.enums import ApprovalStatus
from legate.db.base import Base, TimestampMixin, UUIDPrimaryKeyMixin
from legate.policy.roles import Role


class Approval(UUIDPrimaryKeyMixin, TimestampMixin, Base):
    __tablename__ = "approvals"

    workspace_id: Mapped[uuid.UUID] = mapped_column(Uuid, index=True, nullable=False)
    run_id: Mapped[uuid.UUID] = mapped_column(
        Uuid, ForeignKey("runs.id", ondelete="CASCADE"), index=True, nullable=False
    )
    step_id: Mapped[uuid.UUID | None] = mapped_column(
        Uuid, ForeignKey("steps.id", ondelete="SET NULL"), nullable=True
    )
    status: Mapped[ApprovalStatus] = mapped_column(
        SAEnum(
            ApprovalStatus,
            native_enum=False,
            length=20,
            values_callable=lambda e: [m.value for m in e],
        ),
        nullable=False,
        default=ApprovalStatus.PENDING,
        index=True,
    )
    # The proposed action awaiting a decision.
    requested_tool: Mapped[str] = mapped_column(String(255), nullable=False)
    requested_args: Mapped[dict] = mapped_column(JSON, nullable=False, default=dict)
    summary: Mapped[str] = mapped_column(String(1024), nullable=False, default="")
    # Snapshot of the run transcript so the run can resume where it paused.
    pending_transcript: Mapped[list] = mapped_column(JSON, nullable=False, default=list)
    # Optional role gate: if set, only this role may decide.
    required_role: Mapped[Role | None] = mapped_column(
        SAEnum(Role, native_enum=False, length=20, values_callable=lambda e: [m.value for m in e]),
        nullable=True,
    )
    expires_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    decided_by: Mapped[uuid.UUID | None] = mapped_column(
        Uuid, ForeignKey("users.id", ondelete="SET NULL"), nullable=True
    )
    decided_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    decision_reason: Mapped[str | None] = mapped_column(Text, nullable=True)

    def __repr__(self) -> str:  # pragma: no cover - debug helper
        return f"<Approval {self.requested_tool} run={self.run_id} status={self.status}>"
