from __future__ import annotations

from datetime import datetime
from typing import Any, Literal

from pydantic import BaseModel, ConfigDict, Field


class AgentAssetReleasePolicyWrite(BaseModel):
    shadow_min_samples: int = Field(default=20, ge=1, le=1_000_000)
    canary_min_samples: int = Field(default=100, ge=1, le=1_000_000)
    max_error_rate: float = Field(default=0.02, ge=0, le=1)
    min_precision: float = Field(default=0.98, ge=0, le=1)
    max_precision_drop: float = Field(default=0.02, ge=0, le=1)
    canary_traffic_percent: int = Field(default=5, ge=1, le=50)
    reviewer_quorum: int = Field(default=1, ge=1, le=2)
    recall_gate_enabled: bool = True
    negative_sample_percent: int = Field(default=20, ge=1, le=100)
    negative_min_reviewed: int = Field(default=5, ge=1, le=10_000)
    min_recall: float = Field(default=0.95, ge=0, le=1)
    recall_confidence_level: Literal[0.9, 0.95, 0.99] = 0.95


class AgentAssetReleaseStartWrite(BaseModel):
    candidate_version: str = Field(min_length=1, max_length=30)
    policy: AgentAssetReleasePolicyWrite = Field(default_factory=AgentAssetReleasePolicyWrite)


class AgentAssetReleaseMonitorTriggerWrite(BaseModel):
    """可信监控触发器不接受调用方提供的样本数或质量指标。"""

    model_config = ConfigDict(extra="forbid")


class AgentAssetReleaseRollbackWrite(BaseModel):
    reason: str = Field(min_length=2, max_length=1000)


class AgentAssetReleaseStateRead(BaseModel):
    stage: Literal["unmanaged", "shadow", "canary", "active", "rolled_back"] = "unmanaged"
    release_id: str = ""
    candidate_version: str = ""
    previous_version: str = ""
    policy: dict[str, Any] = Field(default_factory=dict)
    started_at: str = ""
    started_by: str = ""
    updated_at: str = ""
    history: list[dict[str, Any]] = Field(default_factory=list)
    rollback: dict[str, Any] | None = None


class AgentAssetReleaseServingPlanRead(BaseModel):
    stage: str
    primary_version: str
    candidate_version: str
    candidate_traffic_percent: int
    shadow_evaluation: bool


class AgentAssetReleaseMonitorRead(BaseModel):
    asset_id: str
    release_id: str
    stage: str
    version: str
    telemetry_status: Literal["collecting", "ready"]
    status: Literal["collecting", "passed", "failed"]
    evaluation_submitted: bool
    release_stage: str
    rolled_back: bool
    reasons: list[str] = Field(default_factory=list)
    test_run_id: str | None = None
    metrics: dict[str, Any] = Field(default_factory=dict)
    alerts: list[dict[str, str]] = Field(default_factory=list)


class AgentAssetReleaseReviewItemRead(BaseModel):
    sample_id: str
    observation_id: str
    source_document_id: str
    rule_code: str
    business_stage: Literal["expense_application", "reimbursement"]
    prediction_blinded: Literal[True] = True
    reviewer_count: int = 0
    required_reviewers: int = 1
    conflicted: bool = False
    created_at: datetime


class AgentAssetReleaseReviewQueueRead(BaseModel):
    asset_id: str
    release_id: str
    stage: Literal["shadow", "canary", "active"]
    version: str
    pending_total: int
    telemetry_status: Literal["collecting", "ready"]
    reasons: list[str] = Field(default_factory=list)
    metrics: dict[str, Any] = Field(default_factory=dict)
    alerts: list[dict[str, str]] = Field(default_factory=list)
    items: list[AgentAssetReleaseReviewItemRead] = Field(default_factory=list)


class AgentAssetReleaseReviewLabelWrite(BaseModel):
    model_config = ConfigDict(extra="forbid")

    label: Literal[
        "risk_present",
        "risk_absent",
        "confirmed",
        "false_positive",
    ]


class AgentAssetReleaseReviewLabelRead(BaseModel):
    label_id: str
    observation_id: str
    label: Literal["risk_present", "risk_absent", "confirmed", "false_positive"]
    monitor: AgentAssetReleaseMonitorRead
