"""System health, diagnostics, and warning alerts collector."""

import logging
import os
import re
import socket
import subprocess
import time
from typing import Any, Dict, List

from .settings import get_settings

logger = logging.getLogger(__name__)

_ALERTS_CACHE: Dict[str, Any] = {"timestamp": 0, "data": None}
CACHE_TTL = 60


def get_public_ip() -> str:
    try:
        res = subprocess.run(["curl", "-s", "-m", "2", "https://ifconfig.me"], capture_output=True, text=True)
        ip = res.stdout.strip()
        if re.match(r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$", ip):
            return ip
    except Exception:
        pass
    return ""


def check_apt_updates() -> Dict[str, Any]:
    try:
        from .operations import get_apt_job_state

        job = get_apt_job_state()
        if job.get("running"):
            return {
                "id": "apt_updates",
                "ok": False,
                "level": "warning",
                "title": "Aggiornamento APT in Corso (In Queue)",
                "message": f"L'aggiornamento pacchetti di sistema (apt update && upgrade -y) è in corso in background dall'ora {job.get('started_at')}.",
            }
    except Exception:
        pass

    try:
        res = subprocess.run(["apt-get", "-s", "upgrade"], capture_output=True, text=True, timeout=4)
        match = re.search(r"^(\d+)\s+upgraded", res.stdout, re.MULTILINE)
        if match:
            count = int(match.group(1))
            if count > 0:
                return {
                    "id": "apt_updates",
                    "ok": False,
                    "level": "warning",
                    "title": "Aggiornamenti APT Pendenti",
                    "message": f"Ci sono {count} pacchetti di sistema in attesa di aggiornamento tramite apt.",
                }
    except Exception as exc:
        logger.debug(f"apt updates check error: {exc}")

    return {
        "id": "apt_updates",
        "ok": True,
        "level": "success",
        "title": "Sistema APT Aggiornato",
        "message": "Nessun aggiornamento pendente rilevato per i pacchetti di sistema.",
    }


def check_limristem_version() -> Dict[str, Any]:
    settings = get_settings()
    current_version = getattr(settings, "app_version", "0.1.14")
    latest_version = current_version

    channel_url = getattr(settings, "version_manifest_url", None) or "https://nightly.limristem.eu/mail/version.json"
    try:
        req = urllib_request.Request(channel_url, headers={"User-Agent": "LimristemMailCheck/1.0"})
        with urllib_request.urlopen(req, timeout=2.5) as resp:
            if resp.status == 200:
                data = json.loads(resp.read().decode("utf-8"))
                if isinstance(data, list) and data:
                    remote_v = str(data[0].get("version", "")).strip()
                    if remote_v:
                        latest_version = remote_v
    except Exception as exc:
        logger.debug(f"Remote version check error: {exc}")

    def parse_ver(v_str: str) -> tuple[int, ...]:
        return tuple(int(p) for p in re.findall(r"\d+", v_str))

    is_outdated = False
    try:
        if parse_ver(latest_version) > parse_ver(current_version):
            is_outdated = True
    except Exception:
        if latest_version != current_version:
            is_outdated = True

    if is_outdated:
        return {
            "id": "app_version",
            "ok": False,
            "level": "warning",
            "title": f"Nuova Versione Limristem eMail Disponibile (v{latest_version})",
            "message": f"È disponibile una nuova versione (v{latest_version}). Versione attualmente installata sul server: v{current_version}.",
        }

    return {
        "id": "app_version",
        "ok": True,
        "level": "success",
        "title": f"Versione Limristem eMail (v{current_version})",
        "message": f"Il server è aggiornato all'ultima versione disponibile ({current_version}).",
    }


def check_dns_and_ptr(hostname: str) -> List[Dict[str, Any]]:
    results = []
    public_ip = get_public_ip()

    # 1. PTR Record Check
    ptr_ok = False
    ptr_value = ""
    if public_ip:
        try:
            res = subprocess.run(["dig", "+short", "-x", public_ip, "@8.8.8.8"], capture_output=True, text=True, timeout=3)
            ptr_value = res.stdout.strip().rstrip(".")
        except Exception:
            pass

    if ptr_value == hostname:
        results.append({
            "id": "ptr_record",
            "ok": True,
            "level": "success",
            "title": "Record PTR Reverse DNS",
            "message": f"Record PTR impostato per IP {public_ip} -> {ptr_value}.",
        })
    elif ptr_value:
        results.append({
            "id": "ptr_record",
            "ok": False,
            "level": "warning",
            "title": "Record PTR Non Coincidente",
            "message": f"Il record PTR per {public_ip} punta a '{ptr_value}', ma dovrebbe puntare all'hostname del server '{hostname}'. Questo potrebbe abbassare il punteggio di deliverability.",
        })
    else:
        results.append({
            "id": "ptr_record",
            "ok": False,
            "level": "danger",
            "title": "Record PTR Mancante / Non Impostato",
            "message": f"Il record Reverse DNS (PTR) per l'IP pubblico {public_ip or 'del server'} non risulta configurato. Potrebbe causare il rifiuto delle e-mail inviate dai principali provider (Gmail, Outlook).",
        })

    # 2. Hostname A Record Check
    a_ok = False
    resolved_ip = ""
    if hostname:
        try:
            res = subprocess.run(["dig", "+short", hostname, "@8.8.8.8"], capture_output=True, text=True, timeout=3)
            resolved_ip = res.stdout.strip()
            if resolved_ip and (not public_ip or resolved_ip == public_ip):
                a_ok = True
        except Exception:
            pass

    if a_ok:
        results.append({
            "id": "hostname_a_record",
            "ok": True,
            "level": "success",
            "title": f"Record A Hostname ({hostname})",
            "message": f"Il record DNS A per {hostname} risolve correttamente sull'IP {resolved_ip}.",
        })
    else:
        results.append({
            "id": "hostname_a_record",
            "ok": False,
            "level": "danger",
            "title": f"Record A Hostname ({hostname}) Non Corretto",
            "message": f"Il record DNS A per l'hostname {hostname} non punta all'IP del server ({public_ip or 'sconosciuto'}). Rilevato: {resolved_ip or 'Nessuna risoluzione'}.",
        })

    # 3. MTA-STS A Record & SSL Cert Check
    mta_sts_host = f"mta-sts.{hostname}" if hostname and hostname != "localhost" else ""
    if mta_sts_host:
        mta_sts_a_ok = False
        mta_sts_ip = ""
        try:
            res = subprocess.run(["dig", "+short", mta_sts_host, "@8.8.8.8"], capture_output=True, text=True, timeout=3)
            mta_sts_ip = res.stdout.strip()
            if mta_sts_ip:
                mta_sts_a_ok = True
        except Exception:
            pass

        mta_sts_ssl_ok = False
        if mta_sts_a_ok:
            try:
                import ssl
                ctx = ssl.create_default_context()
                ctx.check_hostname = False
                ctx.verify_mode = ssl.CERT_NONE
                with socket.create_connection((mta_sts_host, 443), timeout=3) as sock:
                    with ctx.wrap_socket(sock, server_hostname=mta_sts_host):
                        mta_sts_ssl_ok = True
            except Exception:
                pass

        if mta_sts_a_ok and mta_sts_ssl_ok:
            results.append({
                "id": "mta_sts",
                "ok": True,
                "level": "success",
                "title": f"MTA-STS ({mta_sts_host})",
                "message": f"Record A ed SSL Let's Encrypt attivi e funzionanti per {mta_sts_host}.",
            })
        elif mta_sts_a_ok:
            results.append({
                "id": "mta_sts",
                "ok": False,
                "level": "warning",
                "title": f"Certificato Let's Encrypt Mancante per {mta_sts_host}",
                "message": f"Il record A per {mta_sts_host} esiste ({mta_sts_ip}) ma il certificato SSL HTTPS/Let's Encrypt non risulta attivo.",
            })
        else:
            results.append({
                "id": "mta_sts",
                "ok": False,
                "level": "warning",
                "title": f"Record MTA-STS Mancante ({mta_sts_host})",
                "message": f"Il sottodominio {mta_sts_host} per la sicurezza MTA-STS non possiede un record DNS A configurato.",
            })

    return results


def check_core_services() -> Dict[str, Any]:
    services = ["postfix", "dovecot", "rspamd"]
    down = []
    for s in services:
        try:
            res = subprocess.run(["systemctl", "is-active", s], capture_output=True, text=True, timeout=2)
            if res.stdout.strip() != "active":
                down.append(s)
        except Exception:
            down.append(s)

    if down:
        return {
            "id": "core_services",
            "ok": False,
            "level": "danger",
            "title": "Servizi Core Disattivi / Errore",
            "message": f"I seguenti servizi mail non sono attivi: {', '.join(down)}. Postfix, Dovecot e Rspamd devono essere tutti online.",
        }

    return {
        "id": "core_services",
        "ok": True,
        "level": "success",
        "title": "Servizi Core Online",
        "message": "Postfix, Dovecot e Rspamd sono attivi ed in esecuzione.",
    }


def check_firewall_useful_ports() -> Dict[str, Any]:
    try:
        try:
            from .admin_ops import load_firewall_config
        except (ImportError, ValueError):
            from admin_ops import load_firewall_config
        cfg = load_firewall_config()
    except Exception as exc:
        logger.debug(f"Failed to load firewall config for alerts: {exc}")
        cfg = {}

    raw_enabled = cfg.get("firewall-enabled", cfg.get("firewall", "yes"))
    is_enabled = str(raw_enabled).lower() in {"yes", "1", "true", "enabled"}

    if not is_enabled:
        return {
            "id": "firewall_ports",
            "ok": False,
            "level": "danger",
            "title": "Firewall Disattivato",
            "message": "Il firewall di sistema (nftables) risulta disattivato. Attivare il firewall nella sezione Security per proteggere il server di posta.",
        }

    rules = cfg.get("firewall-rules", [])
    allowed_ports = set()

    for r in rules:
        if isinstance(r, dict) and str(r.get("enabled", "")).lower() in {"yes", "1", "true"}:
            ports_str = str(r.get("ports", ""))
            for p in re.split(r"[\s,]+", ports_str):
                p = p.strip()
                if p:
                    if "-" in p:
                        try:
                            start, end = map(int, p.split("-", 1))
                            for port_num in range(start, end + 1):
                                allowed_ports.add(str(port_num))
                        except ValueError:
                            pass
                    else:
                        allowed_ports.add(p)

    if not allowed_ports and cfg.get("firewall-allowed-tcp-ports"):
        for p in re.split(r"[\s,]+", str(cfg["firewall-allowed-tcp-ports"])):
            if p.strip():
                allowed_ports.add(p.strip())

    essential_ports = [
        ("25", "SMTP Inbound/Outbound"),
        ("80", "HTTP / Certificati Let's Encrypt"),
        ("443", "HTTPS / Admin Panel"),
        ("465", "SMTPS Submission"),
        ("587", "SMTP Submission"),
        ("993", "IMAPS / Dovecot"),
        ("995", "POP3S / Dovecot"),
    ]

    blocked = [f"Porta {p} ({label})" for p, label in essential_ports if p not in allowed_ports]

    if blocked:
        return {
            "id": "firewall_ports",
            "ok": False,
            "level": "danger",
            "title": "Porte Mail Essenziali Chiuse o Inaccessibili",
            "message": f"Il firewall è attivo ma le seguenti porte essenziali per l'invio, la ricezione e l'accesso mail risultano chiuse nelle regole: {', '.join(blocked)}.",
        }

    return {
        "id": "firewall_ports",
        "ok": True,
        "level": "success",
        "title": "Firewall Attivo e Porte Configurate",
        "message": "Il firewall nftables è attivo e le porte essenziali per il funzionamento della posta (25, 80, 443, 465, 587, 993, 995) sono aperte ed accessibili.",
    }


def collect_system_alerts(force_refresh: bool = False) -> Dict[str, Any]:
    global _ALERTS_CACHE
    now = time.time()
    if not force_refresh and (now - _ALERTS_CACHE["timestamp"] < CACHE_TTL) and _ALERTS_CACHE.get("data"):
        return _ALERTS_CACHE["data"]

    settings = get_settings()
    hostname = settings.hostname or "localhost"

    alerts = []
    alerts.append(check_apt_updates())
    alerts.append(check_limristem_version())
    alerts.extend(check_dns_and_ptr(hostname))
    alerts.append(check_core_services())
    alerts.append(check_firewall_useful_ports())

    active_issues = [a for a in alerts if not a.get("ok")]
    has_issues = len(active_issues) > 0

    data = {
        "has_issues": has_issues,
        "issue_count": len(active_issues),
        "alerts": alerts,
        "active_issues": active_issues,
    }

    _ALERTS_CACHE = {"timestamp": now, "data": data}
    return data
