from __future__ import annotations

from decimal import Decimal
from typing import Literal

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


SoatUnit = Literal["SMLDV", "UVT", "UVB"]
ComponentName = Literal["cirujano", "anestesia", "ayudantia", "sala", "materiales"]


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

    url: str
    sha256: str = Field(pattern=r"^[0-9a-f]{64}$")
    local_file: str | None = None
    page_table: str


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

    name: ComponentName
    code: str
    coefficient: Decimal = Field(gt=0)
    official_value: int | None = Field(default=None, ge=0)


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

    code: str = Field(pattern=r"^\d{5}$")
    description: str = Field(min_length=3)
    surgical_group: int = Field(ge=2, le=23)
    base_coefficient: Decimal = Field(gt=0)
    official_base_value: int | None = Field(default=None, ge=0)
    components: list[SoatTariffComponent] = Field(min_length=4)
    source: CatalogSource

    @model_validator(mode="after")
    def _components_are_unique(self) -> SoatCatalogEntry:
        names = [component.name for component in self.components]
        if len(names) != len(set(names)):
            raise ValueError("Los componentes tarifarios deben ser unicos")
        return self


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

    year: int = Field(ge=2022, le=2100)
    unit: SoatUnit
    unit_value: Decimal = Field(gt=0)
    version: str = Field(min_length=1)
    generated_from: list[CatalogSource] = Field(min_length=1)
    entries: list[SoatCatalogEntry]

    @field_validator("entries")
    @classmethod
    def _codes_are_unique(cls, value: list[SoatCatalogEntry]) -> list[SoatCatalogEntry]:
        codes = [entry.code for entry in value]
        if len(codes) != len(set(codes)):
            raise ValueError("Los codigos SOAT deben ser unicos por vigencia")
        return value
