"""Pydantic models for xeno-canto API v3 responses."""

from __future__ import annotations

from pydantic import BaseModel, Field, field_validator


class SonogramURLs(BaseModel):
    small: str = ""
    med: str = ""
    large: str = ""
    full: str = ""


class OscillogramURLs(BaseModel):
    small: str = ""
    med: str = ""
    large: str = ""


class Recording(BaseModel):
    """A single xeno-canto recording (all 37 v3 fields)."""

    id: str
    gen: str = ""
    sp: str = ""
    ssp: str = ""
    grp: str = ""
    en: str = ""
    rec: str = ""
    cnt: str = ""
    loc: str = ""
    lat: str | None = None
    lon: str | None = None
    alt: str = ""
    type: str = ""
    sex: str = ""
    stage: str = ""
    method: str = ""
    url: str = ""
    file: str = ""
    file_name: str = Field("", alias="file-name")
    sono: SonogramURLs = Field(default_factory=SonogramURLs)
    osci: OscillogramURLs = Field(default_factory=OscillogramURLs)
    lic: str = ""
    q: str = ""
    length: str = ""
    time: str = ""
    date: str = ""
    uploaded: str = ""
    also: list[str] = Field(default_factory=list)
    rmk: str = ""
    animal_seen: str = Field("", alias="animal-seen")
    playback_used: str = Field("", alias="playback-used")
    temp: str = ""
    regnr: str = ""
    auto: str = ""
    dvc: str = ""
    mic: str = ""
    smp: str | None = None

    model_config = {"populate_by_name": True}

    @field_validator(
        "gen", "sp", "ssp", "grp", "en", "rec", "cnt", "loc", "alt",
        "type", "sex", "stage", "method", "url", "file", "file_name",
        "lic", "q", "length", "time", "date", "uploaded", "rmk",
        "animal_seen", "playback_used", "temp", "regnr", "auto",
        "dvc", "mic",
        mode="before",
    )
    @classmethod
    def none_to_empty(cls, v: object) -> str:
        if v is None:
            return ""
        return str(v)

    @field_validator("also", mode="before")
    @classmethod
    def none_to_empty_list(cls, v: object) -> list[str]:
        if v is None:
            return []
        return v  # type: ignore[return-value]

    @field_validator("lat", "lon", mode="before")
    @classmethod
    def coerce_latlon(cls, v: object) -> str | None:
        if v is None or v == "" or v == "null":
            return None
        return str(v)

    @property
    def species_name(self) -> str:
        return f"{self.gen} {self.sp}".strip()

    @property
    def type_list(self) -> list[str]:
        """Parse the free-text type field into a list."""
        if not self.type:
            return []
        return [t.strip() for t in self.type.split(",")]


class APIResponse(BaseModel):
    """Xeno-canto API v3 response envelope."""

    numRecordings: str = "0"
    numSpecies: str = "0"
    page: int = 1
    numPages: int = 1
    recordings: list[Recording] = Field(default_factory=list)

    @property
    def total_recordings(self) -> int:
        return int(self.numRecordings)

    @property
    def total_species(self) -> int:
        return int(self.numSpecies)
