"""商业运行时额度预占、结算与补偿状态。"""

from __future__ import annotations

import hashlib
import json
from dataclasses import dataclass
from datetime import UTC, datetime, timedelta
from decimal import Decimal
from typing import Any

from sqlalchemy import func, select
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import Session

from app.models.commercial import CommercialEntitlement, TenantSubscription, UsageMeterEvent
from app.models.commercial_billing import CommercialBillingPeriod
from app.models.commercial_runtime import CommercialRuntimeReservation
from app.services.commercial_access_policy import (
    CommercialConfigurationError,
    CommercialConflictError,
)
from app.services.commercial_billing_periods import CommercialBillingPeriodService
from app.services.commercial_periods import quota_period_key_for
from app.services.commercial_runtime_policy import runtime_meter_matches

HOLDING_RESERVATION_STATUSES = ("reserved", "reconciliation_required")
DEFAULT_RESERVATION_TTL = timedelta(minutes=15)
MAX_RESERVATION_TTL = timedelta(hours=1)


@dataclass(frozen=True)
class RuntimeReservationUsage:
    used_quantity: Decimal
    held_quantity: Decimal
    effective_limit: Decimal | None


class CommercialRuntimeReservationService:
    def __init__(self, db: Session) -> None:
        self.db = db

    def reserve(
        self,
        *,
        tenant_id: str,
        entitlement_id: str,
        run_id: str,
        tool_call_id: str,
        tool_type: str,
        tool_name: str,
        quantity_basis: str,
        reserved_quantity: Decimal,
        meter_config: dict[str, Any],
        as_of: datetime | None = None,
        ttl: timedelta = DEFAULT_RESERVATION_TTL,
    ) -> tuple[CommercialRuntimeReservation, bool]:
        when = _utc(as_of or datetime.now(UTC))
        ttl = _validated_ttl(ttl)
        identity = _identity(tenant_id, run_id, tool_call_id, tool_type, tool_name)
        quantity = _positive_decimal(reserved_quantity, "预占数量")
        fingerprint = _fingerprint(
            {
                **identity,
                "entitlement_id": entitlement_id,
                "quantity_basis": quantity_basis,
                "reserved_quantity": str(quantity),
                "meter_config": meter_config,
            }
        )
        existing = self._by_tool_call(identity["tenant_id"], identity["tool_call_id"])
        if existing is not None:
            self._assert_fingerprint(existing, fingerprint)
            if existing.status == "released":
                return self._reactivate_released(
                    existing,
                    fingerprint=fingerprint,
                    meter_config=meter_config,
                    identity=identity,
                    quantity_basis=quantity_basis,
                    quantity=quantity,
                    when=when,
                    ttl=ttl,
                )
            return existing, False

        subscription, entitlement, billing_period = self._locked_contract(
            identity["tenant_id"],
            _required(entitlement_id, "权益编号"),
            when,
        )
        self._assert_meter_snapshot(
            entitlement,
            meter_config,
            identity["tool_type"],
            identity["tool_name"],
            quantity_basis,
        )
        existing = self._by_tool_call(identity["tenant_id"], identity["tool_call_id"])
        if existing is not None:
            self._assert_fingerprint(existing, fingerprint)
            return existing, False

        quota_period_key = quota_period_key_for(entitlement, subscription, when)
        usage = self._locked_usage(subscription, entitlement, quota_period_key)
        if usage.effective_limit is not None:
            resulting = usage.used_quantity + usage.held_quantity + quantity
            if resulting > usage.effective_limit:
                raise CommercialConflictError("运行时预占将超过商业硬配额，真实工具未执行。")
        row = CommercialRuntimeReservation(
            tenant_id=identity["tenant_id"],
            subscription_id=subscription.id,
            entitlement_id=entitlement.id,
            billing_period_id=billing_period.id,
            run_id=identity["run_id"],
            tool_call_id=identity["tool_call_id"],
            tool_type=identity["tool_type"],
            tool_name=identity["tool_name"],
            quantity_basis=quantity_basis,
            reserved_quantity=quantity,
            actual_quantity=None,
            period_key=billing_period.period_key,
            quota_period_key=quota_period_key,
            status="reserved",
            request_fingerprint=fingerprint,
            meter_config_json=dict(meter_config),
            resolution_code=None,
            expires_at=when + ttl,
            settled_at=None,
            created_at=when,
            updated_at=when,
        )
        return self._flush_idempotently(row, fingerprint)

    def _reactivate_released(
        self,
        existing: CommercialRuntimeReservation,
        *,
        fingerprint: str,
        meter_config: dict[str, Any],
        identity: dict[str, str],
        quantity_basis: str,
        quantity: Decimal,
        when: datetime,
        ttl: timedelta,
    ) -> tuple[CommercialRuntimeReservation, bool]:
        row = self._locked_reservation(existing.id)
        self._assert_fingerprint(row, fingerprint)
        if row.status != "released":
            return row, False
        subscription, entitlement, billing_period = self._locked_contract(
            identity["tenant_id"],
            row.entitlement_id,
            when,
        )
        self._assert_meter_snapshot(
            entitlement,
            meter_config,
            identity["tool_type"],
            identity["tool_name"],
            quantity_basis,
        )
        quota_period_key = quota_period_key_for(entitlement, subscription, when)
        if (
            row.subscription_id != subscription.id
            or row.billing_period_id != billing_period.id
            or row.quota_period_key != quota_period_key
        ):
            raise CommercialConflictError(
                "已释放预占不属于当前商业账期，请使用新的业务操作标识重试。"
            )
        usage = self._locked_usage(subscription, entitlement, quota_period_key)
        if (
            usage.effective_limit is not None
            and usage.used_quantity + usage.held_quantity + quantity
            > usage.effective_limit
        ):
            raise CommercialConflictError("运行时预占将超过商业硬配额，真实工具未执行。")
        row.status = "reserved"
        row.actual_quantity = None
        row.resolution_code = None
        row.expires_at = when + ttl
        row.settled_at = None
        row.updated_at = when
        self.db.flush()
        return row, False

    def record_reconciliation_backlog(
        self,
        *,
        tenant_id: str,
        entitlement_id: str,
        run_id: str,
        tool_call_id: str,
        tool_type: str,
        tool_name: str,
        quantity_basis: str,
        reserved_quantity: Decimal,
        actual_quantity: Decimal | None,
        meter_config: dict[str, Any],
        resolution_code: str,
        as_of: datetime | None = None,
    ) -> tuple[CommercialRuntimeReservation, bool]:
        when = _utc(as_of or datetime.now(UTC))
        identity = _identity(tenant_id, run_id, tool_call_id, tool_type, tool_name)
        reserved = _positive_decimal(reserved_quantity, "补偿占位数量")
        actual = (
            _positive_decimal(actual_quantity, "真实调用数量")
            if actual_quantity is not None
            else None
        )
        fingerprint = _fingerprint(
            {
                **identity,
                "entitlement_id": entitlement_id,
                "quantity_basis": quantity_basis,
                "reserved_quantity": str(reserved),
                "actual_quantity": str(actual) if actual is not None else None,
                "resolution_code": resolution_code,
                "meter_config": meter_config,
            }
        )
        existing = self._by_tool_call(identity["tenant_id"], identity["tool_call_id"])
        if existing is not None:
            self._assert_fingerprint(existing, fingerprint)
            return existing, False
        subscription, entitlement, billing_period = self._locked_contract(
            identity["tenant_id"],
            _required(entitlement_id, "权益编号"),
            when,
            require_consumable=False,
        )
        self._assert_meter_snapshot(
            entitlement,
            meter_config,
            identity["tool_type"],
            identity["tool_name"],
            quantity_basis,
            require_active=False,
        )
        row = CommercialRuntimeReservation(
            tenant_id=identity["tenant_id"],
            subscription_id=subscription.id,
            entitlement_id=entitlement.id,
            billing_period_id=billing_period.id,
            run_id=identity["run_id"],
            tool_call_id=identity["tool_call_id"],
            tool_type=identity["tool_type"],
            tool_name=identity["tool_name"],
            quantity_basis=quantity_basis,
            reserved_quantity=reserved,
            actual_quantity=actual,
            period_key=billing_period.period_key,
            quota_period_key=quota_period_key_for(entitlement, subscription, when),
            status="reconciliation_required",
            request_fingerprint=fingerprint,
            meter_config_json=dict(meter_config),
            resolution_code=_required(resolution_code, "补偿原因")[:64],
            expires_at=when + DEFAULT_RESERVATION_TTL,
            settled_at=None,
            created_at=when,
            updated_at=when,
        )
        return self._flush_idempotently(row, fingerprint)

    def by_tool_call(self, tool_call_id: str) -> CommercialRuntimeReservation | None:
        return self.db.scalar(
            select(CommercialRuntimeReservation).where(
                CommercialRuntimeReservation.tool_call_id == _required(tool_call_id, "工具调用编号")
            )
        )

    def get_for_update(self, reservation_id: str) -> CommercialRuntimeReservation:
        return self._locked_reservation(reservation_id)

    def mark_committed(
        self,
        reservation_id: str,
        actual_quantity: Decimal,
        *,
        settled_at: datetime | None = None,
    ) -> CommercialRuntimeReservation:
        row = self._locked_reservation(reservation_id)
        quantity = _positive_decimal(actual_quantity, "真实调用数量")
        if row.status == "committed":
            if Decimal(row.actual_quantity or 0) != quantity:
                raise CommercialConflictError("预占已按不同真实数量完成结算。")
            return row
        if row.status != "reserved":
            raise CommercialConflictError(f"预占状态 {row.status} 不允许自动结算。")
        if quantity > Decimal(row.reserved_quantity):
            raise CommercialConflictError("真实调用数量超过执行前可信预占，拒绝伪造结算。")
        now = _utc(settled_at or datetime.now(UTC))
        row.status = "committed"
        row.actual_quantity = quantity
        row.resolution_code = None
        row.settled_at = now
        row.updated_at = now
        self.db.flush()
        return row

    def require_committed_reconciliation(
        self,
        reservation_id: str,
        *,
        reason_code: str,
    ) -> CommercialRuntimeReservation:
        row = self._locked_reservation(reservation_id)
        if row.status == "committed_reconciliation_required":
            return row
        if row.status != "committed" or row.actual_quantity is None:
            raise CommercialConflictError("只有已写入用量的预占可进入提交后补偿。")
        row.status = "committed_reconciliation_required"
        row.resolution_code = _required(reason_code, "提交后补偿原因")[:64]
        row.updated_at = datetime.now(UTC)
        self.db.flush()
        return row

    def require_reconciliation(
        self,
        reservation_id: str,
        *,
        reason_code: str,
        actual_quantity: Decimal | None = None,
    ) -> CommercialRuntimeReservation:
        """真实调用已发生但尚不能安全结算时冻结预占并进入补偿队列。"""

        row = self._locked_reservation(reservation_id)
        actual = (
            _positive_decimal(actual_quantity, "真实调用数量")
            if actual_quantity is not None
            else None
        )
        if row.status == "reconciliation_required":
            persisted = (
                Decimal(row.actual_quantity) if row.actual_quantity is not None else None
            )
            if persisted != actual:
                raise CommercialConflictError("补偿预占已绑定不同真实调用数量。")
            return row
        if row.status != "reserved":
            raise CommercialConflictError(
                f"预占状态 {row.status} 不允许进入执行后补偿。"
            )
        row.status = "reconciliation_required"
        row.actual_quantity = actual
        row.resolution_code = _required(reason_code, "补偿原因")[:64]
        row.settled_at = None
        row.updated_at = datetime.now(UTC)
        self.db.flush()
        return row

    def mark_reconciled(self, reservation_id: str) -> CommercialRuntimeReservation:
        row = self._locked_reservation(reservation_id)
        if row.status == "committed":
            return row
        if row.status != "committed_reconciliation_required":
            raise CommercialConflictError("当前预占不在提交后补偿状态。")
        row.status = "committed"
        row.resolution_code = None
        row.updated_at = datetime.now(UTC)
        self.db.flush()
        return row

    def release(
        self,
        reservation_id: str,
        *,
        reason_code: str,
        expired: bool = False,
        settled_at: datetime | None = None,
    ) -> CommercialRuntimeReservation:
        row = self._locked_reservation(reservation_id)
        target_status = "expired" if expired else "released"
        if row.status == target_status:
            return row
        if row.status != "reserved":
            raise CommercialConflictError(f"预占状态 {row.status} 不允许释放。")
        now = _utc(settled_at or datetime.now(UTC))
        row.status = target_status
        row.actual_quantity = None
        row.resolution_code = _required(reason_code, "释放原因")[:64]
        row.settled_at = now
        row.updated_at = now
        self.db.flush()
        return row

    def expired_candidates(
        self,
        *,
        as_of: datetime | None = None,
        limit: int = 100,
    ) -> list[CommercialRuntimeReservation]:
        if not isinstance(limit, int) or isinstance(limit, bool) or not 1 <= limit <= 500:
            raise ValueError("预占补偿批次大小必须介于 1 和 500 之间。")
        when = _utc(as_of or datetime.now(UTC))
        return list(
            self.db.scalars(
                select(CommercialRuntimeReservation)
                .where(
                    CommercialRuntimeReservation.status == "reserved",
                    CommercialRuntimeReservation.expires_at <= when,
                )
                .order_by(
                    CommercialRuntimeReservation.expires_at,
                    CommercialRuntimeReservation.id,
                )
                .limit(limit)
            ).all()
        )

    def reconciliation_candidates(
        self,
        *,
        limit: int = 100,
    ) -> list[CommercialRuntimeReservation]:
        if not isinstance(limit, int) or isinstance(limit, bool) or not 1 <= limit <= 500:
            raise ValueError("运行时补偿队列批次大小必须介于 1 和 500 之间。")
        return list(
            self.db.scalars(
                select(CommercialRuntimeReservation)
                .where(
                    CommercialRuntimeReservation.status.in_(
                        (
                            "reconciliation_required",
                            "committed_reconciliation_required",
                        )
                    )
                )
                .order_by(
                    CommercialRuntimeReservation.created_at,
                    CommercialRuntimeReservation.id,
                )
                .limit(limit)
            ).all()
        )

    def _locked_contract(
        self,
        tenant_id: str,
        entitlement_id: str,
        when: datetime,
        *,
        require_consumable: bool = True,
    ) -> tuple[TenantSubscription, CommercialEntitlement, CommercialBillingPeriod]:
        hint = self.db.scalar(
            select(CommercialEntitlement).where(
                CommercialEntitlement.tenant_id == tenant_id,
                CommercialEntitlement.id == entitlement_id,
            )
        )
        if hint is None:
            raise LookupError("商业运行权益不存在。")
        subscription = self.db.scalar(
            select(TenantSubscription)
            .where(
                TenantSubscription.tenant_id == tenant_id,
                TenantSubscription.id == hint.subscription_id,
            )
            .with_for_update()
            .execution_options(populate_existing=True)
        )
        entitlement = self.db.scalar(
            select(CommercialEntitlement)
            .where(
                CommercialEntitlement.tenant_id == tenant_id,
                CommercialEntitlement.subscription_id == hint.subscription_id,
                CommercialEntitlement.id == entitlement_id,
            )
            .with_for_update()
            .execution_options(populate_existing=True)
        )
        if subscription is None or entitlement is None:
            raise LookupError("商业运行订阅或权益不存在。")
        billing_period = CommercialBillingPeriodService(self.db).resolve(
            tenant_id,
            subscription.id,
            when,
        )
        if require_consumable:
            _assert_consumable(subscription, entitlement, billing_period, when)
        return subscription, entitlement, billing_period

    def _locked_usage(
        self,
        subscription: TenantSubscription,
        entitlement: CommercialEntitlement,
        quota_period_key: str,
    ) -> RuntimeReservationUsage:
        used = Decimal(
            self.db.scalar(
                select(func.coalesce(func.sum(UsageMeterEvent.quantity), 0)).where(
                    UsageMeterEvent.tenant_id == subscription.tenant_id,
                    UsageMeterEvent.subscription_id == subscription.id,
                    UsageMeterEvent.entitlement_id == entitlement.id,
                    UsageMeterEvent.quota_period_key == quota_period_key,
                )
            )
            or 0
        )
        held = active_reserved_quantity(
            self.db,
            tenant_id=subscription.tenant_id,
            subscription_id=subscription.id,
            entitlement_id=entitlement.id,
            period_key=quota_period_key,
        )
        return RuntimeReservationUsage(
            used_quantity=max(Decimal("0"), used),
            held_quantity=max(Decimal("0"), held),
            effective_limit=_effective_limit(entitlement),
        )

    @staticmethod
    def _assert_meter_snapshot(
        entitlement: CommercialEntitlement,
        meter_config: dict[str, Any],
        tool_type: str,
        tool_name: str,
        quantity_basis: str,
        *,
        require_active: bool = True,
    ) -> None:
        current_config = (
            entitlement.config_json if isinstance(entitlement.config_json, dict) else {}
        )
        if current_config.get("runtime_meter") != meter_config:
            raise CommercialConflictError("运行时计量配置在额度预占前发生变化，请重试。")
        if not runtime_meter_matches(meter_config, tool_type, tool_name):
            raise CommercialConfigurationError("运行时计量器与工具维度不匹配。")
        if str(meter_config.get("quantity_basis") or "").strip() != quantity_basis:
            raise CommercialConfigurationError("运行时计量基准与权益配置不一致。")
        if require_active and entitlement.status != "active":
            raise CommercialConflictError("商业运行权益当前不可用。")

    def _by_tool_call(
        self,
        tenant_id: str,
        tool_call_id: str,
    ) -> CommercialRuntimeReservation | None:
        return self.db.scalar(
            select(CommercialRuntimeReservation).where(
                CommercialRuntimeReservation.tenant_id == tenant_id,
                CommercialRuntimeReservation.tool_call_id == tool_call_id,
            )
        )

    def _locked_reservation(self, reservation_id: str) -> CommercialRuntimeReservation:
        row = self.db.scalar(
            select(CommercialRuntimeReservation)
            .where(CommercialRuntimeReservation.id == _required(reservation_id, "预占编号"))
            .with_for_update()
        )
        if row is None:
            raise LookupError("商业运行预占不存在。")
        return row

    def _flush_idempotently(
        self,
        row: CommercialRuntimeReservation,
        fingerprint: str,
    ) -> tuple[CommercialRuntimeReservation, bool]:
        try:
            with self.db.begin_nested():
                self.db.add(row)
                self.db.flush()
            return row, True
        except IntegrityError:
            existing = self._by_tool_call(row.tenant_id, row.tool_call_id)
            if existing is None:
                raise
            self._assert_fingerprint(existing, fingerprint)
            return existing, False

    @staticmethod
    def _assert_fingerprint(row: CommercialRuntimeReservation, fingerprint: str) -> None:
        if row.request_fingerprint != fingerprint:
            raise CommercialConflictError("工具调用编号已被不同商业预占请求占用。")


def active_reserved_quantity(
    db: Session,
    *,
    tenant_id: str,
    subscription_id: str,
    entitlement_id: str,
    period_key: str,
) -> Decimal:
    return Decimal(
        db.scalar(
            select(
                func.coalesce(
                    func.sum(CommercialRuntimeReservation.reserved_quantity),
                    0,
                )
            ).where(
                CommercialRuntimeReservation.tenant_id == tenant_id,
                CommercialRuntimeReservation.subscription_id == subscription_id,
                CommercialRuntimeReservation.entitlement_id == entitlement_id,
                CommercialRuntimeReservation.quota_period_key == period_key,
                CommercialRuntimeReservation.status.in_(HOLDING_RESERVATION_STATUSES),
            )
        )
        or 0
    )


def _assert_consumable(
    subscription: TenantSubscription,
    entitlement: CommercialEntitlement,
    billing_period: CommercialBillingPeriod,
    when: datetime,
) -> None:
    if subscription.status not in {"trialing", "active"}:
        raise CommercialConflictError("订阅当前不可预占商业用量。")
    start = _utc(subscription.starts_at)
    end = _utc(subscription.ends_at) if subscription.ends_at is not None else None
    period_start = _utc(billing_period.period_start)
    period_end = _utc(billing_period.period_end)
    if when < start or (end is not None and when >= end) or not period_start <= when < period_end:
        raise CommercialConflictError("预占时间不在当前订阅计费周期内。")
    effective_from = _utc(entitlement.effective_from)
    effective_to = _utc(entitlement.effective_to) if entitlement.effective_to else None
    if entitlement.status != "active" or when < effective_from:
        raise CommercialConflictError("商业运行权益当前不可预占。")
    if effective_to is not None and when >= effective_to:
        raise CommercialConflictError("商业运行权益已经过期。")


def _effective_limit(entitlement: CommercialEntitlement) -> Decimal | None:
    if entitlement.entitlement_type == "unlimited":
        return None
    if entitlement.hard_limit_quantity is not None:
        return Decimal(entitlement.hard_limit_quantity)
    if entitlement.overage_policy == "block" and entitlement.included_quantity is not None:
        return Decimal(entitlement.included_quantity)
    return None


def _identity(
    tenant_id: str,
    run_id: str,
    tool_call_id: str,
    tool_type: str,
    tool_name: str,
) -> dict[str, str]:
    return {
        "tenant_id": _required(tenant_id, "租户编号"),
        "run_id": _required(run_id, "运行编号"),
        "tool_call_id": _required(tool_call_id, "工具调用编号"),
        "tool_type": _required(tool_type, "工具类型"),
        "tool_name": _required(tool_name, "工具名称"),
    }


def _fingerprint(payload: dict[str, Any]) -> str:
    canonical = json.dumps(payload, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
    return hashlib.sha256(canonical.encode("utf-8")).hexdigest()


def _validated_ttl(value: timedelta) -> timedelta:
    if not isinstance(value, timedelta) or value <= timedelta(0) or value > MAX_RESERVATION_TTL:
        raise CommercialConfigurationError("运行时预占有效期必须大于 0 且不超过 1 小时。")
    return value


def _positive_decimal(value: Any, label: str) -> Decimal:
    try:
        result = Decimal(str(value))
    except Exception as error:
        raise CommercialConfigurationError(f"{label}必须是正数。") from error
    if not result.is_finite() or result <= 0:
        raise CommercialConfigurationError(f"{label}必须是正数。")
    return result


def _required(value: Any, label: str) -> str:
    normalized = str(value or "").strip()
    if not normalized:
        raise CommercialConfigurationError(f"{label}不能为空。")
    return normalized


def _utc(value: datetime) -> datetime:
    return value.replace(tzinfo=UTC) if value.tzinfo is None else value.astimezone(UTC)
