"""Recoleccion OSINT pasiva de registros DNS via el binario `dig`.

Funcion IMPURA: para cada tipo de registro ejecuta `dig +short <domain> <TYPE>`
como subprocess y parsea la salida (una linea por valor). Es OSINT pasivo:
consulta DNS publico, no envia trafico intrusivo al objetivo.

Nunca lanza: devuelve un dict con `status` ("ok"/"error"). El campo `raw`
siempre esta presente para guardar la evidencia legible en un vault OSINT.
"""

import subprocess

DEFAULT_TYPES = ["A", "AAAA", "MX", "NS", "SOA", "TXT", "CNAME"]


def dns_records(
    domain: str,
    record_types: list[str] | None = None,
    timeout_s: int = 20,
) -> dict:
    """Resuelve registros DNS de un dominio ejecutando `dig +short`.

    Para cada tipo en ``record_types`` ejecuta ``dig +short <domain> <TYPE>``
    y parsea la salida: cada linea no vacia es un valor del registro. Un
    dominio o registro inexistente produce una lista vacia (no error).

    Args:
        domain: Dominio a resolver (ej. ``"google.com"``).
        record_types: Lista de tipos de registro DNS (ej. ``["A", "MX"]``).
            None usa los defaults: A, AAAA, MX, NS, SOA, TXT, CNAME.
        timeout_s: Timeout total en segundos repartido entre las consultas
            (cada `dig` recibe una porcion del presupuesto, minimo 2s).

    Returns:
        Dict de estado. En exito::

            {
                "status": "ok",
                "domain": <domain>,
                "records": {"A": [...], "MX": [...], ...},
                "raw": "=== A ===\\n...\\n=== MX ===\\n...",
            }

        En fallo (binario ausente, dominio vacio)::

            {"status": "error", "error": <str>, "raw": ""}
    """
    if not domain or not domain.strip():
        return {"status": "error", "error": "dns_records: domain vacio", "raw": ""}

    domain = domain.strip()
    query_types = record_types if record_types is not None else list(DEFAULT_TYPES)
    per_query_timeout = max(2.0, float(timeout_s) / max(1, len(query_types)))

    records: dict[str, list[str]] = {}
    raw_parts: list[str] = []

    for record_type in query_types:
        rt = record_type.strip().upper()
        try:
            proc = subprocess.run(
                ["dig", "+short", domain, rt],
                capture_output=True,
                text=True,
                timeout=per_query_timeout,
            )
            values = [
                line.strip()
                for line in proc.stdout.splitlines()
                if line.strip()
            ]
            section_body = proc.stdout.rstrip("\n") if proc.stdout.strip() else "(sin registros)"
        except FileNotFoundError:
            return {
                "status": "error",
                "error": "dns_records: binario `dig` no encontrado en PATH (paquete dnsutils)",
                "raw": "",
            }
        except subprocess.TimeoutExpired:
            values = []
            section_body = "(timeout)"

        records[rt] = values
        raw_parts.append(f"=== {rt} ===\n{section_body}")

    return {
        "status": "ok",
        "domain": domain,
        "records": records,
        "raw": "\n".join(raw_parts),
    }


if __name__ == "__main__":
    try:
        result = dns_records("google.com", record_types=["A", "MX", "NS", "TXT"])
        print(result["status"])
        if result["status"] == "ok":
            print("A:", result["records"].get("A"))
            print("MX:", result["records"].get("MX"))
            print("--- raw ---")
            print(result["raw"])
        else:
            print("error:", result.get("error"))
    except Exception as exc:  # smoke: tolera cualquier fallo de red sin romper
        print("smoke fallo (tolerado):", exc)
