from __future__ import annotations

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

import pytest
from sqlalchemy import create_engine
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.financial_record import ExpenseClaim, ExpenseClaimItem
from app.schemas.commercial import (
    CommercialCostEventCreate,
    CommercialEntitlementUpsert,
    CommercialPlanCreate,
    CommercialPricingScenarioWrite,
    CommercialSubscriptionCreate,
    CommercialSubscriptionTransition,
    UsageMeterEventCreate,
)
from app.schemas.savings import (
    SavingsEvidenceCreate,
    SavingsRealizationActionCreate,
    SavingsRealizationCreate,
)
from app.services.commercial_access_policy import CommercialConflictError
from app.services.commercial_admin import CommercialAdminService
from app.services.commercial_analytics import CommercialAnalyticsService
from app.services.commercial_entitlements import CommercialEntitlementService
from app.services.commercial_metering import CommercialMeteringService
from app.services.commercial_pricing import CommercialPricingService
from app.services.commercial_queries import CommercialQueryService
from app.services.savings_discovery import SavingsDiscoveryService
from app.services.savings_realization import SavingsRealizationService


@pytest.fixture()
def db() -> Session:
    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)
    with factory() as session:
        yield session
    Base.metadata.drop_all(engine)
    engine.dispose()


def test_commercial_account_quota_idempotency_and_security_gate(db: Session) -> None:
    now = datetime.now(UTC)
    _, subscription, entitlement = _seed_commercial_account(db, "tenant-a", now)
    service = CommercialMeteringService(db)
    payload = UsageMeterEventCreate(
        subscription_id=subscription.id,
        entitlement_id=entitlement.id,
        quantity=Decimal("2"),
        occurred_at=now,
        source_system="agent-runtime",
        idempotency_key="usage-001",
        subject_type="agent_run",
        subject_id="run-001",
    )
    created, was_created = service.record_usage(
        "tenant-a",
        payload,
        actor_type="system",
        actor_id="agent-runtime",
    )
    replay, replay_created = service.record_usage(
        "tenant-a",
        payload,
        actor_type="system",
        actor_id="agent-runtime",
    )
    assert was_created is True
    assert replay_created is False
    assert replay.id == created.id

    with pytest.raises(CommercialConflictError, match="幂等键"):
        service.record_usage(
            "tenant-a",
            payload.model_copy(update={"quantity": Decimal("3")}),
            actor_type="system",
            actor_id="agent-runtime",
        )

    account = CommercialEntitlementService(db).get_account(
        _user("finance-a", tenant_id="tenant-a", roles=["finance"]),
        as_of=now + timedelta(seconds=1),
    )
    assert account.tenant_id == "tenant-a"
    assert account.quotas[0].used_quantity == Decimal("2")
    assert account.quotas[0].hard_limit_remaining == Decimal("4")

    commercial_gate = CommercialEntitlementService(db).check(
        "tenant-a",
        entitlement_key="ai_precheck",
        requested_quantity=Decimal("1"),
        security_decision="human_review",
        as_of=now + timedelta(seconds=1),
    )
    assert commercial_gate.commercial_allowed is True
    assert commercial_gate.final_allowed is False
    assert "人工审核" in commercial_gate.reason

    with pytest.raises(CommercialConflictError, match="硬配额"):
        service.record_usage(
            "tenant-a",
            payload.model_copy(
                update={
                    "quantity": Decimal("5"),
                    "idempotency_key": "usage-over-limit",
                }
            ),
            actor_type="system",
            actor_id="agent-runtime",
        )

    with pytest.raises(CommercialConflictError, match="不能回改配额"):
        CommercialAdminService(db).upsert_entitlement(
            "tenant-a",
            CommercialEntitlementUpsert(
                subscription_id=subscription.id,
                entitlement_key="ai_precheck",
                metric_key="ai_precheck_runs",
                entitlement_type="metered",
                unit="run",
                included_quantity=Decimal("5"),
                hard_limit_quantity=Decimal("8"),
                reset_interval="monthly",
                overage_policy="block",
                effective_from=now - timedelta(days=10),
            ),
        )
    with pytest.raises(LookupError):
        service.record_usage(
            "tenant-b",
            payload.model_copy(update={"idempotency_key": "cross-tenant"}),
            actor_type="system",
            actor_id="agent-runtime",
        )


def test_cost_events_are_server_derived_idempotent_and_reversible(db: Session) -> None:
    now = datetime.now(UTC)
    _, subscription, _ = _seed_commercial_account(db, "tenant-a", now)
    payload = CommercialCostEventCreate(
        subscription_id=subscription.id,
        event_type="incurred",
        cost_category="ai_inference",
        quantity=Decimal("200"),
        unit="1k_tokens",
        unit_cost=Decimal("0.1"),
        original_currency="CNY",
        reporting_currency="CNY",
        fx_rate=Decimal("1"),
        provider="provider-a",
        model_name="model-a",
        allocation_key="tenant-a:ai",
        occurred_at=now,
        source_system="provider-billing",
        idempotency_key="cost-001",
    )
    service = CommercialMeteringService(db)
    event, created = service.record_cost("tenant-a", payload)
    replay, replay_created = service.record_cost("tenant-a", payload)
    assert created is True
    assert replay_created is False
    assert replay.id == event.id
    assert event.cost_amount == Decimal("20.0000")
    assert event.reporting_amount == Decimal("20.0000")

    with pytest.raises(CommercialConflictError, match="幂等键"):
        service.record_cost(
            "tenant-a",
            payload.model_copy(update={"quantity": Decimal("201")}),
        )

    reversal_payload = payload.model_copy(
        update={
            "event_type": "reversal",
            "idempotency_key": "cost-reversal-001",
            "reversal_of_cost_event_id": event.id,
            "occurred_at": now + timedelta(minutes=1),
        }
    )
    reversal, _ = service.record_cost("tenant-a", reversal_payload)
    assert reversal.cost_amount == Decimal("-20.0000")
    assert reversal.reporting_amount == Decimal("-20.0000")
    with pytest.raises(CommercialConflictError, match="已经冲回"):
        service.record_cost(
            "tenant-a",
            reversal_payload.model_copy(update={"idempotency_key": "cost-reversal-002"}),
        )


def test_commercial_analytics_separates_charge_cost_savings_and_currency(db: Session) -> None:
    now = datetime.now(UTC)
    _, subscription, _ = _seed_commercial_account(db, "tenant-a", now)
    CommercialMeteringService(db).record_cost(
        "tenant-a",
        _cost_payload(subscription.id, now, "CNY", Decimal("20"), "cost-cny"),
    )
    CommercialMeteringService(db).record_cost(
        "tenant-a",
        _cost_payload(subscription.id, now, "USD", Decimal("5"), "cost-usd"),
    )
    _seed_verified_savings(db, "tenant-a", now, Decimal("200"), "CNY")
    db.commit()

    result = CommercialAnalyticsService(db).build(
        "tenant-a",
        start=now - timedelta(days=2),
        end=now + timedelta(days=2),
        as_of=now + timedelta(days=1),
    )
    assert result.customer_charges.status == "partial"
    assert _money(result.customer_charges) == {"CNY": Decimal("100.0000")}
    assert _money(result.internal_costs) == {
        "CNY": Decimal("20.0000"),
        "USD": Decimal("5.0000"),
    }
    assert _money(result.contribution_margin) == {"CNY": Decimal("80.0000")}
    assert result.contribution_margin.status == "partial"
    assert result.verified_cash_savings.status == "available"
    assert _money(result.verified_cash_savings) == {"CNY": Decimal("200.0000")}
    assert result.customer_roi.ratios[0].currency == "CNY"
    assert result.customer_roi.ratios[0].ratio == Decimal("1.000000")
    assert result.customer_roi.ratios[0].numerator == Decimal("100.0000")
    assert result.customer_labor_value.status == "unavailable"
    assert any("币种" in note for note in result.contribution_margin.notes)

    unavailable = CommercialAnalyticsService(db).build(
        "tenant-b",
        start=now - timedelta(days=2),
        end=now + timedelta(days=2),
        as_of=now + timedelta(days=1),
    )
    assert unavailable.customer_charges.status == "unavailable"
    assert unavailable.customer_charges.values == []
    assert unavailable.internal_costs.status == "unavailable"
    assert unavailable.customer_roi.status == "unavailable"


def test_commercial_write_contract_rejects_ambiguous_naive_timestamps() -> None:
    with pytest.raises(ValueError, match="时区"):
        UsageMeterEventCreate(
            subscription_id=str(uuid.uuid4()),
            entitlement_id=str(uuid.uuid4()),
            quantity=Decimal("1"),
            occurred_at=datetime(2026, 7, 16, 12, 0),
            source_system="ambiguous-clock",
            idempotency_key="naive-time",
        )


def test_subscription_lifecycle_and_history_queries_are_operable(db: Session) -> None:
    now = datetime.now(UTC)
    plan, subscription, entitlement = _seed_commercial_account(db, "tenant-a", now)
    admin = CommercialAdminService(db)
    suspended = admin.transition_subscription(
        "tenant-a",
        subscription.id,
        CommercialSubscriptionTransition(
            expected_version=subscription.version,
            target_status="suspended",
            reason="客户要求暂时停用自动续费与用量消费",
        ),
        actor_id="platform-admin",
    )
    assert suspended.status == "suspended"
    assert suspended.metadata_json["status_history"][-1]["to"] == "suspended"
    resumed = admin.activate_subscription(
        "tenant-a",
        subscription.id,
        expected_version=suspended.version,
    )
    assert resumed.status == "active"
    canceled = admin.transition_subscription(
        "tenant-a",
        subscription.id,
        CommercialSubscriptionTransition(
            expected_version=resumed.version,
            target_status="canceled",
            reason="试点合同到期且客户确认不再续约",
        ),
        actor_id="platform-admin",
    )
    assert canceled.status == "canceled"
    assert canceled.canceled_at is not None
    assert canceled.ends_at is not None
    with pytest.raises(CommercialConflictError, match="不能从"):
        admin.transition_subscription(
            "tenant-a",
            subscription.id,
            CommercialSubscriptionTransition(
                expected_version=canceled.version,
                target_status="suspended",
                reason="终态不可恢复",
            ),
            actor_id="platform-admin",
        )

    query = CommercialQueryService(db)
    assert [row.id for row in query.list_plans("tenant-a")] == [plan.id]
    assert [row.id for row in query.list_subscriptions("tenant-a")] == [subscription.id]
    assert [row.id for row in query.list_entitlements("tenant-a")] == [entitlement.id]
    assert query.list_plans("tenant-b") == []
    with pytest.raises(ValueError, match="时区"):
        query.list_usage_events(
            "tenant-a",
            start=datetime(2026, 7, 16, 0, 0),
        )


def test_pricing_scenario_uses_cost_floor_and_verified_value_ceiling(db: Session) -> None:
    now = datetime.now(UTC)
    _, subscription, _ = _seed_commercial_account(db, "tenant-a", now)
    CommercialMeteringService(db).record_cost(
        "tenant-a",
        _cost_payload(subscription.id, now, "CNY", Decimal("20"), "pricing-cost"),
    )
    _seed_verified_savings(db, "tenant-a", now, Decimal("200"), "CNY")
    db.commit()

    result = CommercialPricingService(db).build(
        "tenant-a",
        CommercialPricingScenarioWrite(
            start=now - timedelta(days=2),
            end=now + timedelta(days=2),
            as_of=now + timedelta(days=1),
            target_contribution_margin_rate=Decimal("0.5"),
            max_verified_savings_share=Decimal("0.25"),
        ),
    )
    assert result.recommended_model == "hybrid"
    assert result.evidence_status == "complete"
    scenario = result.scenarios[0]
    assert scenario.status == "feasible"
    assert scenario.minimum_sustainable_charge == Decimal("40.0000")
    assert scenario.maximum_value_aligned_charge == Decimal("50.0000")
    assert scenario.maximum_success_fee == Decimal("10.0000")
    assert scenario.customer_roi_at_minimum_charge == Decimal("4.000000")
    assert scenario.contribution_margin_at_value_ceiling == Decimal("0.600000")

    conflict = CommercialPricingService(db).build(
        "tenant-a",
        CommercialPricingScenarioWrite(
            start=now - timedelta(days=2),
            end=now + timedelta(days=2),
            as_of=now + timedelta(days=1),
            target_contribution_margin_rate=Decimal("0.5"),
            max_verified_savings_share=Decimal("0.05"),
        ),
    )
    assert conflict.recommended_model == "optimize_unit_economics"
    assert conflict.scenarios[0].status == "insufficient_value"


def _seed_commercial_account(db: Session, tenant_id: str, now: datetime):
    admin = CommercialAdminService(db)
    plan = admin.create_plan(
        tenant_id,
        CommercialPlanCreate(
            plan_code="growth",
            name="成长版",
            pricing_model="subscription",
            billing_interval="monthly",
            currency="CNY",
            base_fee=Decimal("100"),
            included_seats=10,
            effective_from=now - timedelta(days=30),
        ),
        actor_id="platform-admin",
    )
    admin.activate_plan(tenant_id, plan.id, expected_version=plan.version)
    subscription = admin.create_subscription(
        tenant_id,
        CommercialSubscriptionCreate(
            subscription_key=f"{tenant_id}-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",
    )
    entitlement = admin.upsert_entitlement(
        tenant_id,
        CommercialEntitlementUpsert(
            subscription_id=subscription.id,
            entitlement_key="ai_precheck",
            metric_key="ai_precheck_runs",
            entitlement_type="metered",
            unit="run",
            included_quantity=Decimal("5"),
            hard_limit_quantity=Decimal("6"),
            reset_interval="monthly",
            overage_policy="block",
            effective_from=now - timedelta(days=10),
        ),
    )
    db.flush()
    return plan, subscription, entitlement


def _cost_payload(
    subscription_id: str,
    now: datetime,
    currency: str,
    amount: Decimal,
    key: str,
) -> CommercialCostEventCreate:
    return CommercialCostEventCreate(
        subscription_id=subscription_id,
        cost_category="infrastructure",
        quantity=Decimal("1"),
        unit="allocation",
        unit_cost=amount,
        original_currency=currency,
        reporting_currency=currency,
        fx_rate=Decimal("1"),
        allocation_key=key,
        occurred_at=now,
        source_system="cost-ledger",
        idempotency_key=key,
    )


def _seed_verified_savings(
    db: Session,
    tenant_id: str,
    now: datetime,
    amount: Decimal,
    currency: str,
) -> None:
    operator = _user("finance-operator", tenant_id=tenant_id, roles=["finance"])
    confirmer = _user("finance-confirmer", tenant_id=tenant_id, roles=["finance"])
    original = Decimal("1000")
    claim = ExpenseClaim(
        id=str(uuid.uuid4()),
        claim_no=f"BX-{uuid.uuid4().hex[:12]}",
        employee_name="测试员工",
        department_name="销售部",
        project_code="COMMERCIAL-ROI",
        expense_type="hotel",
        reason="客户现场差旅",
        location="上海",
        amount=original,
        currency=currency,
        invoice_count=1,
        occurred_at=now,
        status="draft",
        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=original,
    )
    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": "商业 ROI 测试政策差额",
                "original_amount": str(original),
                "reimbursable_amount": str(original - amount),
                "employee_absorbed_amount": str(amount),
                "policy_rule_version": "commercial-roi-v1",
                "policy_grade": "P6",
                "policy_matched_city": "上海",
                "calculation_fingerprint": "sha256:" + uuid.uuid4().hex * 2,
            }
        ],
        current_user=operator,
        request_id=f"discover-{uuid.uuid4().hex[:8]}",
    )[0]
    realization = (
        SavingsRealizationService(db)
        .record(
            opportunity.id,
            SavingsRealizationCreate(
                request_id=f"record-{uuid.uuid4().hex[:8]}",
                expected_version=1,
                comment="登记可追溯实际节省",
                actual_gross=amount,
                incremental_cost=Decimal("0"),
                currency=currency,
                realized_at=now,
                attribution_method="server_policy_counterfactual",
                attribution_ratio=Decimal("1"),
                evidence_level="business_state",
                evidence=[
                    SavingsEvidenceCreate(
                        evidence_key=f"payment-{uuid.uuid4().hex}",
                        evidence_role="payment_business_state",
                        resource_type="business_event",
                        resource_id=f"payment-{uuid.uuid4().hex}",
                        source_system="x-financial",
                        content_hash="c" * 64,
                        occurred_at=now,
                        verification_status="unverified",
                    )
                ],
            ),
            operator,
        )
        .response.realization
    )
    SavingsRealizationService(db).execute_action(
        realization.id,
        SavingsRealizationActionCreate(
            action="confirm",
            request_id=f"confirm-{uuid.uuid4().hex[:8]}",
            expected_version=1,
            comment="独立财务确认商业 ROI 事实",
        ),
        confirmer,
    )


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


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