from __future__ import annotations

from datetime import datetime
from enum import StrEnum
from typing import Any

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


CURATION_SCHEMA_VERSION = "v1"


class CurationModel(BaseModel):
    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True)


class CodeSystem(StrEnum):
    CIE10 = "cie10"
    CUPS = "cups"
    SOAT = "soat"
    BILLING_INTERNAL = "billing_internal"


class CandidateOrigin(StrEnum):
    EXPLICIT = "explicit"
    RAG = "rag"
    LLM = "llm"
    MANUAL = "manual"


class CurationStatus(StrEnum):
    AUTO_VALIDATED = "validado_automaticamente"
    PENDING_REVIEW = "pendiente_revision"
    EXTERNAL_SERVICE_PENDING = "pendiente_servicio_externo"
    CONFLICT = "conflicto"
    APPROVED = "aprobado"
    CORRECTED = "corregido"
    REJECTED = "rechazado"


class ProcedureState(StrEnum):
    DOCUMENTED = "documentado"
    PERFORMED = "realizado"
    BILLED = "facturado"


class CurationDecisionType(StrEnum):
    APPROVE = "approve"
    CORRECT = "correct"
    REJECT = "reject"


class EvidenceReference(CurationModel):
    document_id: str = ""
    document_type: str
    filename: str = ""
    page: int | None = Field(default=None, ge=1)
    section: str = ""
    excerpt: str = Field(default="", max_length=280)
    source_hash: str = ""


class CatalogReference(CurationModel):
    system: CodeSystem
    index_name: str
    fingerprint: str
    built_at: str = ""
    source_name: str = ""
    available: bool = True


class CurationDecision(CurationModel):
    decision: CurationDecisionType
    actor: str
    decided_at: datetime
    original_code: str = ""
    original_description: str = ""
    corrected_code: str = ""
    corrected_description: str = ""
    reason: str = Field(default="", max_length=500)
    expected_version: str


class ObjectiveDiagnosis(CurationModel):
    item_id: str
    code_system: CodeSystem = CodeSystem.CIE10
    code: str = ""
    description: str
    original_text: str = ""
    origin: CandidateOrigin
    status: CurationStatus
    evidence: list[EvidenceReference] = Field(default_factory=list)
    catalog: CatalogReference | None = None
    context_type: str = "no_clasificado"
    is_primary: bool = False
    warnings: list[str] = Field(default_factory=list)
    decisions: list[CurationDecision] = Field(default_factory=list)

    @model_validator(mode="after")
    def validate_cie10_system(self) -> ObjectiveDiagnosis:
        if self.code_system != CodeSystem.CIE10:
            raise ValueError("Un diagnóstico objetivo solo puede usar el sistema CIE-10.")
        return self


class ObjectiveProcedure(CurationModel):
    item_id: str
    description: str
    original_text: str = ""
    codigo_cups: str = ""
    codigo_soat: str = ""
    codigo_facturacion: str = ""
    origin: CandidateOrigin
    status: CurationStatus
    states: list[ProcedureState] = Field(default_factory=list)
    evidence: list[EvidenceReference] = Field(default_factory=list)
    catalog: CatalogReference | None = None
    is_primary: bool = False
    warnings: list[str] = Field(default_factory=list)
    decisions: list[CurationDecision] = Field(default_factory=list)

    @field_validator("states")
    @classmethod
    def deduplicate_states(cls, values: list[ProcedureState]) -> list[ProcedureState]:
        return list(dict.fromkeys(values))


class CodingConflict(CurationModel):
    conflict_id: str
    kind: str
    severity: str = "warning"
    message: str
    item_ids: list[str] = Field(default_factory=list)
    blocking: bool = False


class ObjectiveData(CurationModel):
    schema_version: str = CURATION_SCHEMA_VERSION
    diagnoses: list[ObjectiveDiagnosis] = Field(default_factory=list)
    procedures: list[ObjectiveProcedure] = Field(default_factory=list)
    catalogs: list[CatalogReference] = Field(default_factory=list)
    processing_status: str = "completed"
    retryable_errors: list[str] = Field(default_factory=list)


class CaseCuration(CurationModel):
    schema_version: str = CURATION_SCHEMA_VERSION
    version: str
    diagnoses: list[ObjectiveDiagnosis] = Field(default_factory=list)
    procedures: list[ObjectiveProcedure] = Field(default_factory=list)
    conflicts: list[CodingConflict] = Field(default_factory=list)
    pending_item_ids: list[str] = Field(default_factory=list)
    catalogs: list[CatalogReference] = Field(default_factory=list)
    generation_blocked: bool = False
    blocking_reason: str = ""


def dump_curation_model(model: BaseModel) -> dict[str, Any]:
    return model.model_dump(mode="json", exclude_none=True)
