"""商业配置与自动续期的脱敏追加式审计。"""

from __future__ import annotations

import uuid
from datetime import date, datetime
from decimal import Decimal
from typing import Any, Literal

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

from app.models.commercial import (
    CommercialEntitlement,
    TenantCommercialPlan,
    TenantSubscription,
)
from app.models.commercial_billing import CommercialAdminEvent, CommercialBillingPeriod
from app.services.commercial_access_policy import (
    CommercialConfigurationError,
    CommercialConflictError,
)

AuditResource = (
    TenantCommercialPlan | TenantSubscription | CommercialEntitlement | CommercialBillingPeriod
)
ActorType = Literal["user", "system", "migration"]

_RESOURCE_FIELDS: dict[type[Any], tuple[str, ...]] = {
    TenantCommercialPlan: (
        "id",
        "tenant_id",
        "plan_code",
        "name",
        "pricing_model",
        "billing_interval",
        "currency",
        "base_fee",
        "included_seats",
        "overage_enabled",
        "status",
        "effective_from",
        "effective_to",
        "version",
    ),
    TenantSubscription: (
        "id",
        "tenant_id",
        "subscription_key",
        "plan_id",
        "status",
        "starts_at",
        "ends_at",
        "current_period_start",
        "current_period_end",
        "seats",
        "base_fee_snapshot",
        "currency",
        "billing_interval",
        "auto_renew",
        "canceled_at",
        "version",
    ),
    CommercialEntitlement: (
        "id",
        "tenant_id",
        "subscription_id",
        "entitlement_key",
        "metric_key",
        "entitlement_type",
        "unit",
        "included_quantity",
        "hard_limit_quantity",
        "reset_interval",
        "overage_policy",
        "status",
        "effective_from",
        "effective_to",
        "version",
    ),
    CommercialBillingPeriod: (
        "id",
        "tenant_id",
        "subscription_id",
        "plan_id",
        "period_sequence",
        "period_key",
        "status",
        "period_start",
        "period_end",
        "subscription_status_snapshot",
        "plan_code_snapshot",
        "plan_version_snapshot",
        "pricing_model_snapshot",
        "billing_interval",
        "currency",
        "base_fee_snapshot",
        "seats_snapshot",
        "source",
    ),
}
_RESOURCE_TYPES = {
    TenantCommercialPlan: "plan",
    TenantSubscription: "subscription",
    CommercialEntitlement: "entitlement",
    CommercialBillingPeriod: "billing_period",
}
_BANNED_KEY_PARTS = (
    "secret",
    "token",
    "password",
    "signature",
    "authorization",
    "contract_terms",
    "metadata_json",
    "config_json",
    "external_subscription",
)


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

    def record(
        self,
        *,
        tenant_id: str,
        actor_type: ActorType,
        actor_id: str,
        request_id: str,
        reason: str,
        action: str,
        resource: AuditResource,
        before: dict[str, Any] | None,
        after: dict[str, Any] | None,
    ) -> tuple[CommercialAdminEvent, bool]:
        resource_type = resource_type_for(resource)
        resource_id = _required(resource.id, "审计资源编号")
        version = resource_version_for(resource)
        normalized_request = _required(request_id, "X-Request-Id")[:120]
        normalized_before = _validate_snapshot(before or {})
        normalized_after = _validate_snapshot(after or {})
        existing = self.db.scalar(
            select(CommercialAdminEvent).where(
                CommercialAdminEvent.tenant_id == tenant_id,
                CommercialAdminEvent.request_id == normalized_request,
                CommercialAdminEvent.action == action,
                CommercialAdminEvent.resource_type == resource_type,
                CommercialAdminEvent.resource_id == resource_id,
            )
        )
        if existing is not None:
            self._assert_same(existing, normalized_before, normalized_after)
            return existing, False
        row = CommercialAdminEvent(
            tenant_id=_required(tenant_id, "审计租户编号")[:64],
            actor_type=actor_type,
            actor_id=_required(actor_id, "审计操作人")[:120],
            request_id=normalized_request,
            reason=_required(reason, "审计原因")[:500],
            action=action,
            resource_type=resource_type,
            resource_id=resource_id,
            resource_version=version,
            before_json=normalized_before,
            after_json=normalized_after,
        )
        try:
            with self.db.begin_nested():
                self.db.add(row)
                self.db.flush()
            return row, True
        except IntegrityError:
            existing = self.db.scalar(
                select(CommercialAdminEvent).where(
                    CommercialAdminEvent.tenant_id == tenant_id,
                    CommercialAdminEvent.request_id == normalized_request,
                    CommercialAdminEvent.action == action,
                    CommercialAdminEvent.resource_type == resource_type,
                    CommercialAdminEvent.resource_id == resource_id,
                )
            )
            if existing is None:
                raise
            self._assert_same(existing, normalized_before, normalized_after)
            return existing, False

    @staticmethod
    def _assert_same(
        row: CommercialAdminEvent,
        before: dict[str, Any],
        after: dict[str, Any],
    ) -> None:
        if row.before_json != before or row.after_json != after:
            raise CommercialConflictError("X-Request-Id 已被不同商业审计动作占用。")


def snapshot_resource(resource: AuditResource) -> dict[str, Any]:
    fields = _RESOURCE_FIELDS.get(type(resource))
    if fields is None:
        raise CommercialConfigurationError("不支持的商业审计资源类型。")
    return {name: _json_value(getattr(resource, name)) for name in fields}


def resource_type_for(resource: AuditResource) -> str:
    resource_type = _RESOURCE_TYPES.get(type(resource))
    if resource_type is None:
        raise CommercialConfigurationError("不支持的商业审计资源类型。")
    return resource_type


def resource_version_for(resource: AuditResource) -> int:
    if isinstance(resource, CommercialBillingPeriod):
        return int(resource.period_sequence)
    return int(resource.version)


def service_request_id(prefix: str) -> str:
    normalized = "-".join(str(prefix or "commercial").strip().split())[:48]
    return f"service:{normalized}:{uuid.uuid4().hex}"


def _validate_snapshot(value: dict[str, Any]) -> dict[str, Any]:
    for key, item in value.items():
        normalized = str(key).casefold()
        if any(part in normalized for part in _BANNED_KEY_PARTS):
            raise CommercialConfigurationError("商业审计快照包含禁止记录的敏感字段。")
        if isinstance(item, dict):
            _validate_snapshot(item)
    return dict(value)


def _json_value(value: Any) -> Any:
    if isinstance(value, Decimal):
        return str(value)
    if isinstance(value, (datetime, date)):
        return value.isoformat()
    if isinstance(value, (str, int, float, bool)) or value is None:
        return value
    raise CommercialConfigurationError("商业审计快照包含不可序列化字段。")


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