"""
menu.py — Interactive hierarchical menu for vps-tool.
"""

import sys
from modules.output import info, warn, header


class _Quit(Exception):
    """Raised when the user requests a full quit from within a submenu."""
    pass

# ── Menu tree definition ──────────────────────────────────────────────────────
# Each entry: (display_label, command_key_or_submenu)
# If value is a list, it's a submenu. If a string, it's a command key.

MENU_TREE = [
    ("Apache", [
        ("apacheprocs   — Show Apache status and scoreboard", "apacheprocs"),
        ("apache-stats  — Show request table sorted by domain", "apache-stats"),
        ("service-status — Detailed uptime: Apache, MySQL, DNS", "service-status"),
        ("domain-config  — Show domain configuration", "domain-config"),
    ]),
    ("Disk Space", [
        ("tops         — Top largest files/directories", "tops"),
        ("finddiskspace — Check disk usage and large files", "finddiskspace"),
        ("dspace        — Deep disk scan (dirs+files >200MB)", "dspace"),
        ("findbigdirs   — Dirs with 800+ files", "findbigdirs"),
    ]),
    ("Email & Spam", [
        ("spamcheck    — Full email health report", "spamcheck"),
        ("lsforwards   — List email forwarders", "lsforwards"),
        ("exim-security — Show Exim TLS config", "exim-security"),
    ]),
    ("Security", [
        ("security-policy  — Show cPanel SecurityPolicy fields", "security-policy"),
        ("cphulk-status    — Show cPHulk brute-force protection status", "cphulk-status"),
        ("cphulk-enable    — Enable cPHulk", "cphulk-enable"),
        ("cphulk-disable   — Disable cPHulk [WARNING]", "cphulk-disable"),
        ("enable-root  — Enable root SSH password auth [WARNING]", "enable-root"),
        ("disable-root — Disable root SSH password auth [Keys Required]", "disable-root"),
        ("pass-security — Verify mailbox password", "pass-security"),
        ("sec-enable   — Re-enable cPanel Security Questions", "sec-enable"),
        ("sec-disable  — Disable cPanel Security Questions [WARNING]", "sec-disable"),
        ("hack          — Scan for WP brute-force attacks", "hack"),
        ("blocks        — Today's CSF/LFD blocks", "blocks"),
        ("wpsecurity    — Find Wordfence/WP-Cerber installs", "wpsecurity"),
        ("exim-security — Exim TLS options and ciphers", "exim-security"),
    ]),
    ("DNS & Domains", [
        ("zone         — Show BIND zone file", "zone"),
        ("checkssl     — Check SSL certificate details", "checkssl"),
        ("docroot      — Get document root for a domain", "docroot"),
        ("lsdomains    — List all domains and owners", "lsdomains"),
    ]),
    ("DDoS & Bots", [
        ("ddos         — Analyze access log for DDoS patterns", "ddos"),
        ("bots         — Scan domlogs for bot traffic", "bots"),
    ]),
    ("WordPress", [
        ("wpfunctions  — WordPress command help menu", "wpfunctions"),
        ("wpaudit      — WP install audit (run from WP dir)", "wpaudit"),
        ("wppluginlist — List plugins as owner", "wppluginlist"),
        ("wpthemelist  — List themes as owner", "wpthemelist"),
        ("wpoldplugins — Check plugins: version, age, updates", "wpoldplugins"),
        ("wploginurl   — Generate one-time auto-login URL", "wploginurl"),
        ("wpsecurity   — Find security plugin installs", "wpsecurity"),
    ]),
    ("Sessions & Access", [
        ("cpanel-login  — cPanel session login URL", "cpanel-login"),
        ("whm-login     — WHM/reseller session login URL", "whm-login"),
        ("root-whm      — Root WHM session login URL", "root-whm"),
        ("webmail-login — Webmail session login URL (guided)", "webmail-login"),
        ("shell-enable  — Enable shell access for user", "shell-enable"),
        ("shell-disable — Disable shell access for user", "shell-disable"),
    ]),
    ("2FA Management", [
        ("2fa-status    — Check server-wide 2FA policy", "2fa-status"),
        ("2fa-enable    — Enable 2FA policy server-wide", "2fa-enable"),
        ("2fa-disable   — Disable 2FA policy [Warning]", "2fa-disable"),
        ("2fa-users     — List user 2FA configurations", "2fa-users"),
        ("2fa-remove    — Remove a user's 2FA config [Warning]", "2fa-remove"),
    ]),
    ("Network", [
        ("cfallow      — Whitelist missing Cloudflare IPs in CSF", "cfallow"),
        ("csf-search   — Search CSF/iptables rules for an IP", "csf-search"),
        ("csf-allow    — Add an IP to CSF allow list", "csf-allow"),
        ("csf-unblock  — Remove an IP from temp bans and deny list", "csf-unblock"),
        ("ggnull       — Null-route an IP address", "ggnull"),
        ("ggpaste      — Upload to GreenGeeks paste service", "ggpaste"),
        ("fix-license  — Fix cPanel license (cpkeyclt)", "fix-license"),
        ("summary      — Run migration summary script", "summary"),
    ]),
    ("Miscellaneous", [
        ("ggloop       — Batch run a command from a file", "ggloop"),
        ("phpusers     — List users and PHP versions", "phpusers"),
        ("errors       — Today's error_log entries", "errors"),
        ("robotstxt    — Find crawl-delay in robots.txt", "robotstxt"),
    ]),
]


def _prompt(prompt_str, valid_range=None):
    """Prompt for input. Returns string, None (back/empty), or 'QUIT' (q)."""
    try:
        val = input(prompt_str).strip()
    except (EOFError, KeyboardInterrupt):
        print()
        return None
    if val.lower() in ('q', 'quit', 'exit'):
        return 'QUIT'
    if val.lower() in ('back', 'b', ''):
        return None
    if valid_range and val.isdigit() and int(val) not in valid_range:
        return None
    return val


def _prompt_args(cmd):
    """For commands that need arguments, prompt for them."""
    needs_arg = {
        'zone':         'Domain name (e.g. example.com): ',
        'checkssl':     'Domain name (e.g. example.com): ',
        'docroot':      'Domain name (e.g. example.com): ',
        'ddos':         'Path to access log file: ',
        'ggnull':       'IP address to null-route: ',
        'ggpaste':      'Paste type (text/log/etc): ',

        'pass-security':'Email address: ',
        'ggloop':       'File path: ',
        'tops':         'Args (or Enter for default — all /home/* and /backup): ',
        'cpanel-login': 'cPanel username: ',
        'whm-login':    'Username: ',
        'shell-enable': 'cPanel username: ',
        'shell-disable':'cPanel username: ',

        '2fa-remove':   'cPanel username: ',
    }
    prompts = needs_arg.get(cmd)
    if not prompts:
        return []

    try:
        val = input(f"  {prompts}").strip()
    except (EOFError, KeyboardInterrupt):
        print()
        return []

    if not val:
        return []

    # Commands that take a second argument
    second = {
        'pass-security': 'Password: ',
        'ggpaste':       'File path (or Enter for stdin): ',
        'ggloop':        'Command template (use %v as placeholder): ',
    }
    if cmd in second:
        try:
            val2 = input(f"  {second[cmd]}").strip()
        except (EOFError, KeyboardInterrupt):
            print()
            return [val]
        return [val, val2] if val2 else [val]

    # tops args are space-split
    if cmd == 'tops':
        return val.split() if val else []

    return [val]


def show_menu():
    """Run the interactive hierarchical menu."""
    from modules.utils import VERSION
    while True:
        print()
        header(f"GGVPS Troubleautomagic — Main Menu  (v{VERSION})")
        print()
        for i, (label, _) in enumerate(MENU_TREE, 1):
            print(f"  {i:>2}. {label}")
        print()
        print("   h. Help / list all commands")
        print("   q. Quit")
        print()

        choice = _prompt("Select category [1-{}/h/q]: ".format(len(MENU_TREE)))
        if choice is None or choice == 'QUIT':
            print("Goodbye.")
            break

        if choice.lower() == 'h':
            from modules.misc import listfunctions
            listfunctions()
            continue

        if not choice.isdigit() or not (1 <= int(choice) <= len(MENU_TREE)):
            warn(f"Invalid choice: {choice}")
            continue

        cat_label, submenu = MENU_TREE[int(choice) - 1]
        try:
            _show_submenu(cat_label, submenu)
        except _Quit:
            print("Goodbye.")
            break


def _show_submenu(category, items):
    """Show a category submenu and dispatch the chosen command."""
    # Pull out wpfunctions if present — shown as 'h' (help) instead of numbered
    help_key = None
    display_items = []
    for label, key in items:
        if key == 'wpfunctions':
            help_key = key
        else:
            display_items.append((label, key))

    while True:
        print()
        header(f"  {category}")
        print()
        for i, (label, _) in enumerate(display_items, 1):
            print(f"  {i:>2}. {label}")
        print()
        if help_key:
            print("   h. Help (WordPress commands)")
        print("   b. Back  |  q. Quit")
        print()

        choice = _prompt(f"Select command [1-{len(display_items)}/b/q]: ")
        if choice == 'QUIT':
            raise _Quit
        if choice is None:
            break

        if choice.lower() == 'h' and help_key:
            print()
            dispatch(help_key)
            try:
                val = input("\n  [Press Enter to continue]").strip()
                if val.lower() in ('q', 'quit'):
                    raise _Quit
            except (EOFError, KeyboardInterrupt):
                break
            continue

        if not choice.isdigit() or not (1 <= int(choice) <= len(display_items)):
            warn(f"Invalid choice: {choice}")
            continue

        _, cmd_key = display_items[int(choice) - 1]
        args = _prompt_args(cmd_key)
        print()
        dispatch(cmd_key, args)

        try:
            val = input("\n  [Press Enter to continue]").strip()
            if val.lower() in ('q', 'quit'):
                raise _Quit
        except (EOFError, KeyboardInterrupt):
            break


def dispatch(cmd, args=None):
    """Dispatch a command key to its implementation."""
    args = args or []

    from modules import (
        apache, disk, email_tools, security,
        dns, ddos as ddos_mod, wordpress, network, misc, sessions
    )

    cmd_map = {
        # Apache
        'apacheprocs':    lambda: apache.apacheprocs(),
        'apache-stats':   lambda: apache.apache_stats(),
        'service-status': lambda: apache.service_status(),
        'domain-config':  lambda: apache.domain_config(args[0] if args else None),

        # Disk
        'tops':           lambda: disk.tops(args),
        'finddiskspace':  lambda: disk.finddiskspace(),
        'dspace':         lambda: disk.dspace(),
        'findbigdirs':    lambda: disk.findbigdirs(),

        # Email
        'spamcheck':      lambda: email_tools.spamcheck(),
        'lsforwards':     lambda: email_tools.lsforwards(),
        'exim-security':  lambda: security.exim_security(),

        # Security
        'security-policy': lambda: security.security_policy(),
        'cphulk-status':  lambda: security.cphulk_status(),
        'cphulk-enable':  lambda: security.cphulk_enable(),
        'cphulk-disable': lambda: security.cphulk_disable(),
        'enable-root':    lambda: security.enable_root(),
        'disable-root':   lambda: security.disable_root(),
        'pass-security':  lambda: security.pass_security(
                              args[0] if args else _ask('Email: '),
                              args[1] if len(args) > 1 else _ask('Password: ')),
        'sec-enable':     lambda: security.sec_enable(),
        'sec-disable':    lambda: security.sec_disable(),
        'hack':           lambda: security.hack(),
        'blocks':         lambda: security.blocks(),
        'wpsecurity':     lambda: security.wpsecurity(),

        # DNS
        'zone':           lambda: dns.zone(args[0] if args else _ask('Domain: ')),
        'checkssl':       lambda: dns.checkssl(args[0] if args else _ask('Domain: ')),
        'docroot':        lambda: dns.docroot(args[0] if args else _ask('Domain: ')),
        'lsdomains':      lambda: dns.lsdomains(),

        # DDoS
        'ddos':           lambda: ddos_mod.ddos(args[0] if args else _ask('Log file: ')),
        'bots':           lambda: ddos_mod.bots(),

        # WordPress
        'wpfunctions':    lambda: wordpress.wpfunctions(),
        'wpaudit':        lambda: wordpress.wpaudit(inp=args[0] if args else None),
        'wppluginlist':   lambda: wordpress.wppluginlist(inp=args[0] if args else None),
        'wpthemelist':    lambda: wordpress.wpthemelist(inp=args[0] if args else None),
        'wpoldplugins':   lambda: wordpress.wpoldplugins(inp=args[0] if args else None),
        'wploginurl':     lambda: wordpress.wploginurl(inp=args[0] if args else None),

        # Sessions & Access
        'cpanel-login':   lambda: sessions.cpanel_login(args[0] if args else None),
        'whm-login':      lambda: sessions.whm_login(args[0] if args else None),
        'root-whm':       lambda: sessions.root_whm_login(),
        'webmail-login':  lambda: sessions.webmail_login(),
        'shell-enable':   lambda: sessions.shell_enable(args[0] if args else None),
        'shell-disable':  lambda: sessions.shell_disable(args[0] if args else None),

        # 2FA Management
        '2fa-status':     lambda: sessions.twofactor_status(),
        '2fa-enable':     lambda: sessions.twofactor_enable(),
        '2fa-disable':    lambda: sessions.twofactor_disable(),
        '2fa-users':      lambda: sessions.twofactor_users(),
        '2fa-remove':     lambda: sessions.twofactor_remove_user(args[0] if args else None),

        # Network
        'cfallow':        lambda: network.cfallow(),
        'csf-search':     lambda: network.csf_search(args[0] if args else None),
        'csf-allow':      lambda: network.csf_allow(args[0] if args else None),
        'csf-unblock':    lambda: network.csf_unblock(args[0] if args else None),
        'ggnull':         lambda: network.ggnull(args[0] if args else _ask('IP: ')),
        'ggpaste':        lambda: network.ggpaste(
                              args[0] if args else _ask('Type: '),
                              args[1] if len(args) > 1 else None),
        'fix-license':    lambda: network.fix_license(),
        'summary':        lambda: network.run_summary_script(),
        'install-softaculous': lambda: network.install_softaculous(),

        # Misc
        'ggloop':         lambda: misc.ggloop(
                              args[0] if args else _ask('File: '),
                              args[1] if len(args) > 1 else _ask('Command (use %v): ')),
        'phpusers':       lambda: misc.phpusers(),
        'errors':         lambda: misc.errors(),
        'robotstxt':      lambda: misc.robotstxt(),
        'help':           lambda: misc.listfunctions(),
        '?':              lambda: misc.listfunctions(),
        'listfunctions':  lambda: misc.listfunctions(),
    }

    fn = cmd_map.get(cmd)
    if fn:
        try:
            fn()
        except KeyboardInterrupt:
            print()
            warn("Interrupted.")
        except Exception as e:
            warn(f"Error running '{cmd}': {e}")
    else:
        warn(f"Unknown command: '{cmd}'. Run 'help' to see available commands.")


def _ask(prompt_str):
    """Simple inline prompt for missing arguments."""
    try:
        return input(f"  {prompt_str}").strip()
    except (EOFError, KeyboardInterrupt):
        return ''
