from __future__ import annotations

from dataclasses import dataclass, field
from decimal import Decimal
from typing import Literal

from pydantic import BaseModel, Field

from app.services.expense_rule_runtime_defaults import (
    DEFAULT_SCENE_MATRIX_CONFIG,
    DEFAULT_SCENE_RULE_ASSET_CODE,
    DEFAULT_TRAVEL_POLICY_CONFIG,
    DEFAULT_TRAVEL_RULE_ASSET_CODE,
    DOCUMENT_TYPE_LABELS,
)

class AmountLimitConfig(BaseModel):
    scope: Literal["claim_total", "item_amount"] = "claim_total"
    warn_amount: Decimal | None = None
    block_amount: Decimal | None = None
    exception_keywords: list[str] = Field(default_factory=list)
    metric_label: str = "金额"


class ScenePolicyConfig(BaseModel):
    label: str
    location_required: bool = False
    min_attachment_count: int = 1
    allowed_scene_codes: list[str] = Field(default_factory=list)
    allowed_document_types: list[str] = Field(default_factory=list)
    attachment_mismatch_severity: Literal["low", "medium", "high"] = "high"
    claim_amount_limit: AmountLimitConfig | None = None
    item_amount_limit: AmountLimitConfig | None = None
    always_warn: bool = False
    always_warn_message: str = ""


class SceneMatrixRuleConfig(BaseModel):
    kind: Literal["scene_matrix"]
    version: int = 1
    scenes: dict[str, ScenePolicyConfig]


class TravelClassConfig(BaseModel):
    keyword: str
    level: int


class TravelTransportEstimateConfig(BaseModel):
    origin_city: str = ""
    destination_city: str = ""
    location_band: str = ""
    transport_mode: str
    one_way_amount: Decimal = Decimal("0")
    round_trip_amount: Decimal = Decimal("0")
    confidence: str = "basic_rule"
    basis: str = ""


class TravelPolicyConfig(BaseModel):
    kind: Literal["travel_policy"]
    version: int = 1
    relevant_expense_types: list[str] = Field(default_factory=list)
    long_distance_document_types: list[str] = Field(default_factory=list)
    route_exception_keywords: list[str] = Field(default_factory=list)
    standard_exception_keywords: list[str] = Field(default_factory=list)
    band_labels: dict[str, str] = Field(default_factory=dict)
    city_tiers: dict[str, str] = Field(default_factory=dict)
    hotel_limits: dict[str, dict[str, Decimal]] = Field(default_factory=dict)
    hotel_city_limits: dict[str, dict[str, Decimal]] = Field(default_factory=dict)
    hotel_peak_periods: dict[str, str] = Field(default_factory=dict)
    hotel_peak_city_limits: dict[str, Decimal] = Field(default_factory=dict)
    allowance_limits: dict[str, dict[str, Decimal]] = Field(default_factory=dict)
    standard_rule_code: str = ""
    standard_rule_name: str = ""
    standard_rule_version: str = ""
    transport_limits: dict[str, dict[str, int]] = Field(default_factory=dict)
    transport_estimates: list[TravelTransportEstimateConfig] = Field(default_factory=list)
    transport_estimate_rule_code: str = ""
    transport_estimate_rule_name: str = ""
    transport_estimate_rule_version: str = ""
    flight_classes: list[TravelClassConfig] = Field(default_factory=list)
    train_classes: list[TravelClassConfig] = Field(default_factory=list)


class ExpenseScenePolicy(ScenePolicyConfig):
    expense_type: str
    rule_code: str
    rule_name: str
    rule_version: str


class RuntimeTravelPolicy(TravelPolicyConfig):
    rule_code: str
    rule_name: str
    rule_version: str


@dataclass
class ExpenseRuleCatalog:
    scene_policies: dict[str, ExpenseScenePolicy] = field(default_factory=dict)
    travel_policy: RuntimeTravelPolicy | None = None

    def get_scene_policy(self, expense_type: str | None) -> ExpenseScenePolicy | None:
        normalized = str(expense_type or "").strip().lower() or "other"
        return self.scene_policies.get(normalized)


def resolve_document_type_label(document_type: str | None) -> str:
    normalized = str(document_type or "").strip().lower() or "other"
    return DOCUMENT_TYPE_LABELS.get(normalized, normalized or "其他单据")


def build_default_expense_rule_catalog() -> ExpenseRuleCatalog:
    catalog = ExpenseRuleCatalog()
    scene_matrix = SceneMatrixRuleConfig.model_validate(DEFAULT_SCENE_MATRIX_CONFIG)
    for expense_type, config in scene_matrix.scenes.items():
        catalog.scene_policies[expense_type] = ExpenseScenePolicy(
            expense_type=expense_type,
            rule_code=DEFAULT_SCENE_RULE_ASSET_CODE,
            rule_name="报销场景提交与附件标准",
            rule_version="v1.0.0",
            **config.model_dump(),
        )

    travel_policy = TravelPolicyConfig.model_validate(DEFAULT_TRAVEL_POLICY_CONFIG)
    catalog.travel_policy = RuntimeTravelPolicy(
        rule_code=DEFAULT_TRAVEL_RULE_ASSET_CODE,
        rule_name="差旅报销风险管控制度",
        rule_version="v1.1.0",
        **travel_policy.model_dump(),
    )
    return catalog
