"""Admin API for operational checks: deliverability, RBL, e2e, IP reputation."""

from __future__ import annotations

import os
from typing import List, Optional

from fastapi import APIRouter, Depends, HTTPException, Query
from pydantic import BaseModel, ConfigDict, Field, field_validator
from sqlalchemy.orm import Session

from .. import models
from ..db import get_db
from ..operations import (
    check_public_ip_reputation,
    rbl_inbound_config,
    run_deliverability_check,
    run_e2e_connectivity_tests,
    write_rspamd_rbl_config,
)
from ..security import require_admin
from ..settings import get_settings

router = APIRouter(prefix="/operations", tags=["operations"])
settings = get_settings()


class RblUpdate(BaseModel):
    model_config = ConfigDict(str_strip_whitespace=True)

    enabled: bool
    zones: List[str] = Field(default_factory=list)

    @field_validator("zones")
    @classmethod
    def validate_zones(cls, value: List[str]) -> List[str]:
        cleaned = []
        for item in value:
            zone = item.strip().lower().rstrip(".")
            if not zone:
                continue
            if len(zone) > 253 or " " in zone or ".." in zone:
                raise ValueError(f"Invalid DNSBL zone: {item}")
            cleaned.append(zone)
        return cleaned[:20]


class DeliverabilityRequest(BaseModel):
    model_config = ConfigDict(str_strip_whitespace=True)

    domain: Optional[str] = None
    mail_host: Optional[str] = None
    public_ip: Optional[str] = None
    dkim_selector: Optional[str] = None


@router.get("/deliverability")
def deliverability(
    domain: Optional[str] = Query(default=None),
    mail_host: Optional[str] = Query(default=None),
    public_ip: Optional[str] = Query(default=None),
    dkim_selector: Optional[str] = Query(default=None),
    _: str = Depends(require_admin),
):
    return run_deliverability_check(domain, mail_host, public_ip, dkim_selector)


@router.post("/deliverability")
def deliverability_post(payload: DeliverabilityRequest, _: str = Depends(require_admin)):
    return run_deliverability_check(payload.domain, payload.mail_host, payload.public_ip, payload.dkim_selector)


@router.get("/e2e")
def e2e_tests(mail_host: Optional[str] = Query(default=None), _: str = Depends(require_admin)):
    return run_e2e_connectivity_tests(mail_host)


@router.get("/ip-reputation")
def ip_reputation(
    ip: Optional[str] = Query(default=None),
    _: str = Depends(require_admin),
    db: Session = Depends(get_db),
):
    target = (ip or os.getenv("LIMRISTEM_MAIL_PUBLIC_IP") or "").strip()
    if not target:
        raise HTTPException(status_code=400, detail="Provide ?ip= or set LIMRISTEM_MAIL_PUBLIC_IP")
    result = check_public_ip_reputation(target)
    # Persist snapshot for panel history (best-effort).
    try:
        for item in result.get("results") or []:
            if item.get("listed") is None:
                continue
            row = models.ReputationCheck(
                entity_type="ip",
                entity_value=target,
                source=str(item.get("zone") or "dnsbl")[:64],
                listed=bool(item.get("listed")),
                detail=(item.get("detail") or ",".join(item.get("answers") or []))[:512] or None,
            )
            db.add(row)
        db.commit()
    except Exception:
        db.rollback()
    return result


@router.get("/ip-reputation/history")
def ip_reputation_history(limit: int = Query(default=50, ge=1, le=500), db: Session = Depends(get_db), _: str = Depends(require_admin)):
    rows = (
        db.query(models.ReputationCheck)
        .order_by(models.ReputationCheck.checked_at.desc())
        .limit(limit)
        .all()
    )
    return [
        {
            "id": row.id,
            "entity_type": row.entity_type,
            "entity_value": row.entity_value,
            "source": row.source,
            "listed": bool(row.listed),
            "detail": row.detail,
            "checked_at": row.checked_at,
        }
        for row in rows
    ]


@router.get("/rbl")
def get_rbl(_: str = Depends(require_admin)):
    return rbl_inbound_config()


@router.put("/rbl")
def update_rbl(payload: RblUpdate, _: str = Depends(require_admin)):
    # Persist preferences in env via staged file + optional live rspamd write.
    from ..admin_ops import run_root_script

    zones = " ".join(payload.zones) if payload.zones else "zen.spamhaus.org bl.spamcop.net"
    staged = write_rspamd_rbl_config(enabled=payload.enabled, zones=payload.zones or zones.split())
    try:
        run_root_script(
            "manage-rbl.sh",
            "set",
            "yes" if payload.enabled else "no",
            zones,
        )
    except HTTPException:
        # Root helper may be missing in dev; staged file still written.
        pass
    return {
        "status": "ok",
        "enabled": payload.enabled,
        "zones": payload.zones or zones.split(),
        "staged_config": str(staged),
        "config": rbl_inbound_config(),
    }


@router.get("/overview")
def operations_overview(_: str = Depends(require_admin)):
    from ..topology import topology_summary

    return {
        "topology": topology_summary(),
        "rbl": rbl_inbound_config(),
        "public_ip": os.getenv("LIMRISTEM_MAIL_PUBLIC_IP") or None,
        "hostname": settings.hostname,
        "primary_domain": settings.primary_domain,
    }
