"""Pydantic models for AccuWeather Locations API responses.

The field names intentionally mirror the PascalCase names used by the API
(``Key``, ``EnglishName``, ...) so a model instance reads exactly like the
API documentation. Unknown fields are preserved (``extra="allow"``) so new
API attributes never break parsing.
"""

from pydantic import BaseModel, ConfigDict


class _ApiModel(BaseModel):
    """Base model: tolerate and preserve unknown fields from the API."""

    model_config = ConfigDict(extra="allow")


class RegionModel(_ApiModel):
    """A world region, e.g. Oceania.

    ``EnglishName`` is omitted by slim endpoints such as autocomplete.
    """

    ID: str
    LocalizedName: str
    EnglishName: str | None = None


class CountryModel(_ApiModel):
    """A country, identified by its ISO 3166-1 alpha-2 code.

    ``EnglishName`` is omitted by slim endpoints such as autocomplete.
    """

    ID: str
    LocalizedName: str
    EnglishName: str | None = None

    @property
    def name(self) -> str:
        """The country's English name, falling back to the localized one."""
        return self.EnglishName or self.LocalizedName


class AdministrativeAreaModel(_ApiModel):
    """A state/province/administrative area within a country."""

    ID: str
    LocalizedName: str
    EnglishName: str | None = None
    Level: int | None = None
    LocalizedType: str | None = None
    EnglishType: str | None = None
    CountryID: str | None = None


class TimeZoneModel(_ApiModel):
    """Time-zone information for a location."""

    Code: str
    Name: str
    GmtOffset: float
    IsDaylightSaving: bool
    NextOffsetChange: str | None = None


class UnitValue(_ApiModel):
    """A value with its unit, e.g. ``{"Value": 26.0, "Unit": "m"}``."""

    Value: float | None = None
    Unit: str | None = None
    UnitType: int | None = None


class ElevationModel(_ApiModel):
    """Elevation of a location in metric and imperial units."""

    Metric: UnitValue | None = None
    Imperial: UnitValue | None = None


class GeoPositionModel(_ApiModel):
    """Latitude/longitude (and elevation) of a location."""

    Latitude: float
    Longitude: float
    Elevation: ElevationModel | None = None


class SupplementalAdminArea(_ApiModel):
    """An additional administrative area a location belongs to."""

    Level: int | None = None
    LocalizedName: str | None = None
    EnglishName: str | None = None


class ParentCityModel(_ApiModel):
    """The parent city of a point of interest or neighborhood."""

    Key: str
    LocalizedName: str | None = None
    EnglishName: str | None = None


class LocationModelItem(_ApiModel):
    """A single location as returned by the AccuWeather Locations API.

    The most useful attributes are ``Key`` (needed by every weather
    endpoint), ``LocalizedName``, ``Country`` and ``GeoPosition``.
    """

    Version: int | None = None
    Key: str
    Type: str | None = None
    Rank: int | None = None
    LocalizedName: str
    EnglishName: str | None = None
    PrimaryPostalCode: str | None = None
    Region: RegionModel | None = None
    Country: CountryModel | None = None
    AdministrativeArea: AdministrativeAreaModel | None = None
    TimeZone: TimeZoneModel | None = None
    GeoPosition: GeoPositionModel | None = None
    IsAlias: bool | None = None
    SupplementalAdminAreas: list[SupplementalAdminArea] | None = None
    DataSets: list[str] | None = None
    ParentCity: ParentCityModel | None = None

    def __str__(self) -> str:
        country = self.Country.name if self.Country else "?"
        return f"{self.LocalizedName}, {country} (key={self.Key})"


class LocationModel(BaseModel):
    """Wrapper around a Locations API response.

    ``response`` is a list for search endpoints (city, POI, postal code,
    autocomplete) and a single item for geoposition / IP lookups.
    """

    response: list[LocationModelItem] | LocationModelItem

    @property
    def first(self) -> LocationModelItem:
        """The best-ranked location in the response.

        Raises:
            IndexError: If the response is an empty list.
        """
        if isinstance(self.response, list):
            return self.response[0]
        return self.response

    def get_location_key(self) -> str:
        """Return the AccuWeather location key of the best match."""
        return self.first.Key


# Backwards-compatible aliases for the v1 class names.
Region = RegionModel
Country = CountryModel
AdministrativeArea = AdministrativeAreaModel
TimeZone = TimeZoneModel
Elevation = ElevationModel
GeoPosition = GeoPositionModel
