from __future__ import annotations

from datetime import datetime
from typing import Any, Literal

from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator

RiskDispositionAction = Literal[
    "confirm",
    "false_positive",
    "request_supplement",
    "start_remediation",
    "resolve",
    "request_waiver",
    "approve_waiver",
    "reject_waiver",
]
RiskAdjudication = Literal["unreviewed", "confirmed", "false_positive"]
RiskLifecycleStatus = Literal[
    "open",
    "supplement_requested",
    "remediation_in_progress",
    "waiver_requested",
    "waived",
    "waiver_rejected",
    "resolved",
]


class RiskDispositionActionCreate(BaseModel):
    action: RiskDispositionAction
    expected_version: int = Field(ge=0)
    request_id: str = Field(min_length=8, max_length=120)
    comment: str | None = Field(default=None, max_length=1000)
    assignee: str | None = Field(default=None, max_length=120)
    due_at: datetime | None = None
    resolution: str | None = Field(default=None, max_length=2000)
    waiver_reason: str | None = Field(default=None, max_length=2000)
    waiver_scope: str | None = Field(default=None, max_length=1000)
    waiver_expires_at: datetime | None = None
    waiver_conditions: list[str] = Field(default_factory=list, max_length=20)

    @field_validator(
        "request_id",
        "comment",
        "assignee",
        "resolution",
        "waiver_reason",
        "waiver_scope",
        mode="before",
    )
    @classmethod
    def normalize_text(cls, value: Any) -> Any:
        if value is None:
            return None
        normalized = str(value).strip()
        return normalized or None

    @field_validator("waiver_conditions", mode="before")
    @classmethod
    def normalize_waiver_conditions(cls, value: Any) -> list[str]:
        if not isinstance(value, list):
            return []
        normalized = [str(item or "").strip() for item in value]
        if any(len(item) > 500 for item in normalized):
            raise ValueError("单条豁免条件不能超过 500 个字符")
        return list(dict.fromkeys(item for item in normalized if item))

    @model_validator(mode="after")
    def validate_action_fields(self) -> RiskDispositionActionCreate:
        if self.action == "resolve" and not self.resolution:
            raise ValueError("resolve 动作必须填写 resolution")
        if self.action in {"false_positive", "request_supplement"} and not self.comment:
            raise ValueError(f"{self.action} 动作必须填写 comment")
        if self.action == "request_waiver" and not all(
            (self.waiver_reason, self.waiver_scope, self.waiver_expires_at)
        ):
            raise ValueError("request_waiver 必须填写原因、范围和有效期")
        if self.action in {"approve_waiver", "reject_waiver"} and not self.comment:
            raise ValueError(f"{self.action} 动作必须填写 comment")
        if self.action in {"approve_waiver", "reject_waiver"} and any(
            value is not None for value in (self.assignee, self.due_at, self.resolution)
        ):
            raise ValueError("豁免决定动作不能同时修改负责人、截止时间或解决说明")
        waiver_fields_present = any(
            (
                self.waiver_reason,
                self.waiver_scope,
                self.waiver_expires_at,
                self.waiver_conditions,
            )
        )
        if self.action != "request_waiver" and waiver_fields_present:
            raise ValueError("只有 request_waiver 可以填写豁免申请字段")
        if self.action in {"confirm", "false_positive"} and any(
            value is not None for value in (self.assignee, self.due_at, self.resolution)
        ):
            raise ValueError("裁决动作不能同时修改负责人、截止时间或解决说明")
        return self


class RiskDispositionEventRead(BaseModel):
    model_config = ConfigDict(from_attributes=True)

    id: str
    tenant_id: str
    disposition_id: str
    observation_id: str
    version: int
    action: RiskDispositionAction
    actor_id: str
    actor_name: str
    request_id: str
    comment: str | None
    before_json: dict[str, Any]
    after_json: dict[str, Any]
    created_at: datetime


class RiskDispositionRead(BaseModel):
    model_config = ConfigDict(from_attributes=True)

    id: str
    tenant_id: str
    observation_id: str
    adjudication: RiskAdjudication
    lifecycle_status: RiskLifecycleStatus
    version: int
    assignee: str | None
    due_at: datetime | None
    resolution: str | None
    waiver_requester_id: str | None = None
    waiver_requester_name: str | None = None
    waiver_requested_at: datetime | None = None
    waiver_reason: str | None = None
    waiver_scope: str | None = None
    waiver_expires_at: datetime | None = None
    waiver_conditions_json: list[str] = Field(default_factory=list)
    waiver_decision: Literal["approved", "rejected"] | None = None
    waiver_decider_id: str | None = None
    waiver_decider_name: str | None = None
    waiver_decided_at: datetime | None = None
    waiver_decision_reason: str | None = None
    available_actions: list[RiskDispositionAction] = Field(default_factory=list)
    read_only_reason: str = ""
    created_at: datetime
    updated_at: datetime
    events: list[RiskDispositionEventRead] = Field(default_factory=list)


class RiskDispositionMutationRead(BaseModel):
    disposition: RiskDispositionRead
    event: RiskDispositionEventRead
    replayed: bool = False
