"""SQLAlchemy models and workflow state enum."""

from __future__ import annotations

import enum
import uuid
from datetime import datetime
from typing import Any, Optional

from sqlalchemy import JSON, DateTime, Enum, ForeignKey, Integer, String, Text, UniqueConstraint, func
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
from sqlalchemy.types import Uuid


class Base(DeclarativeBase):
    pass


class WorkflowState(str, enum.Enum):
    RECEIVED = "RECEIVED"
    ANALYZING = "ANALYZING"
    NEEDS_CLARIFICATION = "NEEDS_CLARIFICATION"
    DUPLICATE_REVIEW = "DUPLICATE_REVIEW"
    DOC_REVIEW = "DOC_REVIEW"
    ARCHITECTING = "ARCHITECTING"
    DESIGN_REVIEW = "DESIGN_REVIEW"
    IMPLEMENTING = "IMPLEMENTING"
    REVIEWING = "REVIEWING"
    TESTING = "TESTING"
    PR_CREATED = "PR_CREATED"
    HUMAN_REVIEW = "HUMAN_REVIEW"
    COMPLETED = "COMPLETED"
    FAILED = "FAILED"


ACTIVE_STATES = {
    WorkflowState.RECEIVED,
    WorkflowState.ANALYZING,
    WorkflowState.NEEDS_CLARIFICATION,
    WorkflowState.DUPLICATE_REVIEW,
    WorkflowState.DOC_REVIEW,
    WorkflowState.ARCHITECTING,
    WorkflowState.DESIGN_REVIEW,
    WorkflowState.IMPLEMENTING,
    WorkflowState.REVIEWING,
    WorkflowState.TESTING,
    WorkflowState.HUMAN_REVIEW,
}

TERMINAL_STATES = {WorkflowState.PR_CREATED, WorkflowState.COMPLETED, WorkflowState.FAILED}


def _uuid_col():
    # Prefer portable Uuid; fall back for older SQLAlchemy if needed.
    try:
        return Uuid(as_uuid=True)
    except TypeError:
        return UUID(as_uuid=True)


class Run(Base):
    __tablename__ = "runs"

    id: Mapped[uuid.UUID] = mapped_column(_uuid_col(), primary_key=True, default=uuid.uuid4)
    jira_key: Mapped[str] = mapped_column(String(64), index=True, nullable=False)
    state: Mapped[WorkflowState] = mapped_column(
        Enum(WorkflowState, name="workflow_state", native_enum=False),
        nullable=False,
        default=WorkflowState.RECEIVED,
    )
    retry_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    branch_name: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
    pr_url: Mapped[Optional[str]] = mapped_column(String(1024), nullable=True)
    error: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
    artifact_dir: Mapped[Optional[str]] = mapped_column(String(1024), nullable=True)
    artifacts: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict, nullable=False)
    idempotency_key: Mapped[Optional[str]] = mapped_column(String(255), nullable=True, unique=True)
    created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
    updated_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
    )

    audit_events: Mapped[list["AuditEvent"]] = relationship(back_populates="run")


class AuditEvent(Base):
    __tablename__ = "audit_events"

    id: Mapped[uuid.UUID] = mapped_column(_uuid_col(), primary_key=True, default=uuid.uuid4)
    run_id: Mapped[Optional[uuid.UUID]] = mapped_column(
        _uuid_col(), ForeignKey("runs.id", ondelete="CASCADE"), nullable=True, index=True
    )
    event_type: Mapped[str] = mapped_column(String(128), nullable=False)
    message: Mapped[str] = mapped_column(Text, nullable=False)
    payload: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict, nullable=False)
    created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())

    run: Mapped[Optional[Run]] = relationship(back_populates="audit_events")


class ProcessedWebhook(Base):
    """Idempotency store for webhook deliveries."""

    __tablename__ = "processed_webhooks"
    __table_args__ = (UniqueConstraint("delivery_id", name="uq_processed_webhooks_delivery"),)

    id: Mapped[uuid.UUID] = mapped_column(_uuid_col(), primary_key=True, default=uuid.uuid4)
    delivery_id: Mapped[str] = mapped_column(String(255), nullable=False)
    jira_key: Mapped[Optional[str]] = mapped_column(String(64), nullable=True)
    event_type: Mapped[str] = mapped_column(String(128), nullable=False)
    created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
