from __future__ import annotations

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

from sqlalchemy import create_engine, 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.expense_case import BusinessEvent
from app.models.financial_record import ExpenseClaim, ExpenseClaimItem
from app.models.savings import SavingsEvidenceLink, SavingsRealization
from app.schemas.cfo_value import CfoValueFiltersRead
from app.schemas.savings import SavingsRealizationActionCreate
from app.services.cfo_value_analytics import CfoValueAnalyticsService
from app.services.expense_claims import ExpenseClaimService
from app.services.savings_discovery import SavingsDiscoveryService
from app.services.savings_realization import SavingsRealizationService


def test_standard_adjustment_payment_confirmation_and_reversal_value_e2e() -> 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_value_e2e(db)
    finally:
        Base.metadata.drop_all(engine)
        engine.dispose()


def _run_value_e2e(db: Session) -> None:
    payer = _finance_user("finance-payer")
    confirmer = _finance_user("finance-confirmer")
    claim = ExpenseClaim(
        id=str(uuid.uuid4()),
        claim_no="BX-SAVINGS-E2E-001",
        employee_name="测试员工",
        department_name="销售部",
        project_code="PROJECT-E2E",
        expense_type="hotel",
        reason="上海客户现场差旅",
        location="上海",
        amount=Decimal("66.00"),
        currency="CNY",
        invoice_count=1,
        occurred_at=datetime.now(UTC),
        submitted_at=datetime.now(UTC),
        status="pending_payment",
        approval_stage="待付款",
        risk_flags_json=[],
    )
    item = ExpenseClaimItem(
        id=str(uuid.uuid4()),
        claim=claim,
        item_date=date.today(),
        item_type="hotel",
        item_reason="上海住宿",
        item_location="上海",
        item_note="",
        item_amount=Decimal("100.00"),
    )
    db.add(claim)
    db.flush()
    opportunity = SavingsDiscoveryService(db).discover_standard_adjustments(
        claim=claim,
        items_by_id={item.id: item},
        adjustment_flags=[
            {
                "item_id": item.id,
                "message": "服务端按已发布住宿政策从 100 元调整为 66 元",
                "original_amount": "100.00",
                "reimbursable_amount": "66.00",
                "employee_absorbed_amount": "34.00",
                "policy_rule_version": "hotel-policy-e2e-v1",
                "policy_rule_version_source": "published",
                "policy_grade": "P6",
                "policy_matched_city": "上海",
                "calculation_fingerprint": "sha256:" + "d" * 64,
            }
        ],
        current_user=payer,
        request_id="e2e-discovery-001",
    )[0]
    db.commit()

    ExpenseClaimService(db).mark_claim_paid(
        claim.id,
        payer,
        request_id="e2e-payment-001",
        expected_status="pending_payment",
        expected_approval_stage="待付款",
    )
    realization = db.scalar(
        select(SavingsRealization).where(
            SavingsRealization.opportunity_id == opportunity.id,
            SavingsRealization.realization_type == "actual",
        )
    )
    assert realization is not None
    assert realization.status == "pending_confirmation"
    assert realization.actual_net == Decimal("34.0000")
    assert _verified_cash(db, confirmer) == {}

    confirmed = SavingsRealizationService(db).execute_action(
        realization.id,
        SavingsRealizationActionCreate(
            action="confirm",
            request_id="e2e-confirm-001",
            expected_version=1,
            comment="独立复核政策基线、付款事件、归因和去重键。",
        ),
        confirmer,
    )
    confirmed_as_of = confirmed.response.event.occurred_at
    assert _verified_cash(db, confirmer) == {"CNY": Decimal("34.0000")}
    evidence = list(
        db.scalars(
            select(SavingsEvidenceLink).where(
                SavingsEvidenceLink.realization_id == realization.id
            )
        ).all()
    )
    assert evidence and all(item.verification_status == "verified" for item in evidence)

    SavingsRealizationService(db).execute_action(
        realization.id,
        SavingsRealizationActionCreate(
            action="reverse",
            request_id="e2e-reverse-001",
            expected_version=2,
            comment="员工申诉后完成补付，全额冲回原节省。",
            reversal_amount=Decimal("34.00"),
        ),
        confirmer,
    )
    assert _verified_cash(db, confirmer) == {"CNY": Decimal("0.0000")}
    assert _verified_cash(db, confirmer, as_of=confirmed_as_of) == {
        "CNY": Decimal("34.0000")
    }
    event_types = set(
        db.scalars(
            select(BusinessEvent.event_type).where(
                BusinessEvent.expense_case_id == opportunity.expense_case_id
            )
        ).all()
    )
    assert {
        "saving_opportunity_created",
        "payment_completed",
        "saving_action_completed",
        "saving_confirmed",
        "saving_reversed",
    } <= event_types


def _verified_cash(
    db: Session,
    current_user: CurrentUserContext,
    *,
    as_of: datetime | None = None,
) -> dict[str, Decimal]:
    now = datetime.now(UTC)
    dashboard = CfoValueAnalyticsService(db).build_dashboard(
        current_user,
        start=now - timedelta(days=1),
        end=now + timedelta(days=1),
        as_of=as_of or now,
        filters=CfoValueFiltersRead(),
    )
    return {
        item.currency: item.amount for item in dashboard.kpis.verified_cash.values
    }


def _finance_user(username: str) -> CurrentUserContext:
    return CurrentUserContext(
        username=username,
        name=username,
        role_codes=["finance"],
        is_admin=False,
        tenant_id="default",
        employee_id=username,
    )
