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,
    Integer,
    Numeric,
    String,
    Text,
    UniqueConstraint,
    func,
)
from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy.types import JSON

from app.db.base_class import Base


def _new_id() -> str:
    return str(uuid.uuid4())


class CommercialBillingPeriod(Base):
    """不可变的订阅账期签发事实；时间态由窗口计算，不回写状态。"""

    __tablename__ = "commercial_billing_periods"
    __table_args__ = (
        UniqueConstraint(
            "tenant_id",
            "id",
            name="uq_commercial_billing_periods_tenant_id",
        ),
        UniqueConstraint(
            "tenant_id",
            "subscription_id",
            "id",
            name="uq_commercial_billing_periods_tenant_subscription_id",
        ),
        UniqueConstraint(
            "tenant_id",
            "subscription_id",
            "period_sequence",
            name="uq_commercial_billing_periods_subscription_sequence",
        ),
        UniqueConstraint(
            "tenant_id",
            "subscription_id",
            "period_key",
            name="uq_commercial_billing_periods_subscription_key",
        ),
        UniqueConstraint(
            "tenant_id",
            "subscription_id",
            "period_start",
            name="uq_commercial_billing_periods_subscription_start",
        ),
        UniqueConstraint(
            "tenant_id",
            "subscription_id",
            "idempotency_key",
            name="uq_commercial_billing_periods_subscription_request",
        ),
        ForeignKeyConstraint(
            ["tenant_id", "subscription_id"],
            ["tenant_subscriptions.tenant_id", "tenant_subscriptions.id"],
            name="fk_commercial_billing_periods_tenant_subscription",
            ondelete="RESTRICT",
        ),
        ForeignKeyConstraint(
            ["tenant_id", "plan_id"],
            ["tenant_commercial_plans.tenant_id", "tenant_commercial_plans.id"],
            name="fk_commercial_billing_periods_tenant_plan",
            ondelete="RESTRICT",
        ),
        CheckConstraint(
            "status = 'issued'",
            name="ck_commercial_billing_periods_status",
        ),
        CheckConstraint(
            "subscription_status_snapshot IN ('trialing', 'active', 'past_due', "
            "'suspended', 'canceled', 'expired')",
            name="ck_commercial_billing_periods_subscription_status",
        ),
        CheckConstraint(
            "pricing_model_snapshot IN ('subscription', 'usage', 'hybrid', 'pilot', 'custom')",
            name="ck_commercial_billing_periods_pricing_model",
        ),
        CheckConstraint(
            "billing_interval IN ('monthly', 'quarterly', 'annual', 'contract')",
            name="ck_commercial_billing_periods_interval",
        ),
        CheckConstraint(
            "source IN ('subscription_created', 'auto_renew', 'migration_backfill')",
            name="ck_commercial_billing_periods_source",
        ),
        CheckConstraint(
            "period_sequence >= 1 AND period_end > period_start "
            "AND plan_version_snapshot >= 1 AND base_fee_snapshot >= 0 "
            "AND seats_snapshot > 0",
            name="ck_commercial_billing_periods_values",
        ),
        CheckConstraint(
            "length(trim(period_key)) > 0 AND length(trim(plan_code_snapshot)) > 0 "
            "AND length(trim(currency)) = 3 AND length(trim(idempotency_key)) > 0 "
            "AND length(trim(created_by)) > 0",
            name="ck_commercial_billing_periods_keys",
        ),
        Index(
            "ix_commercial_billing_periods_tenant_window",
            "tenant_id",
            "period_start",
            "period_end",
        ),
        Index(
            "ix_commercial_billing_periods_subscription_window",
            "tenant_id",
            "subscription_id",
            "period_start",
            "period_end",
        ),
    )

    id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_new_id)
    tenant_id: Mapped[str] = mapped_column(String(64), nullable=False)
    subscription_id: Mapped[str] = mapped_column(String(36), nullable=False)
    plan_id: Mapped[str] = mapped_column(String(36), nullable=False)
    period_sequence: Mapped[int] = mapped_column(Integer, nullable=False)
    period_key: Mapped[str] = mapped_column(String(64), nullable=False)
    status: Mapped[str] = mapped_column(
        String(16), nullable=False, default="issued", server_default="issued"
    )
    period_start: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
    period_end: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
    subscription_status_snapshot: Mapped[str] = mapped_column(String(20), nullable=False)
    plan_code_snapshot: Mapped[str] = mapped_column(String(80), nullable=False)
    plan_version_snapshot: Mapped[int] = mapped_column(Integer, nullable=False)
    pricing_model_snapshot: Mapped[str] = mapped_column(String(24), nullable=False)
    billing_interval: Mapped[str] = mapped_column(String(20), nullable=False)
    currency: Mapped[str] = mapped_column(String(3), nullable=False)
    base_fee_snapshot: Mapped[Decimal] = mapped_column(Numeric(20, 4), nullable=False)
    seats_snapshot: Mapped[int] = mapped_column(Integer, nullable=False)
    source: Mapped[str] = mapped_column(String(32), nullable=False)
    idempotency_key: Mapped[str] = mapped_column(String(160), nullable=False)
    created_by: Mapped[str] = mapped_column(String(120), nullable=False)
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), nullable=False, server_default=func.now()
    )


class CommercialAdminEvent(Base):
    """商业配置与续期动作的脱敏追加式审计事实。"""

    __tablename__ = "commercial_admin_events"
    __table_args__ = (
        UniqueConstraint(
            "tenant_id",
            "id",
            name="uq_commercial_admin_events_tenant_id",
        ),
        UniqueConstraint(
            "tenant_id",
            "request_id",
            "action",
            "resource_type",
            "resource_id",
            name="uq_commercial_admin_events_request_resource",
        ),
        CheckConstraint(
            "actor_type IN ('user', 'system', 'migration')",
            name="ck_commercial_admin_events_actor_type",
        ),
        CheckConstraint(
            "action IN ('plan_created', 'plan_activated', 'plan_retired', "
            "'subscription_created', 'subscription_activated', "
            "'subscription_transitioned', 'entitlement_created', "
            "'entitlement_updated', 'entitlement_activated', "
            "'billing_period_created', 'subscription_rolled_over', "
            "'legacy_state_imported')",
            name="ck_commercial_admin_events_action",
        ),
        CheckConstraint(
            "resource_type IN ('plan', 'subscription', 'entitlement', 'billing_period')",
            name="ck_commercial_admin_events_resource_type",
        ),
        CheckConstraint(
            "resource_version >= 1",
            name="ck_commercial_admin_events_resource_version",
        ),
        CheckConstraint(
            "length(trim(actor_id)) > 0 AND length(trim(request_id)) > 0 "
            "AND length(trim(reason)) > 0 AND length(trim(resource_id)) > 0",
            name="ck_commercial_admin_events_required_text",
        ),
        Index(
            "ix_commercial_admin_events_tenant_time",
            "tenant_id",
            "occurred_at",
            "id",
        ),
        Index(
            "ix_commercial_admin_events_tenant_resource",
            "tenant_id",
            "resource_type",
            "resource_id",
            "occurred_at",
        ),
        Index(
            "ix_commercial_admin_events_tenant_request",
            "tenant_id",
            "request_id",
        ),
    )

    id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_new_id)
    tenant_id: Mapped[str] = mapped_column(String(64), nullable=False)
    actor_type: Mapped[str] = mapped_column(String(16), nullable=False)
    actor_id: Mapped[str] = mapped_column(String(120), nullable=False)
    request_id: Mapped[str] = mapped_column(String(120), nullable=False)
    reason: Mapped[str] = mapped_column(Text, nullable=False)
    action: Mapped[str] = mapped_column(String(48), nullable=False)
    resource_type: Mapped[str] = mapped_column(String(24), nullable=False)
    resource_id: Mapped[str] = mapped_column(String(36), nullable=False)
    resource_version: Mapped[int] = mapped_column(Integer, nullable=False)
    before_json: Mapped[dict[str, Any]] = mapped_column(JSON, nullable=False, default=dict)
    after_json: Mapped[dict[str, Any]] = mapped_column(JSON, nullable=False, default=dict)
    occurred_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), nullable=False, server_default=func.now()
    )


__all__ = ["CommercialAdminEvent", "CommercialBillingPeriod"]
