"""商品价格规则（Pydantic schema）。

报价合同字段：channel / member_level / variant / quantity / time window。
`starts_at` / `ends_at` 入参带时区偏移，校验时统一归一为 UTC 存库与比较。
"""
from __future__ import annotations

from datetime import datetime, timezone
from decimal import Decimal
from typing import Literal, Optional

from pydantic import BaseModel, Field, field_validator, model_validator

ChannelScope = Literal["store", "pos", "both"]
PriceType = Literal["fixed", "amount_off", "percent_off"]


class ProductPriceRuleIn(BaseModel):
    """单条规则的入参。"""
    id: Optional[int] = None  # 编辑时携带，更新而非新增
    variant_id: Optional[int] = None
    member_level_id: Optional[int] = None  # None = 默认客户（walk-in）
    channel_scope: ChannelScope = "both"
    min_quantity: int = Field(default=1, ge=1)
    price_type: PriceType = "fixed"
    # price_value 单位：
    #   fixed/amount_off —— 元，保留两位小数（schema 层 Decimal 校验）
    #   percent_off     —— 百分比小数（0~100），保留两位小数
    price_value: Decimal = Field(default=Decimal("0"))
    priority: int = Field(default=100, ge=0)
    is_promotion: bool = False
    starts_at: Optional[datetime] = None  # 带 tz 偏移即可，校验归一为 UTC
    ends_at: Optional[datetime] = None
    is_active: bool = True

    @field_validator("starts_at", "ends_at", mode="before")
    @classmethod
    def _parse_iso(cls, value):
        """接受 ISO 字符串或 datetime；带 tz 偏移则归一为 UTC 朴素 datetime。"""
        if value is None or value == "":
            return None
        if isinstance(value, str):
            v = datetime.fromisoformat(value.replace("Z", "+00:00"))
        else:
            v = value
        if v.tzinfo is not None:
            v = v.astimezone(timezone.utc).replace(tzinfo=None)
        return v

    @model_validator(mode="after")
    def _validate(self):
        if self.price_value < 0:
            raise ValueError("price_value 必须 >= 0")
        if self.price_type == "percent_off" and self.price_value > Decimal("100"):
            raise ValueError("percent_off 不能超过 100")
        if self.starts_at and self.ends_at and self.ends_at <= self.starts_at:
            raise ValueError("ends_at 必须晚于 starts_at")
        return self


class ProductPriceRulesReplace(BaseModel):
    """原子替换单个商品的整组规则。"""
    price_rules_version: int = Field(default=0, ge=0, description="客户端持有的版本号，乐观锁")
    rules: list[ProductPriceRuleIn] = Field(default_factory=list)


class ProductPriceRuleOut(ProductPriceRuleIn):
    id: int
    tenant_id: int
    product_id: int
    created_at: datetime
    updated_at: datetime

    model_config = {"from_attributes": True}


class ProductPriceRulesResponse(BaseModel):
    """GET /api/products/{product_id}/price-rules 的响应。"""
    product_id: int
    price_rules_version: int
    rules: list[ProductPriceRuleOut] = Field(default_factory=list)


def normalize_rule_windows(rule: ProductPriceRuleIn) -> tuple[Optional[datetime], Optional[datetime]]:
    """把带 tz 的 datetime 转换为 UTC 朴素 datetime（持久化前再调一次以兜底）。"""
    def _conv(v: Optional[datetime]) -> Optional[datetime]:
        if v is None:
            return None
        if v.tzinfo is not None:
            return v.astimezone(timezone.utc).replace(tzinfo=None)
        return v

    return _conv(rule.starts_at), _conv(rule.ends_at)


def rule_time_window_valid(rule: ProductPriceRuleIn) -> Optional[str]:
    """返回 None 表示合法；否则返回错误描述。"""
    starts, ends = normalize_rule_windows(rule)
    if starts and ends and ends <= starts:
        return "ends_at 必须晚于 starts_at"
    return None


def channel_scope_allowed(scope: str) -> bool:
    return scope in {"store", "pos", "both"}


def price_type_allowed(price_type: str) -> bool:
    return price_type in {"fixed", "amount_off", "percent_off"}