from __future__ import annotations

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

import pytest
from commercial_runtime_testkit import seed_meter
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.db.base_class import Base
from app.models.agent_run import AgentRun
from app.models.commercial import UsageMeterEvent
from app.models.commercial_runtime import CommercialRuntimeReservation
from app.services.commercial_direct_operation import CommercialDirectOperationBridge
from app.services.runtime_chat_attempts import (
    RuntimeChatAttemptCompletedEvent,
    RuntimeChatAttemptPermitEvent,
    RuntimeChatAuthoritativeUsage,
    RuntimeChatOperationContext,
)
from app.services.runtime_chat_commercial import (
    CommercialDirectReconciliationRequired,
    CommercialRuntimeChatAttemptObserver,
    trusted_runtime_chat_operation_context,
)


@pytest.fixture()
def factory() -> sessionmaker[Session]:
    engine = create_engine(
        "sqlite+pysqlite:///:memory:",
        connect_args={"check_same_thread": False},
        poolclass=StaticPool,
    )
    Base.metadata.create_all(engine)
    result = sessionmaker(bind=engine, expire_on_commit=False)
    try:
        yield result
    finally:
        Base.metadata.drop_all(engine)
        engine.dispose()


def _identity(*, tenant_id: str = "tenant-a", operation_id: str = "operation-a"):
    return RuntimeChatOperationContext(
        tenant_id=tenant_id,
        operation_id=operation_id,
        run_id="run-a",
        invocation_seq=3,
        attempt_scope="user-agent-response",
    ).build_attempt_identity(
        slot="main",
        provider="Ollama",
        model="qwen-test",
        attempt=1,
    )


def test_runtime_chat_observer_settles_authoritative_provider_tokens(
    factory: sessionmaker[Session],
) -> None:
    now = datetime.now(UTC)
    with factory() as db:
        seed_meter(
            db,
            "tenant-a",
            now,
            basis="total_tokens",
            preflight_quantity=Decimal("50"),
        )
        db.commit()
    observer = CommercialRuntimeChatAttemptObserver(
        CommercialDirectOperationBridge(factory)
    )
    identity = _identity()

    permit = observer.on_permit(
        RuntimeChatAttemptPermitEvent(identity=identity, started_at=now)
    )
    observer.on_completed(
        RuntimeChatAttemptCompletedEvent(
            identity=identity,
            started_at=now,
            completed_at=now + timedelta(milliseconds=250),
            outcome="succeeded",
            response_id=None,
            response_model="qwen-test",
            provider_status_code=200,
            usage=RuntimeChatAuthoritativeUsage(
                source="ollama_response",
                availability="available",
                prompt_eval_count=12,
                eval_count=8,
            ),
        )
    )

    assert permit.allowed is True
    with factory() as db:
        reservation = db.scalars(select(CommercialRuntimeReservation)).one()
        usage = db.scalars(select(UsageMeterEvent)).one()
        assert reservation.status == "committed"
        assert Decimal(reservation.actual_quantity or 0) == Decimal("20")
        assert Decimal(usage.quantity) == Decimal("20")
        assert usage.metadata_json["usage_source"] == "ollama_response"


def test_runtime_chat_observer_persists_reconciliation_when_usage_is_missing(
    factory: sessionmaker[Session],
) -> None:
    now = datetime.now(UTC)
    with factory() as db:
        seed_meter(
            db,
            "tenant-a",
            now,
            basis="total_tokens",
            preflight_quantity=Decimal("50"),
        )
        db.commit()
    observer = CommercialRuntimeChatAttemptObserver(
        CommercialDirectOperationBridge(factory)
    )
    identity = _identity(operation_id="operation-missing-usage")
    assert observer.on_permit(
        RuntimeChatAttemptPermitEvent(identity=identity, started_at=now)
    ).allowed

    with pytest.raises(
        CommercialDirectReconciliationRequired,
        match="authoritative_usage_unavailable",
    ):
        observer.on_completed(
            RuntimeChatAttemptCompletedEvent(
                identity=identity,
                started_at=now,
                completed_at=now + timedelta(seconds=1),
                outcome="succeeded",
                response_id="response-without-usage",
                response_model="qwen-test",
                provider_status_code=200,
                usage=RuntimeChatAuthoritativeUsage(),
            )
        )

    with factory() as db:
        reservation = db.scalars(select(CommercialRuntimeReservation)).one()
        assert reservation.status == "reconciliation_required"
        assert reservation.resolution_code == "authoritative_usage_unavailable"
        assert db.query(UsageMeterEvent).count() == 0


def test_operation_context_uses_only_persisted_agent_run_tenant(
    factory: sessionmaker[Session],
) -> None:
    now = datetime.now(UTC)
    with factory() as db:
        db.add_all(
            [
                AgentRun(
                    run_id="trusted-run",
                    agent="user_agent",
                    source="chat",
                    user_id="user-a",
                    route_json={"tenant_id": "tenant-trusted"},
                    permission_level="write",
                    status="running",
                    started_at=now,
                ),
                AgentRun(
                    run_id="unscoped-run",
                    agent="user_agent",
                    source="chat",
                    user_id="user-a",
                    route_json={"context_json": {"tenant_id": "tenant-spoofed"}},
                    permission_level="write",
                    status="running",
                    started_at=now,
                ),
            ]
        )
        db.commit()

        context = trusted_runtime_chat_operation_context(
            db,
            run_id="trusted-run",
            attempt_scope="user-agent-response",
            invocation_seq=2,
        )

        assert context is not None
        assert context.tenant_id == "tenant-trusted"
        assert context.operation_id == "agent-run:trusted-run"
        assert context.invocation_seq == 2
        assert (
            trusted_runtime_chat_operation_context(
                db,
                run_id="unscoped-run",
                attempt_scope="user-agent-response",
            )
            is None
        )
        assert (
            trusted_runtime_chat_operation_context(
                db,
                run_id="missing-run",
                attempt_scope="user-agent-response",
            )
            is None
        )
