from __future__ import annotations

import uuid
from datetime import datetime
from decimal import Decimal
from typing import Any

from sqlalchemy import (
    Boolean,
    CheckConstraint,
    DateTime,
    ForeignKeyConstraint,
    Index,
    Integer,
    Numeric,
    String,
    UniqueConstraint,
    func,
)
from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy.types import JSON

from app.db.base_class import Base


def _new_id() -> str:
    return str(uuid.uuid4())


class AIDecision(Base):
    """AI 在费用流程中给出的结构化建议事实。"""

    __tablename__ = "ai_decisions"
    __table_args__ = (
        UniqueConstraint("tenant_id", "id", name="uq_ai_decisions_tenant_id"),
        UniqueConstraint(
            "tenant_id",
            "expense_case_id",
            "id",
            name="uq_ai_decisions_tenant_case_id",
        ),
        UniqueConstraint(
            "tenant_id",
            "idempotency_key",
            name="uq_ai_decisions_tenant_idempotency",
        ),
        UniqueConstraint(
            "tenant_id",
            "preview_decision_id",
            name="uq_ai_decisions_tenant_preview_decision",
        ),
        ForeignKeyConstraint(
            ["tenant_id", "preview_decision_id"],
            [
                "ai_application_preview_decisions.tenant_id",
                "ai_application_preview_decisions.id",
            ],
            ondelete="RESTRICT",
            name="fk_ai_decisions_tenant_preview_decision",
        ),
        ForeignKeyConstraint(
            ["tenant_id", "expense_case_id"],
            ["expense_cases.tenant_id", "expense_cases.id"],
            ondelete="RESTRICT",
            name="fk_ai_decisions_tenant_case",
        ),
        ForeignKeyConstraint(
            ["tenant_id", "expense_case_id", "business_event_id"],
            [
                "business_events.tenant_id",
                "business_events.expense_case_id",
                "business_events.id",
            ],
            ondelete="RESTRICT",
            name="fk_ai_decisions_tenant_event",
        ),
        CheckConstraint(
            "confidence IS NULL OR (confidence >= 0 AND confidence <= 1)",
            name="ck_ai_decisions_confidence",
        ),
        CheckConstraint(
            "status IN ('suggested', 'accepted', 'edited', 'rejected', "
            "'ignored', 'executed', 'rolled_back')",
            name="ck_ai_decisions_status",
        ),
        Index(
            "ix_ai_decisions_tenant_case_time",
            "tenant_id",
            "expense_case_id",
            "created_at",
        ),
        Index(
            "ix_ai_decisions_tenant_subject",
            "tenant_id",
            "subject_type",
            "subject_id",
        ),
        Index("ix_ai_decisions_agent_run_id", "agent_run_id"),
        Index("ix_ai_decisions_correlation_id", "correlation_id"),
    )

    id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_new_id)
    tenant_id: Mapped[str] = mapped_column(String(64), nullable=False)
    expense_case_id: Mapped[str] = mapped_column(String(36), nullable=False)
    business_event_id: Mapped[str | None] = mapped_column(String(36), nullable=True)
    expense_claim_id: Mapped[str] = mapped_column(String(36), nullable=False, index=True)
    preview_decision_id: Mapped[str | None] = mapped_column(String(36), nullable=True)
    agent_run_id: Mapped[str | None] = mapped_column(String(120), nullable=True)
    correlation_id: Mapped[str] = mapped_column(String(64), nullable=False)
    subject_type: Mapped[str] = mapped_column(String(50), nullable=False)
    subject_id: Mapped[str] = mapped_column(String(100), nullable=False)
    decision_type: Mapped[str] = mapped_column(String(80), nullable=False)
    decision_source: Mapped[str] = mapped_column(String(20), nullable=False)
    status: Mapped[str] = mapped_column(String(20), nullable=False)
    automation_mode: Mapped[str] = mapped_column(String(40), nullable=False)
    confidence: Mapped[Decimal | None] = mapped_column(Numeric(5, 4), nullable=True)
    suggestion_json: Mapped[dict[str, Any]] = mapped_column(JSON, nullable=False, default=dict)
    evidence_json: Mapped[dict[str, Any]] = mapped_column(JSON, nullable=False, default=dict)
    version_json: Mapped[dict[str, Any]] = mapped_column(JSON, nullable=False, default=dict)
    schema_version: Mapped[int] = mapped_column(Integer, nullable=False, default=1)
    training_eligible: Mapped[bool] = mapped_column(
        Boolean, nullable=False, default=False, server_default="false"
    )
    idempotency_key: Mapped[str] = mapped_column(String(120), nullable=False)
    content_fingerprint: Mapped[str] = mapped_column(String(71), nullable=False)
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now()
    )


class AIDecisionFeedback(Base):
    """用户对 AI 建议的采纳或显式字段纠正事实。"""

    __tablename__ = "ai_decision_feedback"
    __table_args__ = (
        UniqueConstraint(
            "tenant_id",
            "id",
            name="uq_ai_decision_feedback_tenant_id",
        ),
        UniqueConstraint(
            "tenant_id",
            "idempotency_key",
            name="uq_ai_decision_feedback_tenant_idempotency",
        ),
        ForeignKeyConstraint(
            ["tenant_id", "decision_id"],
            ["ai_decisions.tenant_id", "ai_decisions.id"],
            ondelete="RESTRICT",
            name="fk_ai_decision_feedback_tenant_decision",
        ),
        CheckConstraint(
            "feedback_type IN ('accepted', 'edited', 'rejected', 'ignored')",
            name="ck_ai_decision_feedback_type",
        ),
        CheckConstraint(
            "verification_status IN ('client_observed', 'server_verified', "
            "'human_verified', 'invalidated')",
            name="ck_ai_decision_feedback_verification",
        ),
        CheckConstraint(
            "NOT training_eligible OR verification_status IN "
            "('server_verified', 'human_verified')",
            name="ck_ai_decision_feedback_training_eligibility",
        ),
        Index(
            "ix_ai_decision_feedback_tenant_decision_time",
            "tenant_id",
            "decision_id",
            "created_at",
        ),
        Index(
            "ix_ai_decision_feedback_tenant_type_time",
            "tenant_id",
            "feedback_type",
            "created_at",
        ),
        Index("ix_ai_decision_feedback_correlation_id", "correlation_id"),
    )

    id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_new_id)
    tenant_id: Mapped[str] = mapped_column(String(64), nullable=False)
    decision_id: Mapped[str] = mapped_column(String(36), nullable=False)
    expense_claim_id: Mapped[str] = mapped_column(String(36), nullable=False, index=True)
    correlation_id: Mapped[str] = mapped_column(String(64), nullable=False)
    feedback_type: Mapped[str] = mapped_column(String(20), nullable=False)
    action_type: Mapped[str] = mapped_column(String(30), nullable=False)
    actor_id: Mapped[str] = mapped_column(String(120), nullable=False)
    actor_type: Mapped[str] = mapped_column(String(30), nullable=False, default="user")
    evidence_source: Mapped[str] = mapped_column(String(40), nullable=False)
    verification_status: Mapped[str] = mapped_column(String(30), nullable=False)
    training_eligible: Mapped[bool] = mapped_column(
        Boolean, nullable=False, default=False, server_default="false"
    )
    final_value_json: Mapped[dict[str, Any]] = mapped_column(JSON, nullable=False, default=dict)
    changed_fields_json: Mapped[list[dict[str, Any]]] = mapped_column(
        JSON, nullable=False, default=list
    )
    idempotency_key: Mapped[str] = mapped_column(String(120), nullable=False)
    content_fingerprint: Mapped[str] = mapped_column(String(71), nullable=False)
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now()
    )


class WorkflowOutcome(Base):
    """与 AI 决策关联、但独立于技术执行状态的业务结果。"""

    __tablename__ = "workflow_outcomes"
    __table_args__ = (
        UniqueConstraint(
            "tenant_id",
            "id",
            name="uq_workflow_outcomes_tenant_id",
        ),
        UniqueConstraint(
            "tenant_id",
            "idempotency_key",
            name="uq_workflow_outcomes_tenant_idempotency",
        ),
        ForeignKeyConstraint(
            ["tenant_id", "expense_case_id"],
            ["expense_cases.tenant_id", "expense_cases.id"],
            ondelete="RESTRICT",
            name="fk_workflow_outcomes_tenant_case",
        ),
        ForeignKeyConstraint(
            ["tenant_id", "expense_case_id", "decision_id"],
            [
                "ai_decisions.tenant_id",
                "ai_decisions.expense_case_id",
                "ai_decisions.id",
            ],
            ondelete="RESTRICT",
            name="fk_workflow_outcomes_tenant_decision",
        ),
        ForeignKeyConstraint(
            ["tenant_id", "expense_case_id", "business_event_id"],
            [
                "business_events.tenant_id",
                "business_events.expense_case_id",
                "business_events.id",
            ],
            ondelete="RESTRICT",
            name="fk_workflow_outcomes_tenant_event",
        ),
        CheckConstraint(
            "outcome_status IN ('recorded', 'verified', 'reversed')",
            name="ck_workflow_outcomes_status",
        ),
        Index(
            "ix_workflow_outcomes_tenant_case_time",
            "tenant_id",
            "expense_case_id",
            "effective_at",
        ),
        Index(
            "ix_workflow_outcomes_tenant_type_time",
            "tenant_id",
            "outcome_type",
            "effective_at",
        ),
        Index("ix_workflow_outcomes_correlation_id", "correlation_id"),
    )

    id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_new_id)
    tenant_id: Mapped[str] = mapped_column(String(64), nullable=False)
    expense_case_id: Mapped[str] = mapped_column(String(36), nullable=False)
    decision_id: Mapped[str] = mapped_column(String(36), nullable=False)
    business_event_id: Mapped[str | None] = mapped_column(String(36), nullable=True)
    expense_claim_id: Mapped[str] = mapped_column(String(36), nullable=False, index=True)
    correlation_id: Mapped[str] = mapped_column(String(64), nullable=False)
    outcome_type: Mapped[str] = mapped_column(String(50), nullable=False)
    outcome_status: Mapped[str] = mapped_column(String(20), nullable=False)
    actor_id: Mapped[str] = mapped_column(String(120), nullable=False)
    actor_type: Mapped[str] = mapped_column(String(30), nullable=False, default="user")
    result_json: Mapped[dict[str, Any]] = mapped_column(JSON, nullable=False, default=dict)
    idempotency_key: Mapped[str] = mapped_column(String(120), nullable=False)
    content_fingerprint: Mapped[str] = mapped_column(String(71), nullable=False)
    effective_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now()
    )
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now()
    )
