"""
MundiX CLI - AutoPilot System
Controls the level of interactivity between the AI and the user.
5 levels from fully manual to fully autonomous.

Levels:
    1 - MANUAL:     Ask before every action. Full user control.
    2 - GUIDED:     Auto-execute safe commands. Ask for installs and dangerous ones.
    3 - BALANCED:   Auto-execute all. Ask only for dangerous commands. Auto-fix errors.
    4 - AGGRESSIVE: Auto-execute everything. Only stop for destructive (rm -rf /) commands.
    5 - AUTOPILOT:  Full autonomy. Execute everything without asking. YOLO mode.
"""

import json
from pathlib import Path

MUNDIX_DIR = Path.home() / ".mundix"
CONFIG_FILE = MUNDIX_DIR / "config.json"

LEVELS = {
    1: {
        "name": "MANUAL",
        "icon": "[cyan]1[/cyan]",
        "color": "cyan",
        "description": "Ask before every action. Full user control.",
        "auto_execute": False,
        "auto_install": False,
        "auto_fix": False,
        "skip_dangerous_confirm": False,
    },
    2: {
        "name": "GUIDED",
        "icon": "[green]2[/green]",
        "color": "green",
        "description": "Auto-execute safe commands. Ask for installs and dangerous.",
        "auto_execute": True,
        "auto_install": False,
        "auto_fix": True,
        "skip_dangerous_confirm": False,
    },
    3: {
        "name": "BALANCED",
        "icon": "[yellow]3[/yellow]",
        "color": "yellow",
        "description": "Auto-execute all. Ask only for dangerous. Auto-fix errors.",
        "auto_execute": True,
        "auto_install": True,
        "auto_fix": True,
        "skip_dangerous_confirm": False,
    },
    4: {
        "name": "AGGRESSIVE",
        "icon": "[red]4[/red]",
        "color": "red",
        "description": "Auto-execute everything. Only stop for destructive commands.",
        "auto_execute": True,
        "auto_install": True,
        "auto_fix": True,
        "skip_dangerous_confirm": True,
    },
    5: {
        "name": "AUTOPILOT",
        "icon": "[bold red]5[/bold red]",
        "color": "bold red",
        "description": "Full autonomy. No questions asked. YOLO mode.",
        "auto_execute": True,
        "auto_install": True,
        "auto_fix": True,
        "skip_dangerous_confirm": True,
    },
}

DEFAULT_LEVEL = 2  # GUIDED is a safe default


class AutoPilot:
    """Manages interactivity levels and auto-execution behavior."""

    def __init__(self):
        MUNDIX_DIR.mkdir(parents=True, exist_ok=True)
        self._config = self._load_config()
        self.level = self._config.get("autopilot_level", DEFAULT_LEVEL)
        self.auto_fix_retries = 3  # Max retries for auto-fix

    def _load_config(self) -> dict:
        """Load config from disk."""
        if CONFIG_FILE.exists():
            try:
                with open(CONFIG_FILE) as f:
                    return json.load(f)
            except (json.JSONDecodeError, IOError):
                return {}
        return {}

    def _save_config(self):
        """Save config to disk."""
        self._config["autopilot_level"] = self.level
        try:
            with open(CONFIG_FILE, "w") as f:
                json.dump(self._config, f, indent=2)
        except IOError:
            pass

    @property
    def settings(self) -> dict:
        """Get current level settings."""
        return LEVELS.get(self.level, LEVELS[DEFAULT_LEVEL])

    @property
    def name(self) -> str:
        return self.settings["name"]

    @property
    def color(self) -> str:
        return self.settings["color"]

    def set_level(self, level: int) -> bool:
        """Set the autopilot level (1-5). Returns True if valid."""
        if 1 <= level <= 5:
            self.level = level
            self._save_config()
            return True
        return False

    def should_auto_execute(self) -> bool:
        """Should commands be auto-executed without asking?"""
        return self.settings["auto_execute"]

    def should_auto_install(self) -> bool:
        """Should missing tools be auto-installed?"""
        return self.settings["auto_install"]

    def should_auto_fix(self) -> bool:
        """Should errors be automatically sent to AI for fixing?"""
        return self.settings["auto_fix"]

    def should_skip_dangerous_confirm(self) -> bool:
        """Should dangerous commands skip confirmation? (levels 4-5 only)"""
        return self.settings["skip_dangerous_confirm"]

    def should_ask_execution(self, is_dangerous: bool, is_install: bool) -> bool:
        """
        Determine if we need to ask the user before executing.
        Returns True if we should ask, False if we can auto-execute.
        """
        if self.level == 1:
            # MANUAL: always ask
            return True

        if self.level == 5:
            # AUTOPILOT: never ask
            return False

        if is_dangerous:
            if self.level == 4:
                # AGGRESSIVE: only ask for truly destructive
                return False  # Even dangerous ones are auto-executed
            # Levels 2-3: ask for dangerous
            return True

        if is_install and self.level == 2:
            # GUIDED: ask for installs
            return True

        # Auto-execute
        return False

    def format_level_display(self) -> str:
        """Format current level for display in prompt/status bar."""
        s = self.settings
        return f"[{s['color']}]AP:{self.level}[/{s['color']}]"

    def format_level_short(self) -> str:
        """Short format for status bar."""
        return f"AP:{self.level}"


# ─── Error Diagnosis Helpers ─────────────────────────────────────────

# Common error patterns and their auto-fix strategies
ERROR_PATTERNS = {
    "command not found": {
        "type": "missing_command",
        "fix": "install_package",
    },
    "not found": {
        "type": "missing_command",
        "fix": "install_package",
    },
    "No such file or directory": {
        "type": "missing_file",
        "fix": "ai_diagnose",
    },
    "Permission denied": {
        "type": "permission",
        "fix": "try_sudo",
    },
    "Connection refused": {
        "type": "network",
        "fix": "ai_diagnose",
    },
    "Connection timed out": {
        "type": "network",
        "fix": "ai_diagnose",
    },
    "Name or service not known": {
        "type": "dns",
        "fix": "ai_diagnose",
    },
    "Unable to locate package": {
        "type": "package_not_found",
        "fix": "ai_diagnose",
    },
    "E: Could not get lock": {
        "type": "apt_lock",
        "fix": "kill_apt",
    },
    "ModuleNotFoundError": {
        "type": "python_module",
        "fix": "pip_install",
    },
    "ImportError": {
        "type": "python_module",
        "fix": "pip_install",
    },
}

# Map of common commands to their package names (Kali/Debian)
COMMAND_TO_PACKAGE = {
    "nmap": "nmap",
    "sqlmap": "sqlmap",
    "gobuster": "gobuster",
    "dirb": "dirb",
    "nikto": "nikto",
    "hydra": "hydra",
    "john": "john",
    "hashcat": "hashcat",
    "wpscan": "wpscan",
    "nuclei": "nuclei",
    "feroxbuster": "feroxbuster",
    "ffuf": "ffuf",
    "masscan": "masscan",
    "rustscan": "rustscan",
    "responder": "responder",
    "enum4linux": "enum4linux",
    "smbclient": "smbclient",
    "rpcclient": "samba-common-bin",
    "crackmapexec": "crackmapexec",
    "netexec": "netexec",
    "bloodhound": "bloodhound",
    "chisel": "chisel",
    "socat": "socat",
    "tcpdump": "tcpdump",
    "tshark": "tshark",
    "wireshark": "wireshark",
    "aircrack-ng": "aircrack-ng",
    "wifite": "wifite",
    "curl": "curl",
    "wget": "wget",
    "git": "git",
    "python3": "python3",
    "pip3": "python3-pip",
    "ruby": "ruby",
    "perl": "perl",
    "gcc": "gcc",
    "make": "make",
    "nc": "netcat-openbsd",
    "ncat": "ncat",
    "whois": "whois",
    "dig": "dnsutils",
    "host": "dnsutils",
    "traceroute": "traceroute",
    "arp-scan": "arp-scan",
    "netdiscover": "netdiscover",
    "theharvester": "theharvester",
    "recon-ng": "recon-ng",
    "maltego": "maltego",
    "sherlock": "sherlock",
    "subfinder": "subfinder",
    "amass": "amass",
    "whatweb": "whatweb",
    "wafw00f": "wafw00f",
    "sudo": "sudo",
    "tree": "tree",
    "jq": "jq",
    "vim": "vim",
    "nano": "nano",
    "tmux": "tmux",
    "screen": "screen",
}


def diagnose_error(command: str, stderr: str, returncode: int) -> dict:
    """
    Analyze a command error and return a diagnosis with suggested fix.
    
    Returns:
        dict with keys:
            - error_type: str (category of error)
            - fix_strategy: str (how to fix)
            - fix_command: str or None (specific command to run)
            - description: str (human-readable explanation)
            - needs_ai: bool (whether AI should be consulted)
    """
    error_text = stderr.strip().lower() if stderr else ""
    original_stderr = stderr.strip() if stderr else ""

    # ── PRIORITY 1: sudo not found (must check BEFORE generic "not found") ──
    # This catches: "/bin/sh: 1: sudo: not found" when running as root
    if command.strip().startswith("sudo ") and ("sudo" in error_text and "not found" in error_text):
        fixed_cmd = command.strip()[5:].strip()
        return {
            "error_type": "no_sudo",
            "fix_strategy": "remove_sudo",
            "fix_command": fixed_cmd,
            "description": "sudo not available. Retrying without sudo (likely already root).",
            "needs_ai": False,
        }

    # ── PRIORITY 2: Command not found (return code 127 or explicit message) ──
    if returncode == 127 or "command not found" in error_text or "not found" in error_text:
        # Extract the command name that was not found
        cmd_name = command.split()[0].strip()
        # Remove sudo prefix if present
        parts = command.split()
        if parts[0] == "sudo" and len(parts) > 1:
            cmd_name = parts[1]

        package = COMMAND_TO_PACKAGE.get(cmd_name)
        if package:
            return {
                "error_type": "missing_command",
                "fix_strategy": "install_package",
                "fix_command": f"apt-get install -y {package}",
                "description": f"'{cmd_name}' is not installed. Package: {package}",
                "needs_ai": False,
            }
        else:
            return {
                "error_type": "missing_command",
                "fix_strategy": "ai_diagnose",
                "fix_command": None,
                "description": f"'{cmd_name}' not found and no known package mapping.",
                "needs_ai": True,
            }

    # ── PRIORITY 3: Permission denied -> try with sudo ──
    if "permission denied" in error_text:
        if not command.startswith("sudo "):
            return {
                "error_type": "permission",
                "fix_strategy": "try_sudo",
                "fix_command": f"sudo {command}",
                "description": "Permission denied. Retrying with sudo.",
                "needs_ai": False,
            }

    # Python module not found
    if "modulenotfounderror" in error_text or "importerror" in error_text:
        # Try to extract module name
        import re
        match = re.search(r"no module named ['\"]?(\w+)", error_text)
        if match:
            module = match.group(1)
            return {
                "error_type": "python_module",
                "fix_strategy": "pip_install",
                "fix_command": f"pip3 install {module}",
                "description": f"Python module '{module}' not installed.",
                "needs_ai": False,
            }

    # APT lock
    if "could not get lock" in error_text:
        return {
            "error_type": "apt_lock",
            "fix_strategy": "kill_apt",
            "fix_command": "killall -9 apt-get apt dpkg 2>/dev/null; dpkg --configure -a",
            "description": "APT is locked by another process.",
            "needs_ai": False,
        }

    # Unable to locate package
    if "unable to locate package" in error_text:
        return {
            "error_type": "package_not_found",
            "fix_strategy": "update_and_retry",
            "fix_command": f"apt-get update && {command}",
            "description": "Package not found. Updating package list and retrying.",
            "needs_ai": False,
        }

    # For any other error, let AI diagnose
    return {
        "error_type": "unknown",
        "fix_strategy": "ai_diagnose",
        "fix_command": None,
        "description": f"Error: {original_stderr[:200]}",
        "needs_ai": True,
    }
