from __future__ import annotations

from datetime import datetime
from typing import Any, Literal

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

from app.schemas.reimbursement import ExpenseClaimRead

ApprovalTaskKind = Literal["root", "add_sign", "countersign"]
ApprovalNodeKey = Literal["direct_manager", "budget_manager", "finance"]
ApprovalTaskStatus = Literal[
    "waiting",
    "pending",
    "completed",
    "returned",
    "cancelled",
    "superseded",
]
ApprovalTaskDecision = Literal["approved", "returned"]
ApprovalTaskAction = Literal[
    "delegate",
    "delegation_revoke",
    "transfer",
    "add_sign",
    "countersign",
    "approve",
    "return",
    "sla_escalate",
]


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

    id: str
    tenant_id: str
    task_id: str
    node_instance_id: str
    event_type: str
    actor_id: str
    actor_name: str
    actor_type: str
    request_id: str
    expected_task_version: int
    result_task_version: int
    payload_json: dict[str, Any] = Field(default_factory=dict)
    before_json: dict[str, Any] = Field(default_factory=dict)
    after_json: dict[str, Any] = Field(default_factory=dict)
    approval_action_ledger_id: str | None = None
    business_event_id: str | None = None
    correlation_id: str | None = None
    causation_id: str | None = None
    occurred_at: datetime


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

    id: str
    tenant_id: str
    claim_id: str
    expense_case_id: str | None = None
    node_instance_id: str
    node_entry_key: str
    parent_task_id: str | None = None
    task_kind: ApprovalTaskKind
    node_key: ApprovalNodeKey
    node_label: str
    node_sequence: int
    sequence_order: int
    coordination_mode: Literal["single", "sequential_before_owner", "parallel_all"]
    owner_kind: Literal["employee", "role"]
    owner_key: str
    owner_employee_id: str | None = None
    owner_name: str
    assignee_kind: Literal["employee", "role"]
    assignee_key: str
    assignee_employee_id: str | None = None
    assignee_name: str
    delegated_by: str | None = None
    delegation_expires_at: datetime | None = None
    status: ApprovalTaskStatus
    decision: ApprovalTaskDecision | None = None
    opinion: str | None = None
    version: int
    claim_status_snapshot: str
    claim_stage_snapshot: str
    entered_at: datetime
    entered_at_source: Literal["workflow_event", "submitted_at", "updated_at", "backfill"]
    activated_at: datetime | None = None
    sla_hours_snapshot: int
    due_at: datetime
    completed_at: datetime | None = None
    cancelled_at: datetime | None = None
    escalation_level: int
    escalated_at: datetime | None = None
    next_escalation_at: datetime | None = None
    priority_score: int
    priority_tier: Literal["normal", "high", "urgent"]
    priority_reasons_json: list[dict[str, Any]] = Field(default_factory=list)
    risk_level: Literal["low", "medium", "high", "critical"]
    open_risk_count: int
    evidence_completeness: float
    batch_eligible: bool
    batch_block_reasons_json: list[str] = Field(default_factory=list)
    projection_updated_at: datetime
    created_at: datetime
    updated_at: datetime
    can_act: bool = False
    available_actions: list[str] = Field(default_factory=list)
    read_only_reason: str = ""


class ApprovalTaskMutationRead(BaseModel):
    task: ApprovalTaskRead
    event: ApprovalTaskEventRead
    related_tasks: list[ApprovalTaskRead] = Field(default_factory=list)
    replayed: bool = False


class ApprovalTaskQueueItemRead(BaseModel):
    task: ApprovalTaskRead
    claim: ExpenseClaimRead


class ApprovalTaskListRead(BaseModel):
    items: list[ApprovalTaskQueueItemRead] = Field(default_factory=list)
    total: int = 0
    page: int = 1
    page_size: int = 20
    total_pages: int = 0
    generated_at: datetime


class ApprovalTaskDecisionRead(BaseModel):
    mutation: ApprovalTaskMutationRead
    claim: ExpenseClaimRead


class ApprovalTaskActionBase(BaseModel):
    request_id: str = Field(min_length=8, max_length=120)
    expected_task_version: int = Field(ge=1)
    reason: str = Field(min_length=2, max_length=500)

    @field_validator("request_id", "reason", mode="before")
    @classmethod
    def normalize_text(cls, value: Any) -> str:
        return str(value or "").strip()


class ApprovalTaskAssignmentAction(ApprovalTaskActionBase):
    target_employee_id: str = Field(min_length=1, max_length=36)
    expires_at: datetime | None = None

    @field_validator("target_employee_id", mode="before")
    @classmethod
    def normalize_employee_id(cls, value: Any) -> str:
        return str(value or "").strip()


class ApprovalTaskParticipantsAction(ApprovalTaskActionBase):
    participant_employee_ids: list[str] = Field(min_length=1, max_length=10)

    @field_validator("participant_employee_ids", mode="before")
    @classmethod
    def normalize_participants(cls, value: Any) -> list[str]:
        if not isinstance(value, list):
            return []
        normalized = [str(item or "").strip() for item in value]
        return [item for item in normalized if item]

    @model_validator(mode="after")
    def validate_unique_participants(self) -> ApprovalTaskParticipantsAction:
        if len(set(self.participant_employee_ids)) != len(self.participant_employee_ids):
            raise ValueError("加签或会签参与人不能重复。")
        return self


class ApprovalTaskDecisionAction(ApprovalTaskActionBase):
    opinion: str | None = Field(default=None, max_length=500)

    @field_validator("opinion", mode="before")
    @classmethod
    def normalize_opinion(cls, value: Any) -> str | None:
        normalized = str(value or "").strip()
        return normalized or None


class ApprovalTaskReturnAction(ApprovalTaskActionBase):
    reason_codes: list[str] = Field(default_factory=list, max_length=10)

    @field_validator("reason_codes", mode="before")
    @classmethod
    def normalize_reason_codes(cls, value: Any) -> list[str]:
        if not isinstance(value, list):
            return []
        return list(
            dict.fromkeys(
                item
                for item in (str(entry or "").strip() for entry in value)
                if item
            )
        )


class ApprovalTaskBatchApproveItem(BaseModel):
    task_id: str = Field(min_length=1, max_length=36)
    expected_task_version: int = Field(ge=1)
    expected_status: str = Field(min_length=1, max_length=30)
    expected_approval_stage: str = Field(min_length=1, max_length=50)
    opinion: str | None = Field(default=None, max_length=500)

    @field_validator(
        "task_id",
        "expected_status",
        "expected_approval_stage",
        "opinion",
        mode="before",
    )
    @classmethod
    def normalize_batch_text(cls, value: Any) -> Any:
        if value is None:
            return None
        return str(value).strip()


class ApprovalTaskBatchApproveCreate(BaseModel):
    batch_request_id: str = Field(min_length=8, max_length=80)
    items: list[ApprovalTaskBatchApproveItem] = Field(min_length=1, max_length=20)

    @field_validator("batch_request_id", mode="before")
    @classmethod
    def normalize_batch_request_id(cls, value: Any) -> str:
        return str(value or "").strip()

    @model_validator(mode="after")
    def validate_unique_tasks(self) -> ApprovalTaskBatchApproveCreate:
        task_ids = [item.task_id for item in self.items]
        if len(set(task_ids)) != len(task_ids):
            raise ValueError("批量审批不能包含重复任务。")
        return self


class ApprovalTaskBatchItemRead(BaseModel):
    task_id: str
    claim_id: str | None = None
    status: Literal[
        "succeeded",
        "replayed",
        "conflict",
        "blocked",
        "forbidden",
        "failed",
    ]
    code: str
    message: str
    claim: dict[str, Any] | None = None


class ApprovalTaskBatchApproveRead(BaseModel):
    batch_request_id: str
    status: Literal["succeeded", "partial", "failed"]
    succeeded_count: int = 0
    replayed_count: int = 0
    conflict_count: int = 0
    blocked_count: int = 0
    forbidden_count: int = 0
    failed_count: int = 0
    items: list[ApprovalTaskBatchItemRead] = Field(default_factory=list)


class ApprovalTaskCandidateRead(BaseModel):
    employee_id: str
    employee_no: str
    name: str
    email: str
    qualified: bool
    reason: str = ""
