from __future__ import annotations

import uuid
from datetime import UTC, date, datetime, timedelta
from decimal import Decimal

from sqlalchemy import create_engine, func, select
from sqlalchemy.orm import Session, sessionmaker
from sqlalchemy.pool import StaticPool

import app.models  # noqa: F401 - 注册完整 metadata
from app.api.deps import CurrentUserContext
from app.db.base_class import Base
from app.models.budget import BudgetAllocation
from app.models.employee import Employee
from app.models.expense_case import BusinessEvent
from app.models.financial_connector import (
    FinancialConnectorConfig,
    FinancialConnectorEvent,
    PaymentReconciliationCase,
)
from app.models.financial_record import ExpenseClaim
from app.models.organization import OrganizationUnit
from app.models.savings import SavingsEvidenceLink, SavingsRealization
from app.schemas.commercial import (
    CommercialCostEventCreate,
    CommercialPlanCreate,
    CommercialPricingScenarioWrite,
    CommercialSubscriptionCreate,
)
from app.schemas.financial_connector import FinancialEventEnvelope
from app.schemas.reimbursement import ExpenseClaimItemCreate
from app.schemas.savings import SavingsRealizationActionCreate
from app.services.commercial_admin import CommercialAdminService
from app.services.commercial_analytics import CommercialAnalyticsService
from app.services.commercial_metering import CommercialMeteringService
from app.services.commercial_pricing import CommercialPricingService
from app.services.expense_claims import ExpenseClaimService
from app.services.financial_connector_auth import (
    FinancialConnectorSecretResolver,
    sign_financial_event,
)
from app.services.financial_connector_ingestion import FinancialConnectorIngestionService
from app.services.savings_discovery import SavingsDiscoveryService
from app.services.savings_realization import SavingsRealizationService


def test_expense_application_to_refund_and_value_evidence_chain() -> None:
    """正式服务贯穿申请、审批、外部财务事实和商业价值口径。"""

    engine = create_engine(
        "sqlite+pysqlite:///:memory:",
        connect_args={"check_same_thread": False},
        poolclass=StaticPool,
    )
    Base.metadata.create_all(engine)
    factory = sessionmaker(bind=engine, expire_on_commit=False)
    try:
        with factory() as db:
            _run_chain(db)
    finally:
        Base.metadata.drop_all(engine)
        engine.dispose()


def _run_chain(db: Session) -> None:
    now = datetime.now(UTC)
    users, application = _seed_application_context(db, now=now)
    claims = ExpenseClaimService(db)

    submitted_application = claims.submit_claim(
        application.id,
        users["employee"],
        correlation_id="value-chain-application-submit",
    )
    assert submitted_application is not None
    assert (submitted_application.status, submitted_application.approval_stage) == (
        "submitted",
        "直属领导审批",
    )

    approved_application = claims.approve_claim(
        application.id,
        users["manager"],
        opinion="差旅必要且预算充足，同意生成报销草稿。",
        request_id="value-chain-application-approve",
        expected_status="submitted",
        expected_approval_stage="直属领导审批",
    )
    assert approved_application is not None
    assert (approved_application.status, approved_application.approval_stage) == (
        "approved",
        "关联单据状态",
    )
    reimbursement = _generated_reimbursement(db, approved_application)

    reimbursement = claims.create_claim_item(
        claim_id=reimbursement.id,
        payload=ExpenseClaimItemCreate(
            item_date=date.today(),
            item_type="hotel",
            item_reason="上海客户现场住宿",
            item_location="上海",
            item_amount=Decimal("66.00"),
            invoice_id="invoice-value-chain-001",
        ),
        current_user=users["employee"],
    )
    assert reimbursement is not None
    reimbursable_item = next(
        item for item in reimbursement.items if item.invoice_id == "invoice-value-chain-001"
    )
    opportunity = SavingsDiscoveryService(db).discover_standard_adjustments(
        claim=reimbursement,
        items_by_id={reimbursable_item.id: reimbursable_item},
        adjustment_flags=[
            {
                "item_id": reimbursable_item.id,
                "message": "已发布住宿政策将原始 100 元锁定为可报 66 元。",
                "original_amount": "100.00",
                "reimbursable_amount": "66.00",
                "employee_absorbed_amount": "34.00",
                "policy_rule_version": "value-chain-hotel-v1",
                "policy_rule_version_source": "published",
                "policy_grade": "P6",
                "policy_matched_city": "上海",
                "calculation_fingerprint": "sha256:" + "a" * 64,
            }
        ],
        current_user=users["employee"],
        request_id="value-chain-saving-discovery",
    )[0]
    db.commit()

    submitted = claims.submit_claim(
        reimbursement.id,
        users["employee"],
        correlation_id="value-chain-reimbursement-submit",
    )
    assert submitted is not None and submitted.approval_stage == "直属领导审批"
    manager_approved = claims.approve_claim(
        reimbursement.id,
        users["manager"],
        opinion="业务真实，同意进入财务审核。",
        request_id="value-chain-manager-approve",
        expected_status="submitted",
        expected_approval_stage="直属领导审批",
    )
    assert manager_approved is not None and manager_approved.approval_stage == "财务审批"
    finance_approved = claims.approve_claim(
        reimbursement.id,
        users["finance"],
        opinion="票据、政策调整和预算均已复核。",
        request_id="value-chain-finance-approve",
        expected_status="submitted",
        expected_approval_stage="财务审批",
    )
    assert finance_approved is not None
    assert (finance_approved.status, finance_approved.approval_stage) == (
        "pending_payment",
        "待付款",
    )

    _seed_commercial_account(db, now=now)
    _seed_connector(db)
    db.commit()
    timestamp = 1_800_000_000

    before_payment_events = _event_count(db, "payment_completed")
    mismatch = _financial_event(
        reimbursement,
        event_id="value-chain-settlement-mismatch",
        extra={"amount": "999.00"},
    )
    mismatch_result = _ingest(db, mismatch, timestamp)
    db.commit()
    db.refresh(reimbursement)
    assert mismatch_result.error_code == "amount_mismatch"
    assert reimbursement.status == "pending_payment"
    assert _event_count(db, "payment_completed") == before_payment_events
    assert (
        db.scalar(
            select(func.count(SavingsRealization.id)).where(
                SavingsRealization.opportunity_id == opportunity.id
            )
        )
        == 0
    )

    settlement = _financial_event(reimbursement, event_id="value-chain-settlement-001")
    settled = _ingest(db, settlement, timestamp + 1)
    db.commit()
    replay = _ingest(db, settlement, timestamp + 1)
    db.commit()
    db.refresh(reimbursement)
    db.refresh(application)
    assert settled.processing_status == "processed" and replay.replayed is True
    assert settled.verification_level == "production_verified"
    assert settled.evidence_classification == "external_cash"
    assert reimbursement.status == "paid"
    assert application.approval_stage == "申请归档"
    assert _event_count(db, "payment_completed") == before_payment_events + 1

    realization = db.scalar(
        select(SavingsRealization).where(
            SavingsRealization.opportunity_id == opportunity.id,
            SavingsRealization.realization_type == "actual",
        )
    )
    assert realization is not None and realization.actual_net == Decimal("34.0000")
    confirmation = SavingsRealizationActionCreate(
        action="confirm",
        request_id="value-chain-saving-confirm",
        expected_version=1,
        comment="独立财务已复核生产银行回执、政策基线和归因键。",
    )
    confirmed = SavingsRealizationService(db).execute_action(
        realization.id,
        confirmation,
        users["confirmer"],
    )
    confirmed_replay = SavingsRealizationService(db).execute_action(
        realization.id,
        confirmation,
        users["confirmer"],
    )
    assert confirmed.response.replayed is False
    assert confirmed_replay.response.replayed is True
    evidence = db.scalar(
        select(SavingsEvidenceLink).where(
            SavingsEvidenceLink.realization_id == realization.id,
            SavingsEvidenceLink.resource_type == "financial_connector_event",
        )
    )
    assert evidence is not None and evidence.verification_status == "verified"

    erp = _financial_event(
        reimbursement,
        event_id="value-chain-erp-001",
        event_type="erp_posted",
        extra={
            "origin_external_event_id": settlement.external_event_id,
            "erp_document_number": "ERP-VALUE-CHAIN-000001",
            "accounting_period": "2026-07",
        },
    )
    erp_result = _ingest(db, erp, timestamp + 2)
    db.commit()
    case = db.get(PaymentReconciliationCase, settled.reconciliation_case_id)
    assert erp_result.processing_status == "processed"
    assert case is not None and case.erp_status == "posted"
    assert case.erp_document_tail == "N-000001"

    window = {
        "start": now - timedelta(days=2),
        "end": now + timedelta(days=2),
        "as_of": now + timedelta(days=1),
    }
    before_refund = CommercialAnalyticsService(db).build("default", **window)
    assert _money(before_refund.verified_cash_savings) == {"CNY": Decimal("34.0000")}
    pricing = CommercialPricingService(db).build(
        "default",
        CommercialPricingScenarioWrite(
            **window,
            target_contribution_margin_rate=Decimal("0.50"),
            max_verified_savings_share=Decimal("0.50"),
        ),
    )
    assert pricing.recommended_model == "hybrid"
    assert pricing.scenarios[0].minimum_sustainable_charge == Decimal("10.0000")
    assert pricing.scenarios[0].maximum_value_aligned_charge == Decimal("17.0000")

    refund = _financial_event(
        reimbursement,
        event_id="value-chain-refund-001",
        event_type="payment_refunded",
        extra={"origin_external_event_id": settlement.external_event_id},
    )
    refunded = _ingest(db, refund, timestamp + 3)
    db.commit()
    refund_replay = _ingest(db, refund, timestamp + 3)
    db.commit()
    db.refresh(reimbursement)
    assert refunded.processing_status == "processed" and refund_replay.replayed is True
    assert reimbursement.status == "pending_payment"
    realizations = list(
        db.scalars(
            select(SavingsRealization).where(SavingsRealization.opportunity_id == opportunity.id)
        ).all()
    )
    assert len(realizations) == 2
    assert sum((row.reporting_amount for row in realizations), Decimal("0")) == Decimal("0")

    duplicate_reversal = _financial_event(
        reimbursement,
        event_id="value-chain-reversal-after-refund",
        event_type="payment_reversed",
        extra={"origin_external_event_id": settlement.external_event_id},
    )
    duplicate_result = _ingest(db, duplicate_reversal, timestamp + 4)
    db.commit()
    assert duplicate_result.processing_status == "exception"
    assert duplicate_result.error_code == "claim_not_paid"
    assert (
        db.scalar(
            select(func.count(SavingsRealization.id)).where(
                SavingsRealization.opportunity_id == opportunity.id
            )
        )
        == 2
    )

    after_refund = CommercialAnalyticsService(db).build("default", **window)
    assert _money(after_refund.verified_cash_savings) == {"CNY": Decimal("0.0000")}
    pricing_after_refund = CommercialPricingService(db).build(
        "default",
        CommercialPricingScenarioWrite(
            **window,
            target_contribution_margin_rate=Decimal("0.50"),
            max_verified_savings_share=Decimal("0.50"),
        ),
    )
    assert pricing_after_refund.recommended_model == "optimize_unit_economics"
    assert pricing_after_refund.scenarios[0].maximum_value_aligned_charge == Decimal("0.0000")
    assert (
        CommercialAnalyticsService(db)
        .build(
            "tenant-other",
            **window,
        )
        .verified_cash_savings.values
        == []
    )
    assert (
        db.scalar(
            select(func.count(FinancialConnectorEvent.id)).where(
                FinancialConnectorEvent.external_event_id == refund.external_event_id
            )
        )
        == 1
    )


def _seed_application_context(
    db: Session,
    *,
    now: datetime,
) -> tuple[dict[str, CurrentUserContext], ExpenseClaim]:
    department = OrganizationUnit(
        unit_code="VALUE-CHAIN-DEPT",
        name="价值链试点部",
        unit_type="department",
    )
    manager = Employee(
        employee_no="VALUE-MANAGER",
        name="价值链经理",
        email="value-manager@example.com",
        organization_unit=department,
    )
    employee = Employee(
        employee_no="VALUE-EMPLOYEE",
        name="价值链员工",
        email="value-employee@example.com",
        organization_unit=department,
        manager=manager,
    )
    db.add_all([department, manager, employee])
    db.flush()
    db.add(
        BudgetAllocation(
            budget_no="BUD-VALUE-CHAIN-2026Q3",
            fiscal_year=2026,
            period_type="quarter",
            period_key="2026Q3",
            department_id=department.id,
            department_name=department.name,
            subject_code="travel",
            subject_name="差旅",
            original_amount=Decimal("50000.00"),
            adjusted_amount=Decimal("0.00"),
            status="active",
            warning_threshold=Decimal("80.00"),
            control_action="block",
        )
    )
    application = ExpenseClaim(
        id=str(uuid.uuid4()),
        claim_no="APP-VALUE-CHAIN-20260716",
        employee_id=employee.id,
        employee_name=employee.name,
        department_id=department.id,
        department_name=department.name,
        expense_type="travel_application",
        reason="上海客户现场差旅",
        location="上海",
        amount=Decimal("66.00"),
        currency="CNY",
        invoice_count=0,
        occurred_at=now,
        status="draft",
        approval_stage="待提交",
        risk_flags_json=[],
    )
    db.add(application)
    db.commit()
    return (
        {
            "employee": _user(
                employee.email,
                name=employee.name,
                employee_id=employee.id,
            ),
            "manager": _user(
                manager.email,
                name=manager.name,
                employee_id=manager.id,
                roles=["manager"],
            ),
            "finance": _user("value-finance@example.com", roles=["finance"]),
            "confirmer": _user("value-confirmer@example.com", roles=["finance"]),
        },
        application,
    )


def _generated_reimbursement(
    db: Session,
    application: ExpenseClaim,
) -> ExpenseClaim:
    draft_id = next(
        str(flag.get("generated_draft_claim_id"))
        for flag in application.risk_flags_json
        if isinstance(flag, dict) and flag.get("generated_draft_claim_id")
    )
    draft = db.get(ExpenseClaim, draft_id)
    assert draft is not None and draft.status == "draft"
    return draft


def _seed_commercial_account(db: Session, *, now: datetime) -> None:
    admin = CommercialAdminService(db)
    plan = admin.create_plan(
        "default",
        CommercialPlanCreate(
            plan_code="value-pilot",
            name="价值证明试点",
            pricing_model="hybrid",
            billing_interval="monthly",
            currency="CNY",
            base_fee=Decimal("8.00"),
            included_seats=10,
            effective_from=now - timedelta(days=30),
        ),
        actor_id="platform-admin",
    )
    admin.activate_plan("default", plan.id, expected_version=plan.version)
    subscription = admin.create_subscription(
        "default",
        CommercialSubscriptionCreate(
            subscription_key="default-value-pilot-2026",
            plan_id=plan.id,
            starts_at=now - timedelta(days=10),
            current_period_start=now - timedelta(days=1),
            current_period_end=now + timedelta(days=29),
            seats=5,
        ),
        actor_id="platform-admin",
    )
    CommercialMeteringService(db).record_cost(
        "default",
        CommercialCostEventCreate(
            subscription_id=subscription.id,
            cost_category="connector",
            quantity=Decimal("1"),
            unit="workflow",
            unit_cost=Decimal("5.00"),
            original_currency="CNY",
            reporting_currency="CNY",
            fx_rate=Decimal("1"),
            allocation_key="value-chain-workflow",
            occurred_at=now,
            source_system="cost-ledger",
            idempotency_key="value-chain-cost-001",
        ),
    )


def _seed_connector(db: Session) -> None:
    db.add(
        FinancialConnectorConfig(
            id=str(uuid.uuid4()),
            tenant_id="default",
            provider="value-bank",
            environment="production",
            key_version="v1",
            secret_ref="connector/value-chain",
            allowed_event_types_json=[
                "payment_settled",
                "payment_failed",
                "erp_posted",
                "erp_posting_failed",
                "payment_refunded",
                "payment_reversed",
            ],
            clock_skew_seconds=300,
            status="active",
            created_by="platform-admin",
        )
    )


def _financial_event(
    claim: ExpenseClaim,
    *,
    event_id: str,
    event_type: str = "payment_settled",
    extra: dict[str, str] | None = None,
) -> FinancialEventEnvelope:
    return FinancialEventEnvelope(
        tenant_id="default",
        external_event_id=event_id,
        event_type=event_type,
        occurred_at=datetime.now(UTC),
        correlation_id=f"corr-{event_id}",
        payload={
            "claim_id": claim.id,
            "claim_reference": claim.claim_no,
            "amount": str(claim.amount),
            "currency": claim.currency,
            "external_payment_reference": f"PAY-{claim.claim_no}",
            **dict(extra or {}),
        },
    )


def _ingest(
    db: Session,
    envelope: FinancialEventEnvelope,
    timestamp: int,
):
    secret = "value-chain-server-secret"
    signature = sign_financial_event(
        envelope,
        timestamp=timestamp,
        secret=secret,
        tenant_id="default",
        provider="value-bank",
        key_version="v1",
    )
    return FinancialConnectorIngestionService(
        db,
        secrets=FinancialConnectorSecretResolver({"connector/value-chain": secret}),
        now_epoch=timestamp,
    ).ingest(
        envelope,
        tenant_header="default",
        provider_header="value-bank",
        key_version_header="v1",
        timestamp_header=str(timestamp),
        signature_header=signature,
    )


def _event_count(db: Session, event_type: str) -> int:
    return int(
        db.scalar(
            select(func.count(BusinessEvent.id)).where(BusinessEvent.event_type == event_type)
        )
        or 0
    )


def _money(metric) -> dict[str, Decimal]:
    return {item.currency: item.amount for item in metric.values}


def _user(
    username: str,
    *,
    name: str | None = None,
    employee_id: str = "",
    roles: list[str] | None = None,
) -> CurrentUserContext:
    return CurrentUserContext(
        username=username,
        name=name or username,
        employee_id=employee_id,
        role_codes=list(roles or []),
        is_admin=False,
        tenant_id="default",
    )
