from __future__ import annotations

import uuid
from datetime import datetime
from decimal import Decimal
from typing import Any

from sqlalchemy import (
    CheckConstraint,
    DateTime,
    ForeignKeyConstraint,
    Index,
    Numeric,
    String,
    UniqueConstraint,
    func,
)
from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy.types import JSON

from app.db.base_class import Base


class CommercialRuntimeReservation(Base):
    """工具执行前的额度占位；它是可结算状态，不是客户用量事实。"""

    __tablename__ = "commercial_runtime_reservations"
    __table_args__ = (
        UniqueConstraint(
            "tenant_id",
            "id",
            name="uq_commercial_runtime_reservations_tenant_id",
        ),
        UniqueConstraint(
            "tool_call_id",
            name="uq_commercial_runtime_reservations_tool_call",
        ),
        ForeignKeyConstraint(
            ["tenant_id", "subscription_id"],
            ["tenant_subscriptions.tenant_id", "tenant_subscriptions.id"],
            name="fk_commercial_runtime_reservations_tenant_subscription",
            ondelete="RESTRICT",
        ),
        ForeignKeyConstraint(
            ["tenant_id", "subscription_id", "entitlement_id"],
            [
                "commercial_entitlements.tenant_id",
                "commercial_entitlements.subscription_id",
                "commercial_entitlements.id",
            ],
            name="fk_commercial_runtime_reservations_tenant_entitlement",
            ondelete="RESTRICT",
        ),
        ForeignKeyConstraint(
            ["tenant_id", "subscription_id", "billing_period_id"],
            [
                "commercial_billing_periods.tenant_id",
                "commercial_billing_periods.subscription_id",
                "commercial_billing_periods.id",
            ],
            name="fk_commercial_runtime_reservations_tenant_billing_period",
            ondelete="RESTRICT",
        ),
        CheckConstraint(
            "status IN ('reserved', 'committed', 'released', 'expired', "
            "'reconciliation_required', 'committed_reconciliation_required')",
            name="ck_commercial_runtime_reservations_status",
        ),
        CheckConstraint(
            "quantity_basis IN ('call', 'input_tokens', 'output_tokens', "
            "'total_tokens', 'duration_ms', 'bytes', 'pages', 'objects', 'events')",
            name="ck_commercial_runtime_reservations_basis",
        ),
        CheckConstraint(
            "reserved_quantity > 0 AND (actual_quantity IS NULL OR actual_quantity > 0)",
            name="ck_commercial_runtime_reservations_quantity",
        ),
        CheckConstraint(
            "expires_at > created_at",
            name="ck_commercial_runtime_reservations_expiry",
        ),
        CheckConstraint(
            "(status = 'reserved' AND actual_quantity IS NULL AND settled_at IS NULL "
            "AND resolution_code IS NULL) OR "
            "(status = 'committed' AND actual_quantity IS NOT NULL "
            "AND actual_quantity <= reserved_quantity AND settled_at IS NOT NULL "
            "AND resolution_code IS NULL) OR "
            "(status IN ('released', 'expired') AND actual_quantity IS NULL "
            "AND settled_at IS NOT NULL AND resolution_code IS NOT NULL) OR "
            "(status = 'reconciliation_required' AND settled_at IS NULL "
            "AND resolution_code IS NOT NULL) OR "
            "(status = 'committed_reconciliation_required' "
            "AND actual_quantity IS NOT NULL "
            "AND actual_quantity <= reserved_quantity "
            "AND settled_at IS NOT NULL AND resolution_code IS NOT NULL)",
            name="ck_commercial_runtime_reservations_state",
        ),
        CheckConstraint(
            "length(trim(run_id)) > 0 AND length(trim(tool_call_id)) > 0 "
            "AND length(trim(tool_type)) > 0 AND length(trim(tool_name)) > 0 "
            "AND length(trim(period_key)) > 0 "
            "AND length(trim(quota_period_key)) > 0 "
            "AND length(trim(request_fingerprint)) > 0",
            name="ck_commercial_runtime_reservations_keys",
        ),
        Index(
            "ix_commercial_runtime_reservations_quota",
            "tenant_id",
            "subscription_id",
            "entitlement_id",
            "quota_period_key",
            "status",
        ),
        Index(
            "ix_commercial_runtime_reservations_billing_period",
            "tenant_id",
            "billing_period_id",
            "status",
        ),
        Index(
            "ix_commercial_runtime_reservations_expiry",
            "status",
            "expires_at",
        ),
        Index(
            "ix_commercial_runtime_reservations_run",
            "tenant_id",
            "run_id",
            "created_at",
        ),
    )

    id: Mapped[str] = mapped_column(
        String(36),
        primary_key=True,
        default=lambda: str(uuid.uuid4()),
    )
    tenant_id: Mapped[str] = mapped_column(String(64), nullable=False)
    subscription_id: Mapped[str] = mapped_column(String(36), nullable=False)
    entitlement_id: Mapped[str] = mapped_column(String(36), nullable=False)
    billing_period_id: Mapped[str] = mapped_column(String(36), nullable=False)
    run_id: Mapped[str] = mapped_column(String(50), nullable=False)
    tool_call_id: Mapped[str] = mapped_column(String(36), nullable=False)
    tool_type: Mapped[str] = mapped_column(String(30), nullable=False)
    tool_name: Mapped[str] = mapped_column(String(100), nullable=False)
    quantity_basis: Mapped[str] = mapped_column(String(20), nullable=False)
    reserved_quantity: Mapped[Decimal] = mapped_column(Numeric(20, 6), nullable=False)
    actual_quantity: Mapped[Decimal | None] = mapped_column(Numeric(20, 6))
    period_key: Mapped[str] = mapped_column(String(64), nullable=False)
    quota_period_key: Mapped[str] = mapped_column(String(64), nullable=False)
    status: Mapped[str] = mapped_column(String(32), nullable=False)
    request_fingerprint: Mapped[str] = mapped_column(String(64), nullable=False)
    meter_config_json: Mapped[dict[str, Any]] = mapped_column(JSON, nullable=False, default=dict)
    resolution_code: Mapped[str | None] = mapped_column(String(64))
    expires_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
    settled_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True),
        nullable=False,
        server_default=func.now(),
    )
    updated_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True),
        nullable=False,
        server_default=func.now(),
        onupdate=func.now(),
    )
