"""Threat-intel enrichment row — one per attacker IP, TTL-cached."""
from datetime import datetime, timezone
from typing import Optional

from sqlalchemy import Column
from sqlmodel import Field, SQLModel

from ._base import _BIG_TEXT


class AttackerIntel(SQLModel, table=True):
    """Aggregated threat-intel verdict for a single attacker IP.

    Populated by the ``decnet enrich`` worker, which queries multiple
    free-tier intel providers (GreyNoise Community, AbuseIPDB,
    abuse.ch Feodo Tracker + ThreatFox) and writes one row per
    attacker IP. The row is TTL-cached via ``expires_at`` so re-firings
    inside the cache window short-circuit before any HTTP egress.

    Per-provider columns are nullable until each provider has answered;
    the enrichment pass writes whichever providers succeeded and leaves
    the rest unchanged on a partial failure.

    ``schema_version`` is committed to storage from day one — federation
    gossip in v2/v3 requires cross-operator compatibility, and
    retrofitting a version column after rows exist is painful. Mirrors
    the rationale on :class:`SessionProfile`.
    """

    __tablename__ = "attacker_intel"

    uuid: str = Field(primary_key=True)  # uuid.uuid4().hex, generated by writer
    # Canonical key. One intel row per attacker UUID; FK guarantees no orphan
    # rows when an attacker is deleted, and UNIQUE keeps upserts honest.
    attacker_uuid: str = Field(
        foreign_key="attackers.uuid",
        unique=True,
        index=True,
    )
    # DENORMALISED — NOT a key. The IP the worker queried providers with at
    # write time. Useful for SIEM payloads and audit lookups; updated on every
    # upsert if the attacker rotates IPs. Never use this column as a lookup
    # key; ``attacker_uuid`` is the only canonical identifier here.
    attacker_ip: str = Field(index=True)
    schema_version: int = Field(default=1)

    # ── GreyNoise Community ─────────────────────────────────────────────
    # classification ∈ {"benign", "malicious", "suspicious", "unknown"}
    greynoise_classification: Optional[str] = Field(default=None, max_length=32)
    greynoise_raw: str = Field(
        default="{}",
        sa_column=Column("greynoise_raw", _BIG_TEXT, nullable=False, default="{}"),
    )
    greynoise_queried_at: Optional[datetime] = Field(default=None)

    # ── AbuseIPDB ────────────────────────────────────────────────────────
    # 0..100 abuse confidence score
    abuseipdb_score: Optional[int] = Field(default=None)
    abuseipdb_raw: str = Field(
        default="{}",
        sa_column=Column("abuseipdb_raw", _BIG_TEXT, nullable=False, default="{}"),
    )
    abuseipdb_queried_at: Optional[datetime] = Field(default=None)

    # ── abuse.ch Feodo Tracker ───────────────────────────────────────────
    feodo_listed: Optional[bool] = Field(default=None)
    feodo_raw: str = Field(
        default="{}",
        sa_column=Column("feodo_raw", _BIG_TEXT, nullable=False, default="{}"),
    )
    feodo_queried_at: Optional[datetime] = Field(default=None)

    # ── abuse.ch ThreatFox ───────────────────────────────────────────────
    threatfox_listed: Optional[bool] = Field(default=None)
    threatfox_raw: str = Field(
        default="{}",
        sa_column=Column("threatfox_raw", _BIG_TEXT, nullable=False, default="{}"),
    )
    threatfox_queried_at: Optional[datetime] = Field(default=None)

    # ── Aggregate verdict ────────────────────────────────────────────────
    # Synthesised from per-provider columns. ∈ {"malicious", "suspicious",
    # "benign", "unknown"}. Used by the dashboard and webhook consumers
    # that don't want to reason over four provider columns.
    aggregate_verdict: Optional[str] = Field(
        default=None, max_length=32, index=True
    )

    # ── TTL bookkeeping ──────────────────────────────────────────────────
    cached_at: datetime = Field(
        default_factory=lambda: datetime.now(timezone.utc), index=True
    )
    expires_at: datetime = Field(index=True)
