from __future__ import annotations

from typing import Any

import pytest
from sqlalchemy import inspect, text
from sqlalchemy.engine import Engine
from sqlalchemy.exc import DBAPIError, IntegrityError

from app.models.savings import (
    ProfileBaselineSnapshot,
    SavingsEvent,
    SavingsEvidenceLink,
    SavingsOpportunity,
    SavingsRealization,
)


def _assert_savings_head_schema(engine: Engine) -> None:
    inspector = inspect(engine)
    for model in (
        ProfileBaselineSnapshot,
        SavingsOpportunity,
        SavingsRealization,
        SavingsEvidenceLink,
        SavingsEvent,
    ):
        table = model.__table__
        live_columns = {
            str(column["name"]): bool(column["nullable"])
            for column in inspector.get_columns(table.name, schema="public")
        }
        declared_columns = {
            column.name: bool(column.nullable) for column in table.columns
        }
        assert live_columns == declared_columns

        live_constraint_names = {
            str(item["name"])
            for loader in (
                inspector.get_unique_constraints,
                inspector.get_check_constraints,
                inspector.get_foreign_keys,
            )
            for item in loader(table.name, schema="public")
            if item.get("name")
        }
        declared_constraint_names = {
            str(constraint.name)
            for constraint in table.constraints
            if constraint.name is not None
        }
        assert live_constraint_names == declared_constraint_names

        live_index_names = {
            str(item["name"])
            for item in inspector.get_indexes(table.name, schema="public")
            if not item.get("duplicates_constraint")
        }
        declared_index_names = {str(index.name) for index in table.indexes}
        assert live_index_names == declared_index_names

    for table_name in ("savings_opportunities", "savings_realizations"):
        constrained_columns = {
            str(column)
            for foreign_key in inspector.get_foreign_keys(table_name, schema="public")
            for column in foreign_key["constrained_columns"]
        }
        assert "claim_id" not in constrained_columns
        assert "claim_item_id" not in constrained_columns

    with engine.connect() as connection:
        canonical_index = str(
            connection.scalar(
                text(
                    "SELECT indexdef FROM pg_indexes "
                    "WHERE schemaname = 'public' "
                    "AND tablename = 'savings_realizations' "
                    "AND indexname = "
                    "'uq_savings_realizations_actual_canonical_benefit'"
                )
            )
            or ""
        ).lower()
        trigger_count = int(
            connection.scalar(
                text(
                    "SELECT COUNT(*) FROM pg_trigger trigger "
                    "JOIN pg_class relation ON relation.oid = trigger.tgrelid "
                    "WHERE relation.relname = 'savings_events' "
                    "AND trigger.tgname = 'trg_savings_events_append_only' "
                    "AND NOT trigger.tgisinternal"
                )
            )
            or 0
        )
    assert "unique index" in canonical_index
    assert "realization_type" in canonical_index
    assert "actual" in canonical_index
    assert "dedupe_status" in canonical_index
    assert "canonical" in canonical_index
    assert trigger_count == 1


def _assert_savings_runtime_invariants(engine: Engine) -> None:
    opportunity_insert = text(
        """
        INSERT INTO savings_opportunities (
            id, tenant_id, opportunity_key, benefit_key, expense_case_id,
            claim_id, claim_no_snapshot, source_type, source_id, category,
            value_kind, title, description, exposure_amount,
            baseline_snapshot_id, baseline_amount, target_amount,
            estimated_gross, estimated_cost, estimated_net,
            estimated_low, estimated_high, confidence, currency,
            reporting_currency, attribution_method, suggested_action,
            owner_id, owner_name, owner_role
        ) VALUES (
            :id, :tenant_id, :opportunity_key, :benefit_key, :expense_case_id,
            'claim-soft-ref', 'CLM-SNAPSHOT', 'policy_adjustment', :id, 'lodging',
            'cash', '住宿标准优化', '服务器政策反事实', 20,
            :baseline_snapshot_id, 100, 80, 20, 0, 20, 15, 25, 1,
            'CNY', 'CNY', 'policy_counterfactual', '按职级标准重算',
            'finance-owner', '财务负责人', 'finance'
        )
        """
    )
    realization_insert = text(
        """
        INSERT INTO savings_realizations (
            id, tenant_id, realization_key, opportunity_id, expense_case_id,
            claim_id, realization_type, reversal_of_realization_id, realized_at,
            recorded_by_id, recorded_by_name, actual_gross, incremental_cost,
            actual_net, original_currency, reporting_amount, reporting_currency,
            fx_rate, fx_source, fx_date, fx_version, attribution_method,
            attribution_ratio, benefit_key, dedupe_status,
            canonical_realization_id, status
        ) VALUES (
            :id, 'tenant-a', :id, :opportunity_id, 'case-a', 'claim-soft-ref',
            :realization_type, :reversal_of_realization_id, now(),
            'recorder-a', '填报人', :actual_gross, 0, :actual_net,
            'CNY', :reporting_amount, 'CNY', 1, 'system-fixed', current_date,
            'fx-cny-v1', 'direct', 1, :benefit_key, :dedupe_status,
            :canonical_realization_id, 'pending_confirmation'
        )
        """
    )

    def execute_rejected(
        connection: Any,
        statement: Any,
        parameters: dict[str, Any],
        error_type: type[DBAPIError] = IntegrityError,
    ) -> None:
        savepoint = connection.begin_nested()
        try:
            with pytest.raises(error_type):
                connection.execute(statement, parameters)
        finally:
            if savepoint.is_active:
                savepoint.rollback()

    with engine.connect() as connection:
        transaction = connection.begin()
        try:
            connection.execute(
                text(
                    """
                    INSERT INTO expense_cases (
                        id, tenant_id, case_no, scene_code, title, current_stage, status
                    ) VALUES (
                        'case-a', 'tenant-a', 'CASE-A', 'expense_reimbursement',
                        '迁移约束探针', 'payment', 'active'
                    )
                    """
                )
            )
            connection.execute(
                text(
                    """
                    INSERT INTO profile_baseline_snapshots (
                        id, tenant_id, baseline_key, baseline_type, dimension_type,
                        dimension_id, metric_key, unit, original_currency,
                        baseline_value, sample_count, method, query_fingerprint,
                        data_quality_status, data_quality_score, algorithm_version,
                        policy_version, policy_effective_from, target_resource_type,
                        target_resource_id, frozen_at, frozen_by
                    ) VALUES (
                        'baseline-a', 'tenant-a', 'baseline-a',
                        'policy_counterfactual', 'claim_item', 'item-soft-ref',
                        'approved_amount', 'currency', 'CNY', 100, 0,
                        'server_policy', 'query-fingerprint-a', 'complete', 1,
                        'policy-calculator-v1', 'policy-v1', current_date,
                        'claim_item', 'item-soft-ref', now(), 'system'
                    )
                    """
                )
            )
            connection.execute(
                opportunity_insert,
                {
                    "id": "opportunity-a",
                    "tenant_id": "tenant-a",
                    "opportunity_key": "opportunity-key-a",
                    "benefit_key": "benefit-a",
                    "expense_case_id": "case-a",
                    "baseline_snapshot_id": "baseline-a",
                },
            )
            connection.execute(
                opportunity_insert,
                {
                    "id": "opportunity-b",
                    "tenant_id": "tenant-a",
                    "opportunity_key": "opportunity-key-b",
                    "benefit_key": "benefit-b",
                    "expense_case_id": "case-a",
                    "baseline_snapshot_id": "baseline-a",
                },
            )
            execute_rejected(
                connection,
                opportunity_insert,
                {
                    "id": "cross-tenant-opportunity",
                    "tenant_id": "tenant-b",
                    "opportunity_key": "cross-tenant-opportunity",
                    "benefit_key": "cross-tenant-benefit",
                    "expense_case_id": "case-a",
                    "baseline_snapshot_id": "baseline-a",
                },
            )

            common_actual = {
                "opportunity_id": "opportunity-a",
                "realization_type": "actual",
                "reversal_of_realization_id": None,
                "actual_gross": 20,
                "actual_net": 20,
                "reporting_amount": 20,
                "canonical_realization_id": None,
            }
            connection.execute(
                realization_insert,
                {
                    **common_actual,
                    "id": "realization-a",
                    "benefit_key": "benefit-a",
                    "dedupe_status": "pending_review",
                },
            )
            connection.execute(
                realization_insert,
                {
                    **common_actual,
                    "id": "realization-canonical",
                    "benefit_key": "benefit-canonical",
                    "dedupe_status": "canonical",
                },
            )
            execute_rejected(
                connection,
                realization_insert,
                {
                    **common_actual,
                    "id": "realization-canonical-duplicate",
                    "benefit_key": "benefit-canonical",
                    "dedupe_status": "canonical",
                },
            )
            execute_rejected(
                connection,
                realization_insert,
                {
                    **common_actual,
                    "id": "realization-wrong-canonical",
                    "benefit_key": "other-benefit",
                    "dedupe_status": "duplicate",
                    "canonical_realization_id": "realization-a",
                },
            )
            execute_rejected(
                connection,
                realization_insert,
                {
                    "id": "cross-opportunity-reversal",
                    "opportunity_id": "opportunity-b",
                    "realization_type": "reversal",
                    "reversal_of_realization_id": "realization-a",
                    "actual_gross": -5,
                    "actual_net": -5,
                    "reporting_amount": -5,
                    "benefit_key": "benefit-a",
                    "dedupe_status": "pending_review",
                    "canonical_realization_id": None,
                },
            )
            execute_rejected(
                connection,
                text(
                    """
                    UPDATE savings_realizations
                    SET status = 'finance_confirmed',
                        finance_confirmer_id = recorded_by_id,
                        finance_confirmer_name = '同一填报人',
                        confirmed_at = now(), confirmation_note = '不应通过'
                    WHERE id = 'realization-canonical'
                    """
                ),
                {},
            )
            connection.execute(
                text(
                    """
                    UPDATE savings_realizations
                    SET status = 'finance_confirmed',
                        finance_confirmer_id = 'finance-b',
                        finance_confirmer_name = '独立财务',
                        confirmed_at = now(), confirmation_note = '证据完整'
                    WHERE id = 'realization-canonical'
                    """
                )
            )
            assert connection.scalar(
                text(
                    "SELECT status FROM savings_realizations "
                    "WHERE id = 'realization-canonical'"
                )
            ) == "finance_confirmed"
            connection.execute(
                realization_insert,
                {
                    "id": "valid-reversal",
                    "opportunity_id": "opportunity-a",
                    "realization_type": "reversal",
                    "reversal_of_realization_id": "realization-a",
                    "actual_gross": -5,
                    "actual_net": -5,
                    "reporting_amount": -5,
                    "benefit_key": "benefit-a",
                    "dedupe_status": "canonical",
                    "canonical_realization_id": None,
                },
            )
            connection.execute(
                text(
                    """
                    UPDATE savings_realizations
                    SET status = 'finance_confirmed',
                        finance_confirmer_id = recorded_by_id,
                        finance_confirmer_name = '冲回财务',
                        confirmed_at = now(), confirmation_note = '全额冲回'
                    WHERE id = 'valid-reversal'
                    """
                )
            )

            connection.execute(
                text(
                    """
                    INSERT INTO savings_events (
                        id, tenant_id, aggregate_type, aggregate_id,
                        opportunity_id, action, actor_id, actor_name, actor_type,
                        request_id, expected_version, result_version,
                        payload_fingerprint
                    ) VALUES (
                        'event-a', 'tenant-a', 'opportunity', 'opportunity-a',
                        'opportunity-a', 'created', 'system', '系统', 'system',
                        'request-a', 0, 1, 'fingerprint-a'
                    )
                    """
                )
            )
            execute_rejected(
                connection,
                text("UPDATE savings_events SET action = 'tampered' WHERE id = 'event-a'"),
                {},
                DBAPIError,
            )
            execute_rejected(
                connection,
                text("DELETE FROM savings_events WHERE id = 'event-a'"),
                {},
                DBAPIError,
            )
        finally:
            transaction.rollback()
