from __future__ import annotations

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

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

from app.db.base_class import Base


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


class ProfileBaselineSnapshot(Base):
    """冻结的费用基线事实；机会始终引用快照而不是实时重算结果。"""
    __tablename__ = "profile_baseline_snapshots"
    __table_args__ = (
        UniqueConstraint("tenant_id", "id", name="uq_profile_baseline_snapshots_tenant_id"),
        UniqueConstraint(
            "tenant_id", "baseline_key", name="uq_profile_baseline_snapshots_tenant_key"
        ),
        CheckConstraint(
            "baseline_type IN ('historical_cohort', 'policy_counterfactual', 'manual')",
            name="ck_profile_baseline_snapshots_type",
        ),
        CheckConstraint(
            "data_quality_status IN ('complete', 'partial', 'insufficient', 'invalid')",
            name="ck_profile_baseline_snapshots_quality_status",
        ),
        CheckConstraint(
            "baseline_value >= 0 AND sample_count >= 0",
            name="ck_profile_baseline_snapshots_values",
        ),
        CheckConstraint(
            "data_quality_score >= 0 AND data_quality_score <= 1",
            name="ck_profile_baseline_snapshots_quality_score",
        ),
        CheckConstraint(
            "window_end IS NULL OR window_start IS NOT NULL",
            name="ck_profile_baseline_snapshots_window_pair",
        ),
        CheckConstraint(
            "window_start IS NULL OR window_end IS NULL OR window_end >= window_start",
            name="ck_profile_baseline_snapshots_window_order",
        ),
        CheckConstraint(
            "baseline_type != 'historical_cohort' OR "
            "(window_start IS NOT NULL AND window_end IS NOT NULL AND sample_count > 0)",
            name="ck_profile_baseline_snapshots_historical_shape",
        ),
        CheckConstraint(
            "baseline_type != 'policy_counterfactual' OR "
            "(policy_version IS NOT NULL AND length(trim(policy_version)) > 0 "
            "AND policy_effective_from IS NOT NULL AND target_resource_type IS NOT NULL "
            "AND target_resource_id IS NOT NULL)",
            name="ck_profile_baseline_snapshots_policy_shape",
        ),
        CheckConstraint(
            "policy_effective_to IS NULL OR policy_effective_from IS NOT NULL",
            name="ck_profile_baseline_snapshots_policy_pair",
        ),
        CheckConstraint(
            "policy_effective_from IS NULL OR policy_effective_to IS NULL "
            "OR policy_effective_to >= policy_effective_from",
            name="ck_profile_baseline_snapshots_policy_order",
        ),
        CheckConstraint(
            "valid_until IS NULL OR valid_until >= frozen_at",
            name="ck_profile_baseline_snapshots_validity",
        ),
        CheckConstraint(
            "length(trim(baseline_key)) > 0 AND length(trim(query_fingerprint)) > 0",
            name="ck_profile_baseline_snapshots_keys",
        ),
        CheckConstraint("version >= 1", name="ck_profile_baseline_snapshots_version"),
        Index(
            "ix_profile_baseline_snapshots_lookup", "tenant_id", "baseline_type",
            "dimension_type", "dimension_id", "metric_key", "frozen_at",
        ),
        Index(
            "ix_profile_baseline_snapshots_quality", "tenant_id",
            "data_quality_status", "frozen_at",
        ),
    )

    id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_new_id)
    tenant_id: Mapped[str] = mapped_column(String(64), nullable=False)
    baseline_key: Mapped[str] = mapped_column(String(160), nullable=False)
    baseline_type: Mapped[str] = mapped_column(String(32), nullable=False)
    dimension_type: Mapped[str] = mapped_column(String(50), nullable=False)
    dimension_id: Mapped[str] = mapped_column(String(160), nullable=False)
    metric_key: Mapped[str] = mapped_column(String(100), nullable=False)
    unit: Mapped[str] = mapped_column(String(30), nullable=False)
    original_currency: Mapped[str | None] = mapped_column(String(3), nullable=True)
    baseline_value: Mapped[Decimal] = mapped_column(Numeric(20, 4), nullable=False)
    window_start: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    window_end: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    sample_count: Mapped[int] = mapped_column(
        Integer, nullable=False, default=0, server_default="0"
    )
    method: Mapped[str] = mapped_column(String(80), nullable=False)
    query_fingerprint: Mapped[str] = mapped_column(String(80), nullable=False)
    data_quality_status: Mapped[str] = mapped_column(String(20), nullable=False)
    data_quality_score: Mapped[Decimal] = mapped_column(Numeric(5, 4), nullable=False)
    quality_issues_json: Mapped[list[dict[str, Any]]] = mapped_column(JSON, default=list)
    algorithm_version: Mapped[str] = mapped_column(String(80), nullable=False)
    policy_version: Mapped[str | None] = mapped_column(String(120), nullable=True)
    policy_effective_from: Mapped[date | None] = mapped_column(Date(), nullable=True)
    policy_effective_to: Mapped[date | None] = mapped_column(Date(), nullable=True)
    target_resource_type: Mapped[str | None] = mapped_column(String(50), nullable=True)
    target_resource_id: Mapped[str | None] = mapped_column(String(160), nullable=True)
    frozen_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
    frozen_by: Mapped[str] = mapped_column(String(120), nullable=False)
    valid_until: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    version: Mapped[int] = mapped_column(Integer, nullable=False, default=1, server_default="1")
    created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())

    opportunities = relationship(
        "SavingsOpportunity", back_populates="baseline_snapshot", passive_deletes=True
    )
    evidence_links = relationship(
        "SavingsEvidenceLink",
        foreign_keys="SavingsEvidenceLink.baseline_snapshot_id",
        back_populates="baseline_snapshot",
        passive_deletes=True,
    )
    events = relationship(
        "SavingsEvent",
        foreign_keys="SavingsEvent.baseline_snapshot_id",
        back_populates="baseline_snapshot",
        passive_deletes=True,
    )


class SavingsOpportunity(Base):
    """从风险暴露中分离出的可执行价值机会投影。"""
    __tablename__ = "savings_opportunities"
    __table_args__ = (
        UniqueConstraint("tenant_id", "id", name="uq_savings_opportunities_tenant_id"),
        UniqueConstraint(
            "tenant_id", "opportunity_key", name="uq_savings_opportunities_tenant_key"
        ),
        ForeignKeyConstraint(
            ["tenant_id", "expense_case_id"],
            ["expense_cases.tenant_id", "expense_cases.id"],
            ondelete="RESTRICT",
            name="fk_savings_opportunities_tenant_case",
        ),
        ForeignKeyConstraint(
            ["tenant_id", "expense_case_id", "discovery_business_event_id"],
            [
                "business_events.tenant_id",
                "business_events.expense_case_id",
                "business_events.id",
            ],
            ondelete="RESTRICT",
            name="fk_savings_opportunities_tenant_event",
        ),
        ForeignKeyConstraint(
            ["tenant_id", "baseline_snapshot_id"],
            ["profile_baseline_snapshots.tenant_id", "profile_baseline_snapshots.id"],
            ondelete="RESTRICT",
            name="fk_savings_opportunities_tenant_baseline",
        ),
        ForeignKeyConstraint(
            ["tenant_id", "expense_case_id", "ai_decision_id"],
            ["ai_decisions.tenant_id", "ai_decisions.expense_case_id", "ai_decisions.id"],
            ondelete="RESTRICT",
            name="fk_savings_opportunities_tenant_ai_decision",
        ),
        CheckConstraint(
            "value_kind IN ('cash', 'labor')",
            name="ck_savings_opportunities_value_kind",
        ),
        CheckConstraint(
            "status IN ('identified', 'accepted', 'in_progress', 'realized', "
            "'verified', 'reversed', 'rejected', 'expired')",
            name="ck_savings_opportunities_status",
        ),
        CheckConstraint(
            "exposure_amount >= 0 AND baseline_amount >= 0 AND target_amount >= 0 "
            "AND estimated_gross >= 0 AND estimated_cost >= 0 AND estimated_net >= 0 "
            "AND estimated_low >= 0 AND estimated_high >= 0",
            name="ck_savings_opportunities_amounts",
        ),
        CheckConstraint(
            "estimated_net = estimated_gross - estimated_cost",
            name="ck_savings_opportunities_net_math",
        ),
        CheckConstraint(
            "estimated_low <= estimated_net AND estimated_net <= estimated_high",
            name="ck_savings_opportunities_interval",
        ),
        CheckConstraint(
            "confidence >= 0 AND confidence <= 1",
            name="ck_savings_opportunities_confidence",
        ),
        CheckConstraint("version >= 1", name="ck_savings_opportunities_version"),
        CheckConstraint(
            "length(trim(benefit_key)) > 0 AND length(trim(opportunity_key)) > 0",
            name="ck_savings_opportunities_keys",
        ),
        CheckConstraint(
            "length(trim(currency)) = 3 AND length(trim(reporting_currency)) = 3",
            name="ck_savings_opportunities_currencies",
        ),
        CheckConstraint(
            "status NOT IN ('accepted', 'in_progress', 'realized', 'verified', 'reversed') "
            "OR accepted_at IS NOT NULL",
            name="ck_savings_opportunities_acceptance",
        ),
        CheckConstraint(
            "status NOT IN ('in_progress', 'realized', 'verified', 'reversed') "
            "OR started_at IS NOT NULL",
            name="ck_savings_opportunities_started",
        ),
        CheckConstraint(
            "status NOT IN ('realized', 'verified', 'reversed') OR realized_at IS NOT NULL",
            name="ck_savings_opportunities_realized",
        ),
        CheckConstraint(
            "status NOT IN ('verified', 'reversed') OR verified_at IS NOT NULL",
            name="ck_savings_opportunities_verified",
        ),
        CheckConstraint(
            "status NOT IN ('verified', 'reversed', 'rejected', 'expired') "
            "OR closed_at IS NOT NULL",
            name="ck_savings_opportunities_closed",
        ),
        Index(
            "ix_savings_opportunities_tenant_status_due",
            "tenant_id",
            "status",
            "due_at",
        ),
        Index(
            "ix_savings_opportunities_tenant_case",
            "tenant_id",
            "expense_case_id",
            "created_at",
        ),
        Index(
            "ix_savings_opportunities_tenant_benefit",
            "tenant_id",
            "benefit_key",
        ),
        Index(
            "ix_savings_opportunities_tenant_owner",
            "tenant_id",
            "owner_id",
            "status",
        ),
    )

    id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_new_id)
    tenant_id: Mapped[str] = mapped_column(String(64), nullable=False)
    opportunity_key: Mapped[str] = mapped_column(String(180), nullable=False)
    benefit_key: Mapped[str] = mapped_column(String(180), nullable=False)
    expense_case_id: Mapped[str] = mapped_column(String(36), nullable=False)
    claim_id: Mapped[str] = mapped_column(String(36), nullable=False)
    claim_no_snapshot: Mapped[str] = mapped_column(String(80), nullable=False)
    claim_item_id: Mapped[str | None] = mapped_column(String(36), nullable=True)
    discovery_business_event_id: Mapped[str | None] = mapped_column(String(36), nullable=True)
    source_type: Mapped[str] = mapped_column(String(50), nullable=False)
    source_id: Mapped[str] = mapped_column(String(160), nullable=False)
    category: Mapped[str] = mapped_column(String(60), nullable=False)
    value_kind: Mapped[str] = mapped_column(String(20), nullable=False)
    title: Mapped[str] = mapped_column(String(200), nullable=False)
    description: Mapped[str] = mapped_column(Text(), nullable=False)
    exposure_amount: Mapped[Decimal] = mapped_column(Numeric(20, 4), nullable=False)
    baseline_snapshot_id: Mapped[str] = mapped_column(String(36), nullable=False)
    baseline_amount: Mapped[Decimal] = mapped_column(Numeric(20, 4), nullable=False)
    target_amount: Mapped[Decimal] = mapped_column(Numeric(20, 4), nullable=False)
    estimated_gross: Mapped[Decimal] = mapped_column(Numeric(20, 4), nullable=False)
    estimated_cost: Mapped[Decimal] = mapped_column(Numeric(20, 4), nullable=False)
    estimated_net: Mapped[Decimal] = mapped_column(Numeric(20, 4), nullable=False)
    estimated_low: Mapped[Decimal] = mapped_column(Numeric(20, 4), nullable=False)
    estimated_high: Mapped[Decimal] = mapped_column(Numeric(20, 4), nullable=False)
    confidence: Mapped[Decimal] = mapped_column(Numeric(5, 4), nullable=False)
    currency: Mapped[str] = mapped_column(String(3), nullable=False)
    reporting_currency: Mapped[str] = mapped_column(String(3), nullable=False)
    attribution_method: Mapped[str] = mapped_column(String(60), nullable=False)
    ai_decision_id: Mapped[str | None] = mapped_column(String(36), nullable=True)
    suggested_action: Mapped[str] = mapped_column(Text(), nullable=False)
    owner_id: Mapped[str] = mapped_column(String(120), nullable=False)
    owner_name: Mapped[str] = mapped_column(String(120), nullable=False)
    owner_role: Mapped[str] = mapped_column(String(60), nullable=False)
    due_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    status: Mapped[str] = mapped_column(
        String(24), nullable=False, default="identified", server_default="identified"
    )
    version: Mapped[int] = mapped_column(Integer, nullable=False, default=1, server_default="1")
    dimension_json: Mapped[dict[str, Any]] = mapped_column(JSON, nullable=False, default=dict)
    baseline_snapshot_json: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict)
    evidence_json: Mapped[list[dict[str, Any]]] = mapped_column(JSON, default=list)
    accepted_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    started_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    realized_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    verified_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    closed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), nullable=False, server_default=func.now()
    )
    updated_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now()
    )

    baseline_snapshot = relationship("ProfileBaselineSnapshot", back_populates="opportunities")
    realizations = relationship(
        "SavingsRealization",
        back_populates="opportunity",
        order_by="asc(SavingsRealization.realized_at)",
        passive_deletes=True,
    )
    evidence_links = relationship(
        "SavingsEvidenceLink",
        foreign_keys="SavingsEvidenceLink.opportunity_id",
        back_populates="opportunity",
        passive_deletes=True,
    )
    events = relationship(
        "SavingsEvent",
        foreign_keys="SavingsEvent.opportunity_id",
        back_populates="opportunity",
        passive_deletes=True,
    )


class SavingsRealization(Base):
    """实际金额事实及其财务确认投影；冲回以新行表达。"""
    __tablename__ = "savings_realizations"
    __table_args__ = (
        UniqueConstraint("tenant_id", "id", name="uq_savings_realizations_tenant_id"),
        UniqueConstraint(
            "tenant_id", "realization_key", name="uq_savings_realizations_tenant_key"
        ),
        UniqueConstraint(
            "tenant_id", "opportunity_id", "benefit_key", "id",
            name="uq_savings_realizations_tenant_opportunity_benefit_id",
        ),
        UniqueConstraint(
            "tenant_id", "benefit_key", "id",
            name="uq_savings_realizations_tenant_benefit_id",
        ),
        ForeignKeyConstraint(
            ["tenant_id", "opportunity_id"],
            ["savings_opportunities.tenant_id", "savings_opportunities.id"],
            ondelete="RESTRICT",
            name="fk_savings_realizations_tenant_opportunity",
        ),
        ForeignKeyConstraint(
            ["tenant_id", "expense_case_id"],
            ["expense_cases.tenant_id", "expense_cases.id"],
            ondelete="RESTRICT",
            name="fk_savings_realizations_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_savings_realizations_tenant_event",
        ),
        ForeignKeyConstraint(
            [
                "tenant_id", "opportunity_id", "benefit_key", "reversal_of_realization_id"
            ],
            [
                "savings_realizations.tenant_id", "savings_realizations.opportunity_id",
                "savings_realizations.benefit_key", "savings_realizations.id",
            ],
            ondelete="RESTRICT",
            name="fk_savings_realizations_tenant_reversal",
        ),
        ForeignKeyConstraint(
            ["tenant_id", "benefit_key", "canonical_realization_id"],
            [
                "savings_realizations.tenant_id", "savings_realizations.benefit_key",
                "savings_realizations.id",
            ],
            ondelete="RESTRICT",
            name="fk_savings_realizations_tenant_canonical",
        ),
        CheckConstraint(
            "realization_type IN ('actual', 'reversal')",
            name="ck_savings_realizations_type",
        ),
        CheckConstraint(
            "dedupe_status IN ('pending_review', 'canonical', 'duplicate', 'excluded')",
            name="ck_savings_realizations_dedupe_status",
        ),
        CheckConstraint(
            "status IN ('pending_confirmation', 'finance_confirmed', 'rejected', 'reversed')",
            name="ck_savings_realizations_status",
        ),
        CheckConstraint(
            "attribution_ratio > 0 AND attribution_ratio <= 1",
            name="ck_savings_realizations_attribution",
        ),
        CheckConstraint(
            "incremental_cost >= 0 AND fx_rate > 0",
            name="ck_savings_realizations_cost_fx",
        ),
        CheckConstraint(
            "actual_net = actual_gross - incremental_cost",
            name="ck_savings_realizations_net_math",
        ),
        CheckConstraint(
            "(realization_type = 'actual' AND reversal_of_realization_id IS NULL "
            "AND actual_gross >= 0 AND actual_net >= 0 AND reporting_amount >= 0) OR "
            "(realization_type = 'reversal' AND reversal_of_realization_id IS NOT NULL "
            "AND actual_gross <= 0 AND actual_net <= 0 AND reporting_amount <= 0)",
            name="ck_savings_realizations_amount_direction",
        ),
        CheckConstraint(
            "(dedupe_status = 'duplicate' AND canonical_realization_id IS NOT NULL "
            "AND canonical_realization_id <> id) OR "
            "(dedupe_status != 'duplicate' AND canonical_realization_id IS NULL)",
            name="ck_savings_realizations_duplicate_target",
        ),
        CheckConstraint(
            "status != 'finance_confirmed' OR "
            "(finance_confirmer_id IS NOT NULL AND finance_confirmer_name IS NOT NULL "
            "AND confirmed_at IS NOT NULL AND confirmation_note IS NOT NULL "
            "AND (realization_type = 'reversal' OR finance_confirmer_id <> recorded_by_id) "
            "AND dedupe_status = 'canonical')",
            name="ck_savings_realizations_confirmation",
        ),
        CheckConstraint(
            "status != 'rejected' OR (rejected_by_id IS NOT NULL "
            "AND rejected_by_name IS NOT NULL AND rejected_at IS NOT NULL "
            "AND rejection_reason IS NOT NULL)",
            name="ck_savings_realizations_rejection",
        ),
        CheckConstraint(
            "status != 'reversed' OR (reversed_by_id IS NOT NULL "
            "AND reversed_by_name IS NOT NULL AND reversed_at IS NOT NULL "
            "AND reversal_reason IS NOT NULL)",
            name="ck_savings_realizations_reversal",
        ),
        CheckConstraint("version >= 1", name="ck_savings_realizations_version"),
        CheckConstraint(
            "length(trim(realization_key)) > 0 AND length(trim(benefit_key)) > 0",
            name="ck_savings_realizations_keys",
        ),
        CheckConstraint(
            "length(trim(original_currency)) = 3 "
            "AND length(trim(reporting_currency)) = 3",
            name="ck_savings_realizations_currencies",
        ),
        Index(
            "uq_savings_realizations_actual_canonical_benefit",
            "tenant_id",
            "benefit_key",
            unique=True,
            postgresql_where=text(
                "realization_type = 'actual' AND dedupe_status = 'canonical'"
            ),
        ).ddl_if(dialect="postgresql"),
        Index(
            "ix_savings_realizations_tenant_status_time", "tenant_id", "status", "realized_at"
        ),
        Index(
            "ix_savings_realizations_tenant_opportunity", "tenant_id",
            "opportunity_id", "realized_at",
        ),
        Index(
            "ix_savings_realizations_tenant_benefit", "tenant_id",
            "benefit_key", "dedupe_status",
        ),
    )

    id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_new_id)
    tenant_id: Mapped[str] = mapped_column(String(64), nullable=False)
    realization_key: Mapped[str] = mapped_column(String(180), nullable=False)
    opportunity_id: Mapped[str] = mapped_column(String(36), nullable=False)
    expense_case_id: Mapped[str] = mapped_column(String(36), nullable=False)
    claim_id: Mapped[str] = mapped_column(String(36), nullable=False)
    claim_item_id: Mapped[str | None] = mapped_column(String(36), nullable=True)
    business_event_id: Mapped[str | None] = mapped_column(String(36), nullable=True)
    realization_type: Mapped[str] = mapped_column(String(20), nullable=False)
    reversal_of_realization_id: Mapped[str | None] = mapped_column(String(36), nullable=True)
    realized_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
    recorded_by_id: Mapped[str] = mapped_column(String(120), nullable=False)
    recorded_by_name: Mapped[str] = mapped_column(String(120), nullable=False)
    actual_gross: Mapped[Decimal] = mapped_column(Numeric(20, 4), nullable=False)
    incremental_cost: Mapped[Decimal] = mapped_column(Numeric(20, 4), nullable=False)
    actual_net: Mapped[Decimal] = mapped_column(Numeric(20, 4), nullable=False)
    original_currency: Mapped[str] = mapped_column(String(3), nullable=False)
    reporting_amount: Mapped[Decimal] = mapped_column(Numeric(20, 4), nullable=False)
    reporting_currency: Mapped[str] = mapped_column(String(3), nullable=False)
    fx_rate: Mapped[Decimal] = mapped_column(Numeric(20, 8), nullable=False)
    fx_source: Mapped[str] = mapped_column(String(80), nullable=False)
    fx_date: Mapped[date] = mapped_column(Date(), nullable=False)
    fx_version: Mapped[str] = mapped_column(String(80), nullable=False)
    attribution_method: Mapped[str] = mapped_column(String(60), nullable=False)
    attribution_ratio: Mapped[Decimal] = mapped_column(Numeric(7, 6), nullable=False)
    benefit_key: Mapped[str] = mapped_column(String(180), nullable=False)
    dedupe_status: Mapped[str] = mapped_column(
        String(24), nullable=False, default="pending_review", server_default="pending_review"
    )
    canonical_realization_id: Mapped[str | None] = mapped_column(String(36), nullable=True)
    status: Mapped[str] = mapped_column(
        String(24),
        nullable=False,
        default="pending_confirmation",
        server_default="pending_confirmation",
    )
    finance_confirmer_id: Mapped[str | None] = mapped_column(String(120), nullable=True)
    finance_confirmer_name: Mapped[str | None] = mapped_column(String(120), nullable=True)
    confirmed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    confirmation_note: Mapped[str | None] = mapped_column(Text(), nullable=True)
    rejected_by_id: Mapped[str | None] = mapped_column(String(120), nullable=True)
    rejected_by_name: Mapped[str | None] = mapped_column(String(120), nullable=True)
    rejected_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    rejection_reason: Mapped[str | None] = mapped_column(Text(), nullable=True)
    reversed_by_id: Mapped[str | None] = mapped_column(String(120), nullable=True)
    reversed_by_name: Mapped[str | None] = mapped_column(String(120), nullable=True)
    reversed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    reversal_reason: Mapped[str | None] = mapped_column(Text(), nullable=True)
    baseline_snapshot_json: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict)
    final_snapshot_json: Mapped[dict[str, Any]] = mapped_column(JSON, nullable=False, default=dict)
    evidence_json: Mapped[list[dict[str, Any]]] = mapped_column(JSON, default=list)
    version: Mapped[int] = mapped_column(Integer, nullable=False, default=1, server_default="1")
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), nullable=False, server_default=func.now()
    )
    updated_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now()
    )

    opportunity = relationship("SavingsOpportunity", back_populates="realizations")
    evidence_links = relationship(
        "SavingsEvidenceLink",
        foreign_keys="SavingsEvidenceLink.realization_id",
        back_populates="realization",
        passive_deletes=True,
    )
    events = relationship(
        "SavingsEvent",
        foreign_keys="SavingsEvent.realization_id",
        back_populates="realization",
        passive_deletes=True,
    )


class SavingsEvidenceLink(Base):
    """基线、机会或实际结果与服务端可验证资源之间的租户安全链接。"""
    __tablename__ = "savings_evidence_links"
    __table_args__ = (
        UniqueConstraint("tenant_id", "id", name="uq_savings_evidence_links_tenant_id"),
        UniqueConstraint(
            "tenant_id",
            "evidence_key",
            name="uq_savings_evidence_links_tenant_key",
        ),
        ForeignKeyConstraint(
            ["tenant_id", "baseline_snapshot_id"],
            ["profile_baseline_snapshots.tenant_id", "profile_baseline_snapshots.id"],
            ondelete="RESTRICT",
            name="fk_savings_evidence_links_tenant_baseline",
        ),
        ForeignKeyConstraint(
            ["tenant_id", "opportunity_id"],
            ["savings_opportunities.tenant_id", "savings_opportunities.id"],
            ondelete="RESTRICT",
            name="fk_savings_evidence_links_tenant_opportunity",
        ),
        ForeignKeyConstraint(
            ["tenant_id", "realization_id"],
            ["savings_realizations.tenant_id", "savings_realizations.id"],
            ondelete="RESTRICT",
            name="fk_savings_evidence_links_tenant_realization",
        ),
        CheckConstraint(
            "entity_type IN ('baseline', 'opportunity', 'realization')",
            name="ck_savings_evidence_links_entity_type",
        ),
        CheckConstraint(
            "(entity_type = 'baseline' AND baseline_snapshot_id = entity_id "
            "AND opportunity_id IS NULL AND realization_id IS NULL) OR "
            "(entity_type = 'opportunity' AND opportunity_id = entity_id "
            "AND baseline_snapshot_id IS NULL AND realization_id IS NULL) OR "
            "(entity_type = 'realization' AND realization_id = entity_id "
            "AND baseline_snapshot_id IS NULL AND opportunity_id IS NULL)",
            name="ck_savings_evidence_links_entity_shape",
        ),
        CheckConstraint(
            "verification_status IN ('unverified', 'verified', 'rejected', 'unavailable')",
            name="ck_savings_evidence_links_verification",
        ),
        CheckConstraint(
            "verification_status != 'verified' OR "
            "(verified_by IS NOT NULL AND verified_at IS NOT NULL)",
            name="ck_savings_evidence_links_verifier",
        ),
        CheckConstraint(
            "length(trim(evidence_key)) > 0 AND length(trim(content_hash)) > 0",
            name="ck_savings_evidence_links_keys",
        ),
        Index(
            "ix_savings_evidence_links_entity",
            "tenant_id",
            "entity_type",
            "entity_id",
            "collected_at",
        ),
        Index(
            "ix_savings_evidence_links_resource",
            "tenant_id",
            "resource_type",
            "resource_id",
        ),
    )

    id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_new_id)
    tenant_id: Mapped[str] = mapped_column(String(64), nullable=False)
    evidence_key: Mapped[str] = mapped_column(String(180), nullable=False)
    entity_type: Mapped[str] = mapped_column(String(20), nullable=False)
    entity_id: Mapped[str] = mapped_column(String(36), nullable=False)
    baseline_snapshot_id: Mapped[str | None] = mapped_column(String(36), nullable=True)
    opportunity_id: Mapped[str | None] = mapped_column(String(36), nullable=True)
    realization_id: Mapped[str | None] = mapped_column(String(36), nullable=True)
    evidence_role: Mapped[str] = mapped_column(String(50), nullable=False)
    resource_type: Mapped[str] = mapped_column(String(50), nullable=False)
    resource_id: Mapped[str] = mapped_column(String(160), nullable=False)
    source_system: Mapped[str] = mapped_column(String(80), nullable=False)
    external_event_id: Mapped[str | None] = mapped_column(String(160), nullable=True)
    content_hash: Mapped[str] = mapped_column(String(80), nullable=False)
    occurred_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
    collected_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
    verification_status: Mapped[str] = mapped_column(
        String(20), nullable=False, default="unverified", server_default="unverified"
    )
    verified_by: Mapped[str | None] = mapped_column(String(120), nullable=True)
    verified_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    metadata_json: Mapped[dict[str, Any]] = mapped_column(JSON, nullable=False, default=dict)
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), nullable=False, server_default=func.now()
    )

    baseline_snapshot = relationship(
        "ProfileBaselineSnapshot",
        foreign_keys=[baseline_snapshot_id],
        back_populates="evidence_links",
    )
    opportunity = relationship(
        "SavingsOpportunity",
        foreign_keys=[opportunity_id],
        back_populates="evidence_links",
    )
    realization = relationship(
        "SavingsRealization",
        foreign_keys=[realization_id],
        back_populates="evidence_links",
    )


class SavingsEvent(Base):
    """Savings Ledger 的不可变动作记录和幂等首次响应。"""
    __tablename__ = "savings_events"
    __table_args__ = (
        UniqueConstraint("tenant_id", "id", name="uq_savings_events_tenant_id"),
        UniqueConstraint(
            "tenant_id",
            "actor_id",
            "request_id",
            name="uq_savings_events_actor_request",
        ),
        UniqueConstraint(
            "tenant_id",
            "aggregate_type",
            "aggregate_id",
            "result_version",
            name="uq_savings_events_aggregate_version",
        ),
        ForeignKeyConstraint(
            ["tenant_id", "baseline_snapshot_id"],
            ["profile_baseline_snapshots.tenant_id", "profile_baseline_snapshots.id"],
            ondelete="RESTRICT",
            name="fk_savings_events_tenant_baseline",
        ),
        ForeignKeyConstraint(
            ["tenant_id", "opportunity_id"],
            ["savings_opportunities.tenant_id", "savings_opportunities.id"],
            ondelete="RESTRICT",
            name="fk_savings_events_tenant_opportunity",
        ),
        ForeignKeyConstraint(
            ["tenant_id", "realization_id"],
            ["savings_realizations.tenant_id", "savings_realizations.id"],
            ondelete="RESTRICT",
            name="fk_savings_events_tenant_realization",
        ),
        CheckConstraint(
            "aggregate_type IN ('baseline', 'opportunity', 'realization')",
            name="ck_savings_events_aggregate_type",
        ),
        CheckConstraint(
            "(aggregate_type = 'baseline' AND baseline_snapshot_id = aggregate_id "
            "AND opportunity_id IS NULL AND realization_id IS NULL) OR "
            "(aggregate_type = 'opportunity' AND opportunity_id = aggregate_id "
            "AND baseline_snapshot_id IS NULL AND realization_id IS NULL) OR "
            "(aggregate_type = 'realization' AND realization_id = aggregate_id "
            "AND baseline_snapshot_id IS NULL AND opportunity_id IS NULL)",
            name="ck_savings_events_aggregate_shape",
        ),
        CheckConstraint(
            "length(trim(action)) > 0",
            name="ck_savings_events_action",
        ),
        CheckConstraint(
            "actor_type IN ('user', 'system', 'agent', 'service')",
            name="ck_savings_events_actor_type",
        ),
        CheckConstraint(
            "expected_version >= 0 AND result_version >= 1 "
            "AND result_version >= expected_version",
            name="ck_savings_events_version",
        ),
        CheckConstraint(
            "length(trim(request_id)) > 0 AND length(trim(payload_fingerprint)) > 0",
            name="ck_savings_events_request",
        ),
        Index(
            "ix_savings_events_tenant_aggregate_time",
            "tenant_id",
            "aggregate_type",
            "aggregate_id",
            "occurred_at",
        ),
        Index(
            "ix_savings_events_tenant_correlation",
            "tenant_id",
            "correlation_id",
            "occurred_at",
        ),
    )

    id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_new_id)
    tenant_id: Mapped[str] = mapped_column(String(64), nullable=False)
    aggregate_type: Mapped[str] = mapped_column(String(20), nullable=False)
    aggregate_id: Mapped[str] = mapped_column(String(36), nullable=False)
    baseline_snapshot_id: Mapped[str | None] = mapped_column(String(36), nullable=True)
    opportunity_id: Mapped[str | None] = mapped_column(String(36), nullable=True)
    realization_id: Mapped[str | None] = mapped_column(String(36), nullable=True)
    action: Mapped[str] = mapped_column(String(60), nullable=False)
    actor_id: Mapped[str] = mapped_column(String(120), nullable=False)
    actor_name: Mapped[str] = mapped_column(String(120), nullable=False)
    actor_type: Mapped[str] = mapped_column(String(20), nullable=False)
    request_id: Mapped[str] = mapped_column(String(120), nullable=False)
    expected_version: Mapped[int] = mapped_column(Integer, nullable=False)
    result_version: Mapped[int] = mapped_column(Integer, nullable=False)
    payload_fingerprint: Mapped[str] = mapped_column(String(80), nullable=False)
    payload_json: Mapped[dict[str, Any]] = mapped_column(JSON, nullable=False, default=dict)
    before_json: Mapped[dict[str, Any]] = mapped_column(JSON, nullable=False, default=dict)
    after_json: Mapped[dict[str, Any]] = mapped_column(JSON, nullable=False, default=dict)
    response_json: Mapped[dict[str, Any]] = mapped_column(JSON, nullable=False, default=dict)
    correlation_id: Mapped[str | None] = mapped_column(String(64), nullable=True)
    causation_id: Mapped[str | None] = mapped_column(String(64), nullable=True)
    occurred_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), nullable=False, server_default=func.now()
    )

    baseline_snapshot = relationship(
        "ProfileBaselineSnapshot",
        foreign_keys=[baseline_snapshot_id],
        back_populates="events",
    )
    opportunity = relationship(
        "SavingsOpportunity",
        foreign_keys=[opportunity_id],
        back_populates="events",
    )
    realization = relationship(
        "SavingsRealization",
        foreign_keys=[realization_id],
        back_populates="events",
    )
