"""租户销售状态到库存动作的最小、可验证映射。"""
import json
from dataclasses import dataclass, field


_DEFAULT_SEQUENCE = ("pending", "paid", "shipped", "completed", "cancelled")


@dataclass(frozen=True)
class InventoryPolicy:
    reserve_statuses: frozenset[str]
    ship_statuses: frozenset[str]
    release_statuses: frozenset[str]
    status_sequence: tuple[str, ...] = field(default=_DEFAULT_SEQUENCE)

    def __post_init__(self) -> None:
        positions = {status: index for index, status in enumerate(self.status_sequence)}
        unknown = (self.reserve_statuses | self.ship_statuses | self.release_statuses) - positions.keys()
        if unknown:
            raise ValueError(f"库存策略包含未知状态: {', '.join(sorted(unknown))}")
        if self.reserve_statuses and self.ship_statuses:
            latest_reserve = max(positions[status] for status in self.reserve_statuses)
            earliest_ship = min(positions[status] for status in self.ship_statuses)
            if latest_reserve > earliest_ship:
                raise ValueError("预留阶段不能晚于出库阶段")
        if self.reserve_statuses & self.ship_statuses:
            raise ValueError("同一状态不能同时预留和出库")

    @classmethod
    def ecommerce(cls) -> "InventoryPolicy":
        return cls(
            reserve_statuses=frozenset({"pending", "paid"}),
            ship_statuses=frozenset({"shipped", "completed"}),
            release_statuses=frozenset({"cancelled"}),
        )

    @classmethod
    def payment_reserve(cls) -> "InventoryPolicy":
        return cls(
            reserve_statuses=frozenset({"paid"}),
            ship_statuses=frozenset({"shipped", "completed"}),
            release_statuses=frozenset({"cancelled"}),
        )

    @classmethod
    def pos_immediate(cls) -> "InventoryPolicy":
        return cls(
            reserve_statuses=frozenset(),
            ship_statuses=frozenset({"paid", "completed"}),
            release_statuses=frozenset({"cancelled"}),
        )

    def action(self, old_status: str | None, new_status: str) -> str | None:
        old_reserved = old_status in self.reserve_statuses
        old_shipped = old_status in self.ship_statuses
        if new_status in self.ship_statuses and not old_shipped:
            return "ship"
        if new_status in self.reserve_statuses and not old_reserved and not old_shipped:
            return "reserve"
        if new_status in self.release_statuses and old_reserved:
            return "release"
        if old_shipped and new_status in self.reserve_statuses:
            return "restore_and_reserve"
        return None

    def to_json(self) -> dict:
        return {
            "reserve_statuses": sorted(self.reserve_statuses),
            "ship_statuses": sorted(self.ship_statuses),
            "release_statuses": sorted(self.release_statuses),
            "status_sequence": list(self.status_sequence),
        }

    @classmethod
    def from_json(cls, value: dict | str | None) -> "InventoryPolicy":
        if not value:
            return cls.ecommerce()
        if isinstance(value, str):
            value = json.loads(value)
        return cls(
            reserve_statuses=frozenset(value.get("reserve_statuses") or []),
            ship_statuses=frozenset(value.get("ship_statuses") or []),
            release_statuses=frozenset(value.get("release_statuses") or []),
            status_sequence=tuple(value.get("status_sequence") or _DEFAULT_SEQUENCE),
        )
