"""Ban history Pydantic models.

Request, response, and domain models used by the history router and service.
"""

from __future__ import annotations

from pydantic import BaseModel, ConfigDict, Field

from app.models.ban import TimeRange

__all__ = [
    "HistoryBanItem",
    "HistoryListResponse",
    "IpDetailResponse",
    "IpTimelineEvent",
    "TimeRange",
]


class HistoryBanItem(BaseModel):
    """A single row in the history ban-list table.

    Populated from the fail2ban database and optionally enriched with
    geolocation data.
    """

    model_config = ConfigDict(strict=True)

    ip: str = Field(..., description="Banned IP address.")
    jail: str = Field(..., description="Jail that issued the ban.")
    banned_at: str = Field(..., description="ISO 8601 UTC timestamp of the ban.")
    ban_count: int = Field(..., ge=1, description="How many times this IP was banned.")
    failures: int = Field(
        default=0,
        ge=0,
        description="Total failure count extracted from the ``data`` column.",
    )
    matches: list[str] = Field(
        default_factory=list,
        description="Matched log lines stored in the ``data`` column.",
    )
    country_code: str | None = Field(
        default=None,
        description="ISO 3166-1 alpha-2 country code, or ``null`` if unknown.",
    )
    country_name: str | None = Field(
        default=None,
        description="Human-readable country name, or ``null`` if unknown.",
    )
    asn: str | None = Field(
        default=None,
        description="Autonomous System Number string (e.g. ``'AS3320'``).",
    )
    org: str | None = Field(
        default=None,
        description="Organisation name associated with the IP.",
    )


class HistoryListResponse(BaseModel):
    """Paginated history ban-list response."""

    model_config = ConfigDict(strict=True)

    items: list[HistoryBanItem] = Field(default_factory=list)
    total: int = Field(..., ge=0, description="Total matching records.")
    page: int = Field(..., ge=1)
    page_size: int = Field(..., ge=1)


# ---------------------------------------------------------------------------
# Per-IP timeline
# ---------------------------------------------------------------------------


class IpTimelineEvent(BaseModel):
    """A single ban event in a per-IP timeline.

    Represents one row from the fail2ban ``bans`` table for a specific IP.
    """

    model_config = ConfigDict(strict=True)

    jail: str = Field(..., description="Jail that triggered this ban.")
    banned_at: str = Field(..., description="ISO 8601 UTC timestamp of the ban.")
    ban_count: int = Field(
        ...,
        ge=1,
        description="Running ban counter for this IP at the time of this event.",
    )
    failures: int = Field(
        default=0,
        ge=0,
        description="Failure count at the time of the ban.",
    )
    matches: list[str] = Field(
        default_factory=list,
        description="Matched log lines that triggered the ban.",
    )


class IpDetailResponse(BaseModel):
    """Full historical record for a single IP address.

    Contains aggregated totals and a chronological timeline of all ban events
    recorded in the fail2ban database for the given IP.
    """

    model_config = ConfigDict(strict=True)

    ip: str = Field(..., description="The IP address.")
    total_bans: int = Field(..., ge=0, description="Total number of ban records.")
    total_failures: int = Field(
        ...,
        ge=0,
        description="Sum of all failure counts across all ban events.",
    )
    last_ban_at: str | None = Field(
        default=None,
        description="ISO 8601 UTC timestamp of the most recent ban, or ``null``.",
    )
    country_code: str | None = Field(
        default=None,
        description="ISO 3166-1 alpha-2 country code, or ``null`` if unknown.",
    )
    country_name: str | None = Field(
        default=None,
        description="Human-readable country name, or ``null`` if unknown.",
    )
    asn: str | None = Field(
        default=None,
        description="Autonomous System Number string.",
    )
    org: str | None = Field(
        default=None,
        description="Organisation name associated with the IP.",
    )
    timeline: list[IpTimelineEvent] = Field(
        default_factory=list,
        description="All ban events for this IP, ordered newest-first.",
    )
