"""Geo and IP lookup Pydantic models.

Response models for the ``GET /api/geo/lookup/{ip}`` endpoint.
"""

from __future__ import annotations

from collections.abc import Awaitable, Callable
from dataclasses import dataclass
from typing import TYPE_CHECKING

from pydantic import BaseModel, ConfigDict, Field

if TYPE_CHECKING:
    import aiohttp
    import aiosqlite


class GeoDetail(BaseModel):
    """Enriched geolocation data for an IP address.

    Populated from the ip-api.com free API.
    """

    model_config = ConfigDict(strict=True)

    country_code: str | None = Field(
        default=None,
        description="ISO 3166-1 alpha-2 country code.",
    )
    country_name: str | None = Field(
        default=None,
        description="Human-readable country name.",
    )
    asn: str | None = Field(
        default=None,
        description="Autonomous System Number (e.g. ``'AS3320'``).",
    )
    org: str | None = Field(
        default=None,
        description="Organisation associated with the ASN.",
    )


class GeoCacheStatsResponse(BaseModel):
    """Response for ``GET /api/geo/stats``.

    Exposes diagnostic counters of the geo cache subsystem so operators
    can assess resolution health from the UI or CLI.
    """

    model_config = ConfigDict(strict=True)

    cache_size: int = Field(..., description="Number of positive entries in the in-memory cache.")
    unresolved: int = Field(..., description="Number of geo_cache rows with country_code IS NULL.")
    neg_cache_size: int = Field(..., description="Number of entries in the in-memory negative cache.")
    dirty_size: int = Field(..., description="Number of newly resolved entries not yet flushed to disk.")


class GeoReResolveResponse(BaseModel):
    """Response for ``POST /api/geo/re-resolve``.

    Reports how many previously unresolved IPs were retried and how many
    gained a resolved country code after the re-resolve operation.
    """

    model_config = ConfigDict(strict=True)

    resolved: int = Field(..., description="Number of IPs successfully resolved.")
    total: int = Field(..., description="Number of IPs retried.")


class IpLookupResponse(BaseModel):
    """Response for ``GET /api/geo/lookup/{ip}``.

    Aggregates current ban status and geographical information for an IP.
    """

    model_config = ConfigDict(strict=True)

    ip: str = Field(..., description="The queried IP address.")
    currently_banned_in: list[str] = Field(
        default_factory=list,
        description="Names of jails where this IP is currently banned.",
    )
    geo: GeoDetail | None = Field(
        default=None,
        description="Enriched geographical and network information.",
    )


# ---------------------------------------------------------------------------
# shared service types
# ---------------------------------------------------------------------------


@dataclass
class GeoInfo:
    """Geo resolution result used throughout backend services."""

    country_code: str | None
    country_name: str | None
    asn: str | None
    org: str | None


GeoEnricher = Callable[[str], Awaitable[GeoInfo | None]]
GeoBatchLookup = Callable[
    [list[str], "aiohttp.ClientSession", "aiosqlite.Connection | None"],
    Awaitable[dict[str, GeoInfo]],
]
GeoCacheLookup = Callable[[list[str]], tuple[dict[str, GeoInfo], list[str]]]
