from __future__ import annotations

from collections.abc import Generator
from datetime import UTC, datetime, timedelta

import pytest
from fastapi import FastAPI
from fastapi.testclient import TestClient
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, get_current_user, get_db
from app.api.v1.endpoints.commercial import router
from app.api.v1.endpoints.commercial_billing import router as billing_router
from app.db.base_class import Base


@pytest.fixture()
def http_context() -> Generator[
    tuple[TestClient, dict[str, CurrentUserContext]],
    None,
    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)
    app = FastAPI()
    app.include_router(router, prefix="/api/v1")
    app.include_router(billing_router, prefix="/api/v1")
    user_box = {"current": _user("platform-admin", "platform", is_admin=True)}

    def override_db() -> Generator[Session, None, None]:
        with factory() as db:
            yield db

    app.dependency_overrides[get_db] = override_db
    app.dependency_overrides[get_current_user] = lambda: user_box["current"]
    client = TestClient(app)
    client.headers.update({"X-Request-Id": "commercial-endpoint-test"})
    try:
        yield client, user_box
    finally:
        client.close()
        app.dependency_overrides.clear()
        Base.metadata.drop_all(engine)
        engine.dispose()


def test_commercial_http_admin_config_metering_and_tenant_read_scope(
    http_context: tuple[TestClient, dict[str, CurrentUserContext]],
) -> None:
    client, user_box = http_context
    now = datetime.now(UTC)
    plan_response = client.post(
        "/api/v1/commercial/admin/tenants/tenant-a/plans",
        json={
            "plan_code": "enterprise",
            "name": "企业版",
            "pricing_model": "subscription",
            "billing_interval": "monthly",
            "currency": "CNY",
            "base_fee": "500.0000",
            "included_seats": 50,
            "effective_from": (now - timedelta(days=30)).isoformat(),
        },
    )
    assert plan_response.status_code == 201
    plan = plan_response.json()
    assert plan["status"] == "draft"
    activate_response = client.post(
        f"/api/v1/commercial/admin/tenants/tenant-a/plans/{plan['id']}/activate",
        json={"expected_version": plan["version"], "reason": "启用企业套餐"},
    )
    assert activate_response.status_code == 200
    assert activate_response.json()["plan"]["status"] == "active"

    subscription_response = client.post(
        "/api/v1/commercial/admin/tenants/tenant-a/subscriptions",
        json={
            "subscription_key": "tenant-a-2026",
            "plan_id": plan["id"],
            "status": "active",
            "starts_at": (now - timedelta(days=5)).isoformat(),
            "current_period_start": (now - timedelta(days=1)).isoformat(),
            "current_period_end": (now + timedelta(days=29)).isoformat(),
            "seats": 20,
        },
    )
    assert subscription_response.status_code == 201
    subscription = subscription_response.json()
    entitlement_response = client.put(
        "/api/v1/commercial/admin/tenants/tenant-a/entitlements",
        json={
            "subscription_id": subscription["id"],
            "entitlement_key": "ocr",
            "metric_key": "ocr_pages",
            "entitlement_type": "metered",
            "unit": "page",
            "included_quantity": "100",
            "hard_limit_quantity": "120",
            "reset_interval": "monthly",
            "overage_policy": "block",
            "status": "active",
            "effective_from": (now - timedelta(days=5)).isoformat(),
        },
    )
    assert entitlement_response.status_code == 200
    entitlement = entitlement_response.json()

    usage_payload = {
        "subscription_id": subscription["id"],
        "entitlement_id": entitlement["id"],
        "event_type": "usage",
        "quantity": "10",
        "occurred_at": now.isoformat(),
        "source_system": "ocr-runtime",
        "idempotency_key": "ocr-usage-001",
    }
    first_usage = client.post(
        "/api/v1/commercial/admin/tenants/tenant-a/usage-events",
        json=usage_payload,
    )
    replay_usage = client.post(
        "/api/v1/commercial/admin/tenants/tenant-a/usage-events",
        json=usage_payload,
    )
    assert first_usage.status_code == 200
    assert first_usage.json()["created"] is True
    assert replay_usage.status_code == 200
    assert replay_usage.json()["created"] is False
    conflict = client.post(
        "/api/v1/commercial/admin/tenants/tenant-a/usage-events",
        json={**usage_payload, "quantity": "11"},
    )
    assert conflict.status_code == 409

    assert (
        client.get("/api/v1/commercial/admin/tenants/tenant-a/plans").json()[0]["id"] == plan["id"]
    )
    assert (
        client.get("/api/v1/commercial/admin/tenants/tenant-a/subscriptions").json()[0]["id"]
        == subscription["id"]
    )
    assert (
        client.get("/api/v1/commercial/admin/tenants/tenant-a/entitlements").json()[0]["id"]
        == entitlement["id"]
    )
    assert (
        client.get("/api/v1/commercial/admin/tenants/tenant-a/usage-events").json()[0]["id"]
        == first_usage.json()["usage_event"]["id"]
    )
    suspended = client.post(
        f"/api/v1/commercial/admin/tenants/tenant-a/subscriptions/{subscription['id']}/transition",
        json={
            "expected_version": subscription["version"],
            "target_status": "suspended",
            "reason": "客户主动暂停商业服务",
        },
    )
    assert suspended.status_code == 200
    assert suspended.json()["status"] == "suspended"
    resumed = client.post(
        f"/api/v1/commercial/admin/tenants/tenant-a/subscriptions/{subscription['id']}/activate",
        json={
            "expected_version": suspended.json()["version"],
            "reason": "客户确认恢复商业服务",
        },
    )
    assert resumed.status_code == 200
    assert resumed.json()["status"] == "active"
    periods = client.get("/api/v1/commercial/admin/tenants/tenant-a/billing-periods")
    assert periods.status_code == 200
    assert periods.json()[0]["subscription_id"] == subscription["id"]
    assert periods.json()[0]["temporal_state"] == "current"
    audit_events = client.get("/api/v1/commercial/admin/tenants/tenant-a/admin-events")
    assert audit_events.status_code == 200
    assert {item["action"] for item in audit_events.json()} >= {
        "plan_created",
        "subscription_created",
        "billing_period_created",
    }

    user_box["current"] = _user("finance-a", "tenant-a", roles=["finance"])
    account = client.get("/api/v1/commercial/account")
    assert account.status_code == 200
    assert account.json()["tenant_id"] == "tenant-a"
    assert account.json()["plan"]["id"] == plan["id"]
    assert account.json()["quotas"][0]["used_quantity"] == "10.000000"
    assert client.get("/api/v1/commercial/billing-periods").status_code == 200
    assert (
        client.get(
            "/api/v1/commercial/admin/tenants/tenant-a/admin-events"
        ).status_code
        == 403
    )
    assert (
        client.post(
            "/api/v1/commercial/admin/tenants/tenant-a/cost-events",
            json=_cost_json(subscription["id"], now),
        ).status_code
        == 403
    )

    user_box["current"] = _user("finance-b", "tenant-b", roles=["finance"])
    other_account = client.get("/api/v1/commercial/account")
    assert other_account.status_code == 200
    assert other_account.json()["tenant_id"] == "tenant-b"
    assert other_account.json()["subscription"] is None
    assert other_account.json()["quotas"] == []
    assert client.get("/api/v1/commercial/billing-periods").json() == []

    user_box["current"] = _user("employee-a", "tenant-a")
    assert client.get("/api/v1/commercial/account").status_code == 403
    assert client.get("/api/v1/commercial/billing-periods").status_code == 403
    user_box["current"] = _user("manager-a", "tenant-a", roles=["manager"])
    assert client.get("/api/v1/commercial/account").status_code == 403
    assert (
        client.post(
            "/api/v1/commercial/admin/tenants/tenant-a/plans",
            json={},
        ).status_code
        == 403
    )


def test_commercial_http_cost_is_admin_only_and_analytics_never_fakes_missing_value(
    http_context: tuple[TestClient, dict[str, CurrentUserContext]],
) -> None:
    client, user_box = http_context
    now = datetime.now(UTC)
    plan = client.post(
        "/api/v1/commercial/admin/tenants/tenant-a/plans",
        json={
            "plan_code": "pilot",
            "name": "试点版",
            "pricing_model": "pilot",
            "billing_interval": "contract",
            "currency": "CNY",
            "base_fee": "1000",
            "effective_from": (now - timedelta(days=10)).isoformat(),
        },
    ).json()
    client.post(
        f"/api/v1/commercial/admin/tenants/tenant-a/plans/{plan['id']}/activate",
        json={"expected_version": plan["version"], "reason": "启用试点套餐"},
    )
    subscription = client.post(
        "/api/v1/commercial/admin/tenants/tenant-a/subscriptions",
        json={
            "subscription_key": "pilot-a",
            "plan_id": plan["id"],
            "starts_at": (now - timedelta(days=3)).isoformat(),
            "current_period_start": (now - timedelta(days=3)).isoformat(),
            "current_period_end": (now + timedelta(days=87)).isoformat(),
            "seats": 5,
        },
    ).json()

    cost = client.post(
        "/api/v1/commercial/admin/tenants/tenant-a/cost-events",
        json=_cost_json(subscription["id"], now),
    )
    replay = client.post(
        "/api/v1/commercial/admin/tenants/tenant-a/cost-events",
        json=_cost_json(subscription["id"], now),
    )
    assert cost.status_code == 200
    assert cost.json()["created"] is True
    assert cost.json()["cost_event"]["cost_amount"] == "25.0000"
    assert replay.status_code == 200
    assert replay.json()["created"] is False

    analytics = client.get(
        "/api/v1/commercial/admin/tenants/tenant-a/analytics",
        params={
            "start": (now - timedelta(days=5)).isoformat(),
            "end": (now + timedelta(days=1)).isoformat(),
            "as_of": (now + timedelta(hours=1)).isoformat(),
        },
    )
    assert analytics.status_code == 200
    body = analytics.json()
    assert body["customer_charges"]["status"] == "partial"
    assert body["internal_costs"]["status"] == "available"
    assert body["contribution_margin"]["status"] == "partial"
    assert body["verified_cash_savings"]["status"] == "unavailable"
    assert body["verified_cash_savings"]["values"] == []
    assert body["customer_roi"]["status"] == "unavailable"
    assert body["customer_labor_value"]["status"] == "unavailable"

    pricing = client.post(
        "/api/v1/commercial/admin/tenants/tenant-a/pricing-scenarios",
        json={
            "start": (now - timedelta(days=5)).isoformat(),
            "end": (now + timedelta(days=1)).isoformat(),
            "as_of": (now + timedelta(hours=1)).isoformat(),
            "target_contribution_margin_rate": "0.65",
            "max_verified_savings_share": "0.25",
        },
    )
    assert pricing.status_code == 200
    assert pricing.json()["recommended_model"] == "subscription"
    assert pricing.json()["scenarios"][0]["status"] == "cost_only"

    user_box["current"] = _user("finance-a", "tenant-a", roles=["finance"])
    assert client.get("/api/v1/commercial/admin/tenants/tenant-a/analytics").status_code == 403


def _cost_json(subscription_id: str, now: datetime) -> dict[str, object]:
    return {
        "subscription_id": subscription_id,
        "event_type": "incurred",
        "cost_category": "ocr",
        "quantity": "100",
        "unit": "page",
        "unit_cost": "0.25",
        "original_currency": "CNY",
        "reporting_currency": "CNY",
        "fx_rate": "1",
        "allocation_key": "tenant-a:ocr",
        "occurred_at": now.isoformat(),
        "source_system": "ocr-provider",
        "idempotency_key": "ocr-cost-001",
    }


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,
    )
