"""Exploit Suggestions Interactive View - Complete Overhaul v2.0

This module provides an enhanced, actionable intelligence dashboard for exploit suggestions.
Features:
- Attempt tracking with visual status indicators
- Keyboard shortcuts for marking exploits
- Severity-based grouping with collapse/expand
- Actionability indicators (one-click, setup needed, manual, research)
- Quick wins section highlighting critical untried exploits
- Direct MSF execution integration
- Detailed info views with full context
"""

import os
import tempfile
from typing import Dict, List, Optional, Tuple

import click
from rich.console import Console
from rich.panel import Panel
from rich.table import Table

from souleyez.core.msf_integration import MSFConsoleManager, MSFResourceGenerator
from souleyez.storage import exploit_attempts
from souleyez.storage.hosts import HostManager
from souleyez.ui.design_system import DesignSystem

console = Console()

# Status indicators
STATUS_ICONS = {"not_tried": "⚪", "attempted": "🔄", "failed": "❌", "success": "✅"}

# Actionability indicators
ACTION_ICONS = {
    "one_click": "🎯",  # Has MSF module, ready to execute
    "setup_needed": "🔧",  # Requires configuration/parameters
    "manual": "📄",  # Manual exploitation required
    "research": "🔍",  # Needs more research
}


def _determine_actionability(exploit: Dict) -> str:
    """Determine how actionable an exploit is."""
    # One-click if has MSF module and no obvious parameters needed
    if exploit.get("msf_module"):
        # Check if it needs creds or configuration (heuristic)
        title_lower = exploit.get("title", "").lower()
        if any(
            word in title_lower for word in ["auth", "credential", "login", "password"]
        ):
            return "setup_needed"
        # Check description for complexity indicators
        desc = exploit.get("description", "").lower()
        if any(word in desc for word in ["requires", "must configure", "needs"]):
            return "setup_needed"
        return "one_click"

    # Has EDB but no MSF module - likely manual
    if exploit.get("edb_id"):
        return "manual"

    # Has CVE but no module - research needed
    if exploit.get("cve"):
        return "research"

    return "research"


def _render_status_badge(status: str) -> str:
    """Render a status badge with icon."""
    icon = STATUS_ICONS.get(status, "⚪")
    if status == "not_tried":
        return f"{icon}"
    elif status == "attempted":
        return f"[yellow]{icon}[/yellow]"
    elif status == "failed":
        return f"[red]{icon}[/red]"
    elif status == "success":
        return f"[green]{icon}[/green]"
    return icon


def _render_actionability_badge(actionability: str) -> str:
    """Render actionability badge with text label."""
    labels = {
        "one_click": "[green]READY[/green]",
        "setup_needed": "[yellow]SETUP[/yellow]",
        "manual": "[cyan]MANUAL[/cyan]",
        "research": "[dim]RESEARCH[/dim]",
    }
    return labels.get(actionability, "[dim]UNKNOWN[/dim]")


def _format_severity_badge(severity: str) -> str:
    """Format severity with color - full names, no abbreviations."""
    severity = severity or "info"
    if severity == "critical":
        return "[red bold]CRITICAL[/red bold]"
    elif severity == "high":
        return "[yellow bold]HIGH[/yellow bold]"
    elif severity == "medium":
        return "[white]MEDIUM[/white]"
    elif severity == "low":
        return "[dim]LOW[/dim]"
    else:
        return "[dim]INFO[/dim]"


def _render_exploits_table(
    exploits: List[Dict],
    width: int,
    show_all: bool = False,
    filter_untried: bool = False,
    start_index: int = 1,
    selected_exploits: set = None,
) -> None:
    """Render exploits in an enhanced table format."""
    if not exploits:
        return

    # Filter if needed
    if filter_untried:
        exploits = [e for e in exploits if e.get("attempt_status") == "not_tried"]

    if not exploits:
        console.print(f"  [dim]No untried exploits in this category[/dim]")
        return

    # Limit display if not showing all
    display_exploits = exploits if show_all else exploits[:5]

    table = Table(
        show_header=True,
        header_style="bold cyan",
        box=None,
        padding=(0, 1),
        width=min(width - 6, 140),
    )

    table.add_column("○", width=3, justify="center")  # Selection checkbox
    table.add_column(
        "ID", width=4, style="cyan"
    )  # Row ID for selection - leftmost for easy reference
    table.add_column("STATUS", width=7)  # Attempt status
    table.add_column("ACTION", width=7)  # Actionability indicator
    table.add_column("SEVERITY", width=9)
    table.add_column("EXPLOIT", width=55)
    table.add_column("CVE", width=16)
    table.add_column("SOURCE", width=7)
    table.add_column("MODULE/EDB", width=24)

    for idx, exploit in enumerate(display_exploits, start_index):
        status = exploit.get("attempt_status", "not_tried")
        actionability = _determine_actionability(exploit)
        severity = _format_severity_badge(exploit.get("severity", "info"))

        title = exploit.get("title", "Unknown")[:58]
        cve = (exploit.get("cve") or "-")[:14]

        source = exploit.get("source", "msf_kb")
        source_display = "MSF" if source == "msf_kb" else "EDB"

        # Module/EDB display with smart truncation
        if exploit.get("msf_module"):
            module_parts = exploit["msf_module"].split("/")
            module_display = (
                "/".join(module_parts[-2:])
                if len(module_parts) >= 2
                else module_parts[-1]
            )

            # Smart truncation at word boundaries
            if len(module_display) > 24:
                # Try to truncate at last underscore or slash before limit
                truncate_at = 21  # Leave room for "..."
                last_sep = max(
                    module_display.rfind("_", 0, truncate_at),
                    module_display.rfind("/", 0, truncate_at),
                )
                if last_sep > 10:  # Don't truncate too early
                    module_display = module_display[:last_sep] + "..."
                else:
                    module_display = module_display[:21] + "..."
        elif exploit.get("edb_id"):
            module_display = f"EDB-{exploit['edb_id']}"[:24]
        else:
            module_display = "-"

        # Add description as subtle hint if available
        description = exploit.get("description", "")
        if description and len(title) < 50:
            title = f"{title}\n[dim]{description[:50]}[/dim]"

        # Determine checkbox state
        exploit_id = exploit.get("identifier", "")
        checkbox = (
            "●" if (selected_exploits and exploit_id in selected_exploits) else "○"
        )

        table.add_row(
            checkbox,
            f"[cyan]{idx}[/cyan]",
            _render_status_badge(status),
            _render_actionability_badge(actionability),
            severity,
            title,
            cve,
            source_display,
            module_display,
        )

    console.print(table)

    if not show_all and len(exploits) > 5:
        console.print(
            f"  [dim]... and {len(exploits) - 5} more. Press [x] to expand.[/dim]"
        )


def _render_quick_wins(host: Dict, width: int, selected_exploits: set = None) -> None:
    """Render Quick Wins section - critical + one-click + untried."""
    all_exploits = []
    for service in host.get("services", []):
        for exploit in service.get("exploits", []):
            exploit["_service"] = service  # Track which service
            all_exploits.append(exploit)

    # Filter for quick wins
    quick_wins = [
        e
        for e in all_exploits
        if e.get("severity") == "critical"
        and e.get("attempt_status") == "not_tried"
        and _determine_actionability(e) in ["one_click", "setup_needed"]
    ]

    if not quick_wins:
        return

    # Header
    separator = "━" * (width - 4)
    console.print(f"\n  {separator}")
    console.print(
        f"  🎯 [red bold]QUICK WINS[/red bold] - [white]Critical untried exploits ready to execute[/white]"
    )
    console.print(f"  {separator}\n")

    # Table
    table = Table(
        show_header=True,
        header_style="bold red",
        box=None,
        padding=(0, 1),
        width=min(width - 6, 140),
    )

    table.add_column("○", width=3, justify="center")  # Selection checkbox
    table.add_column("ID", width=4)
    table.add_column("STATUS", width=7)
    table.add_column("SEVERITY", width=9)
    table.add_column("ACTION", width=9)
    table.add_column("SERVICE", width=15)
    table.add_column("EXPLOIT", width=45)
    table.add_column("MODULE", width=24)

    for idx, exploit in enumerate(quick_wins[:10], 1):
        status = exploit.get("attempt_status", "not_tried")
        severity = _format_severity_badge(exploit.get("severity", "info"))
        actionability = _render_actionability_badge(_determine_actionability(exploit))
        service = exploit["_service"]
        service_name = f"{service.get('service')}:{service.get('port')}"
        title = exploit.get("title", "Unknown")[:43]

        if exploit.get("msf_module"):
            module_parts = exploit["msf_module"].split("/")
            module_display = "/".join(module_parts[-2:])[:22]
        else:
            module_display = "-"

        # Determine checkbox state
        exploit_id = exploit.get("identifier", "")
        checkbox = (
            "●" if (selected_exploits and exploit_id in selected_exploits) else "○"
        )

        table.add_row(
            checkbox,
            f"[red bold]Q{idx}[/red bold]",
            _render_status_badge(status),
            severity,
            actionability,
            service_name,
            title,
            module_display,
        )

    console.print(table)
    console.print(
        f"\n  [dim]💡 Press \\[e] to execute | Full SearchSploit via menu \\[o][/dim]\n"
    )


def _render_severity_group(
    severity: str,
    exploits: List[Dict],
    width: int,
    collapsed: bool,
    filter_untried: bool = False,
) -> None:
    """Render a collapsible severity group."""
    if not exploits:
        return

    # Filter if needed
    display_exploits = exploits
    if filter_untried:
        display_exploits = [
            e for e in exploits if e.get("attempt_status") == "not_tried"
        ]

    if not display_exploits:
        return

    # Header with collapse indicator
    separator = "━" * (width - 4)
    collapse_icon = "▼" if not collapsed else "▶"

    severity_display = severity.upper()
    color = (
        "red"
        if severity == "critical"
        else (
            "yellow"
            if severity == "high"
            else "white" if severity == "medium" else "dim"
        )
    )

    console.print(f"\n  {separator}")
    console.print(
        f"  {collapse_icon} [{color} bold]{severity_display}[/{color} bold] [{color}]({len(display_exploits)} exploits)[/{color}]"
    )
    console.print(f"  {separator}\n")

    if not collapsed:
        _render_exploits_table(
            display_exploits, width, show_all=False, filter_untried=False
        )
    else:
        console.print(f"  [dim]Collapsed. Press [{severity[0]}] to expand.[/dim]\n")


def _render_service_groups(
    host: Dict,
    width: int,
    collapsed_services: set,
    filter_untried: bool,
    selected_exploits: set = None,
) -> None:
    """Render exploits grouped by service with collapse capability."""
    global_index = 1  # Track global exploit ID across all services
    services = host.get("services", [])
    services_with_exploits = [s for s in services if s.get("exploits")]

    for service in services_with_exploits:
        service_key = f"{service.get('service')}:{service.get('port')}"
        collapsed = service_key in collapsed_services

        exploits = service.get("exploits", [])
        if filter_untried:
            exploits = [e for e in exploits if e.get("attempt_status") == "not_tried"]

        if not exploits:
            continue

        # Service header
        separator = "━" * (width - 4)
        collapse_icon = "▼" if not collapsed else "▶"

        service_name = service.get("service", "unknown").upper()
        port = service.get("port", "?")
        version = service.get("version", "version unknown")

        console.print(f"\n  {separator}")
        console.print(
            f"  {collapse_icon} [bright_white bold]{service_name}:{port}[/bright_white bold] - [cyan]{version}[/cyan]"
        )
        console.print(f"  {separator}\n")

        if not collapsed:
            # Group by severity
            critical = [e for e in exploits if e.get("severity") == "critical"]
            high = [e for e in exploits if e.get("severity") == "high"]
            medium = [e for e in exploits if e.get("severity") == "medium"]
            low = [e for e in exploits if e.get("severity") == "low"]

            all_sev_exploits = critical + high + medium + low

            # Render table with current global index
            _render_exploits_table(
                all_sev_exploits,
                width,
                show_all=True,
                filter_untried=False,
                start_index=global_index,
                selected_exploits=selected_exploits,
            )

            # Increment global index for next service
            global_index += len(all_sev_exploits)
        else:
            console.print(f"  [dim]Collapsed. Click to expand.[/dim]\n")


def _render_additional_attack_vectors(host: Dict, width: int, show: bool) -> None:
    """Render generic techniques as collapsible 'Additional Attack Vectors'."""
    if not show:
        return

    services = host.get("services", [])
    technique_map = {}

    for service in services:
        service_type = service.get("service", "unknown")
        techniques = service.get("techniques", [])

        if techniques and service_type not in technique_map:
            technique_map[service_type] = {
                "port": service.get("port"),
                "techniques": techniques,
            }

    if not technique_map:
        return

    separator = "━" * (width - 4)
    console.print(f"\n  {separator}")
    console.print(
        f"  💡 [cyan bold]ADDITIONAL ATTACK VECTORS[/cyan bold] [dim](Generic Techniques)[/dim]"
    )
    console.print(f"  {separator}\n")

    for service_type, data in sorted(technique_map.items()):
        port = data["port"]
        techniques = data["techniques"]

        console.print(f"  [cyan bold]{service_type.upper()}:{port}[/cyan bold]\n")

        if techniques and isinstance(techniques[0], dict):
            table = Table(
                show_header=True,
                header_style="bold cyan",
                box=None,
                padding=(0, 1),
                width=min(width - 6, 110),
            )

            table.add_column("TECHNIQUE", width=40)
            table.add_column("SEV", width=6)
            table.add_column("MODULE", width=45)

            for tech in techniques:
                name = tech.get("name", "Unknown")[:38]
                severity = _format_severity_badge(tech.get("severity", "info"))
                modules = tech.get("msf_modules", [])
                module = modules[0].split("/")[-1][:43] if modules else "-"

                table.add_row(name, severity, module)

            console.print(table)
        else:
            techniques_str = " • ".join([str(t) for t in techniques[:5]])
            console.print(f"  {techniques_str}\n")

        console.print()


def _show_detailed_info(exploit: Dict) -> None:
    """Show detailed information view for an exploit ([i] key)."""
    DesignSystem.clear_screen()

    width = DesignSystem.get_terminal_width()

    # Header
    console.print("\n┌" + "─" * (width - 2) + "┐")
    console.print("│" + " 🔍 EXPLOIT DETAILS ".center(width - 2) + "│")
    console.print("└" + "─" * (width - 2) + "┘\n")

    # Title
    console.print(f"  [bold]{exploit.get('title', 'Unknown Exploit')}[/bold]\n")

    # Metadata
    console.print(
        f"  [cyan]Severity:[/cyan] {_format_severity_badge(exploit.get('severity', 'info'))}"
    )
    console.print(
        f"  [cyan]Actionability:[/cyan] {_render_actionability_badge(_determine_actionability(exploit))}"
    )
    console.print(
        f"  [cyan]Attempt Status:[/cyan] {_render_status_badge(exploit.get('attempt_status', 'not_tried'))}"
    )

    if exploit.get("cve"):
        console.print(f"  [cyan]CVE:[/cyan] {exploit['cve']}")

    if exploit.get("msf_module"):
        console.print(f"  [cyan]Metasploit Module:[/cyan] {exploit['msf_module']}")

    if exploit.get("edb_id"):
        console.print(f"  [cyan]Exploit-DB ID:[/cyan] {exploit['edb_id']}")

    console.print()

    # Description
    if exploit.get("description"):
        console.print(f"  [cyan bold]Description:[/cyan bold]")
        console.print(f"  {exploit['description']}\n")

    # Match type
    match_type = exploit.get("match_type", "unknown")
    console.print(f"  [cyan]Match Type:[/cyan] {match_type.upper()}\n")

    # Actionability assessment
    actionability = _determine_actionability(exploit)
    console.print(f"  [cyan bold]Actionability Assessment:[/cyan bold]")
    if actionability == "one_click":
        console.print(f"  🎯 One-Click Ready - Has MSF module, can execute immediately")
    elif actionability == "setup_needed":
        console.print(f"  🔧 Setup Needed - May require credentials or configuration")
    elif actionability == "manual":
        console.print(
            f"  📄 Manual Exploitation - Requires manual steps, check Exploit-DB"
        )
    else:
        console.print(f"  🔍 Research Required - No ready-to-use module available")

    console.print()

    # Prerequisites (with actual detection)
    console.print(f"  [cyan bold]Prerequisites:[/cyan bold]")

    # Basic requirement
    console.print(f"  • Network connectivity to target")

    # Check if MSF is available
    if exploit.get("msf_module"):
        msf = MSFConsoleManager()
        if msf.is_available():
            console.print(f"  • [green]✓[/green] Metasploit Framework installed")
        else:
            console.print(f"  • [red]✗[/red] Metasploit Framework required (not found)")

    # Check for credential requirements
    if any(
        word in exploit.get("title", "").lower()
        or word in exploit.get("description", "").lower()
        for word in ["auth", "credential", "login", "password", "authenticated"]
    ):
        console.print(f"  • Valid credentials may be required")
        console.print(f"    [dim](Check credentials database for this host)[/dim]")

    console.print()

    # Expected impact
    console.print(f"  [cyan bold]Expected Impact:[/cyan bold]")
    if exploit.get("severity") == "critical":
        console.print(f"  • Full system compromise possible")
        console.print(f"  • Remote code execution likely")
    elif exploit.get("severity") == "high":
        console.print(f"  • Significant access or information disclosure")
    else:
        console.print(f"  • Limited impact, may require chaining with other exploits")

    console.print()

    # Related URLs
    if exploit.get("cve"):
        console.print(
            f"  [cyan]NVD URL:[/cyan] https://nvd.nist.gov/vuln/detail/{exploit['cve']}"
        )
    if exploit.get("edb_id"):
        console.print(
            f"  [cyan]Exploit-DB URL:[/cyan] https://www.exploit-db.com/exploits/{exploit['edb_id']}"
        )

    console.print()
    console.print(f"  [dim]Press any key to return...[/dim]")
    click.getchar()


def _show_help_overlay() -> None:
    """Show help overlay with all keyboard shortcuts."""
    DesignSystem.clear_screen()

    width = DesignSystem.get_terminal_width()

    # Header
    console.print("\n┌" + "─" * (width - 2) + "┐")
    console.print("│" + " ⌨️  KEYBOARD SHORTCUTS ".center(width - 2) + "│")
    console.print("└" + "─" * (width - 2) + "┘\n")

    # Multi-select
    console.print(f"  [cyan bold]Multi-Select:[/cyan bold]")
    console.print(f"  [i] - Interactive select mode (arrow keys + space)")
    console.print(f"  [x] - Bulk actions menu (when exploits selected)")
    console.print(f"  [c] - Clear all selections\n")

    # Status marking
    console.print(f"  [cyan bold]Attempt Status Marking:[/cyan bold]")
    console.print(f"  [m] - Open mark menu")
    console.print(f"  [t] - Mark as tried/attempted")
    console.print(f"  [s] - Mark as successful")
    console.print(f"  [f] - Mark as failed\n")

    # Navigation
    console.print(f"  [cyan bold]Navigation:[/cyan bold]")
    console.print(f"  [1-9] - Select host by number")
    console.print(f"  [b] - Back to host list")
    console.print(f"  [r] - Refresh suggestions")
    console.print(f"  [q] - Back\n")

    # Filtering
    console.print(f"  [cyan bold]Filtering:[/cyan bold]")
    console.print(f"  [u] - Toggle untried exploits filter\n")

    # Views
    console.print(f"  [cyan bold]Views:[/cyan bold]")
    console.print(f"  [v] - Show detailed info for selected exploit")
    console.print(f"  [g] - Toggle additional attack vectors (generic techniques)\n")

    # Actions
    console.print(f"  [cyan bold]Actions:[/cyan bold]")
    console.print(f"  [e] - Execute MSF module (one-click exploits)")
    console.print(f"  [a] - Execute all critical (with confirmation)\n")

    # Help
    console.print(f"  [cyan bold]Help:[/cyan bold]")
    console.print(f"  [h] or [?] - Show this help\n")

    # Legend
    console.print(f"  [cyan bold]LEGEND:[/cyan bold]\n")

    console.print(f"  [dim]STATUS Indicators:[/dim]")
    console.print(f"    [green]✅[/green] SUCCESS   - Exploit succeeded")
    console.print(
        f"    [yellow]🔄[/yellow] TRIED     - Exploit attempted (result unknown)"
    )
    console.print(f"    [red]❌[/red] FAILED    - Exploit failed")
    console.print(f"    ⚪ NOT TRIED - Not yet attempted\n")

    console.print(f"  [dim]ACTION Types:[/dim]")
    console.print(
        f"    [green]READY[/green]    - Execute immediately (one-click, zero prep)"
    )
    console.print(
        f"    [yellow]SETUP[/yellow]    - Needs configuration first (install tools, set options, generate payload)"
    )
    console.print(
        f"    [cyan]MANUAL[/cyan]   - Requires hands-on work (can't be fully automated)"
    )
    console.print(
        f"    [dim]RESEARCH[/dim] - Needs investigation to determine if applicable\n"
    )

    console.print(f"  [dim]SEVERITY Levels:[/dim]")
    console.print(
        f"    [red bold]CRITICAL[/red bold] - Immediate exploitation, full system compromise"
    )
    console.print(
        f"    [yellow bold]HIGH[/yellow bold]     - Significant security impact, privilege escalation"
    )
    console.print(f"    [white]MEDIUM[/white]   - Moderate risk, limited access")
    console.print(f"    [dim]LOW[/dim]      - Minor security issue")
    console.print(f"    [dim]INFO[/dim]     - Informational only\n")

    console.print(f"  [dim]Press any key to return...[/dim]")
    click.getchar()


def _check_prerequisites(
    exploit: Dict, engagement_id: int, host: Dict
) -> Tuple[bool, List[str]]:
    """
    Check if exploit prerequisites are met.

    Returns:
        (prerequisites_met, missing_prerequisites)
    """
    missing = []

    # Check if exploit needs credentials
    title_lower = exploit.get("title", "").lower()
    desc_lower = exploit.get("description", "").lower()

    if any(
        word in title_lower or word in desc_lower
        for word in ["auth", "credential", "login", "password", "authenticated"]
    ):
        # Check if we have credentials for this host/service
        try:
            from souleyez.storage.credentials import CredentialsManager

            cm = CredentialsManager()
            creds = cm.list_credentials(engagement_id)

            # Look for credentials matching this host or service
            host_ip = host.get("ip")
            has_creds = any(
                cred.get("host") == host_ip
                or cred.get("source", "").startswith(host_ip)
                for cred in creds
            )

            if not has_creds:
                missing.append("Valid credentials (none found for this host)")
        except:
            missing.append("Credentials (unable to verify)")

    # Check for MSF module availability
    if exploit.get("msf_module"):
        msf = MSFConsoleManager()
        if not msf.is_available():
            missing.append("Metasploit Framework installation")

    # Check network connectivity (basic check)
    # This is a placeholder - could be enhanced with actual connectivity test

    return (len(missing) == 0, missing)


def _execute_msf_module(
    exploit: Dict, host: Dict, service: Dict, engagement_id: int
) -> bool:
    """
    Execute MSF module for an exploit.

    Returns:
        True if execution started successfully, False otherwise
    """
    msf_module = exploit.get("msf_module")
    if not msf_module:
        console.print("  [red]No MSF module available for this exploit[/red]")
        click.pause()
        return False

    # Check MSF availability
    msf_manager = MSFConsoleManager()
    if not msf_manager.is_available():
        console.print("  [red]Metasploit Framework not found[/red]")
        console.print("  Install MSF or ensure it's in your PATH")
        click.pause()
        return False

    # Check prerequisites
    prereqs_met, missing = _check_prerequisites(exploit, engagement_id, host)

    if not prereqs_met:
        console.print("  [yellow]Warning: Prerequisites may not be met:[/yellow]")
        for item in missing:
            console.print(f"    • {item}")
        console.print()

        if not click.confirm("  Continue anyway?", default=False):
            return False

    # Generate resource script
    console.print("\n  [cyan]Generating MSF resource script...[/cyan]")

    generator = MSFResourceGenerator()

    # Build resource script
    script = generator.generate_header()
    script += f"# Exploit: {exploit.get('title', 'Unknown')}\n"
    script += (
        f"# Target: {host.get('ip')} - {service.get('service')}:{service.get('port')}\n"
    )
    script += f"# Severity: {exploit.get('severity', 'unknown').upper()}\n\n"

    script += f"use {msf_module}\n"
    script += f"set RHOSTS {host.get('ip')}\n"

    if service.get("port"):
        script += f"set RPORT {service.get('port')}\n"

    # Check if module needs credentials (login/bruteforce scanners)
    login_modules = [
        "ssh_login",
        "telnet_login",
        "ftp_login",
        "smb_login",
        "mysql_login",
        "postgres_login",
        "vnc_login",
        "rdp_scanner",
        "http_login",
    ]
    enumuser_modules = ["ssh_enumusers", "smb_enumusers"]

    module_name = msf_module.split("/")[-1] if "/" in msf_module else msf_module

    if any(login_mod in module_name for login_mod in login_modules):
        # Login modules need username file and USER_AS_PASS
        script += "# Credential settings for bruteforce\n"
        script += "set USER_FILE data/wordlists/soul_users.txt\n"
        script += "set USER_AS_PASS true\n"
        script += "set STOP_ON_SUCCESS false\n"
        script += "set VERBOSE true\n"
        console.print("  [yellow]⚠️  Using USER_AS_PASS with soul_users.txt[/yellow]")
    elif any(enum_mod in module_name for enum_mod in enumuser_modules):
        # User enumeration modules need username file
        script += "# Username wordlist for enumeration\n"
        script += "set USER_FILE data/wordlists/soul_users.txt\n"
        console.print(
            "  [yellow]⚠️  Using default username wordlist (soul_users.txt)[/yellow]"
        )

    # Check if module needs payload
    if generator._module_needs_payload(msf_module):
        payload = generator._get_default_payload(msf_module)
        script += f"set PAYLOAD {payload}\n"
        script += "set LHOST 0.0.0.0  # CHANGE THIS to your IP\n"
        script += "set LPORT 4444\n"
        console.print("  [yellow]⚠️  Remember to set LHOST to your IP address![/yellow]")

    # Add check before exploit
    script += "\ncheck\n"
    script += "# Uncomment the line below to actually run the exploit:\n"
    script += "# exploit\n"

    # Show script preview
    console.print("\n  [cyan bold]Resource Script Preview:[/cyan bold]")
    console.print("  " + "─" * 60)
    for line in script.split("\n")[:15]:
        console.print(f"  {line}")
    script_lines = script.split("\n")
    if len(script_lines) > 15:
        console.print(f"  ... ({len(script_lines) - 15} more lines)")
    console.print("  " + "─" * 60)
    console.print()

    # Save script
    with tempfile.NamedTemporaryFile(mode="w", suffix=".rc", delete=False) as f:
        f.write(script)
        rc_file = f.name

    console.print(f"  [green]Resource script saved to: {rc_file}[/green]")
    console.print()

    # Ask for confirmation
    console.print("  [cyan bold]Launch Options:[/cyan bold]")
    console.print("    [1] Launch MSF console interactively (recommended)")
    console.print("    [2] Save script only (don't launch)")
    console.print("    [q] Cancel")
    console.print()

    choice = click.prompt(
        "  Select option", type=str, default="1", show_default=False
    ).strip()

    if choice == "1":
        console.print("\n  [cyan]Launching msfconsole...[/cyan]")
        console.print("  [dim]The script will load but won't auto-execute.[/dim]")
        console.print("  [dim]Review settings and type 'exploit' when ready.[/dim]\n")

        click.pause("  Press Enter to launch...")

        try:
            # Launch MSF console
            msf_manager.launch_with_resource(rc_file, background=False, use_sudo=True)

            # Mark as attempted
            exploit_attempts.record_attempt(
                engagement_id=engagement_id,
                host_id=host.get("host_id"),
                exploit_identifier=exploit.get("identifier"),
                exploit_title=exploit.get("title", "Unknown"),
                status="attempted",
                service_id=service.get("service_id"),
                notes=f"Executed via MSF module: {msf_module}",
            )

            console.print("\n  [green]✓ Exploit execution completed[/green]")
            console.print("  [dim]Status marked as 'attempted'[/dim]")

            return True

        except Exception as e:
            console.print(f"\n  [red]Error launching msfconsole: {e}[/red]")
            click.pause()
            return False
        finally:
            # Clean up temp file after some delay
            try:
                os.unlink(rc_file)
            except:
                pass

    elif choice == "2":
        # Just save the script
        save_path = click.prompt(
            "  Save as",
            default=f"exploit_{exploit.get('identifier', 'unknown').replace(':', '_')}.rc",
        )

        try:
            with open(save_path, "w") as f:
                f.write(script)
            console.print(f"\n  [green]✓ Script saved to: {save_path}[/green]")
            console.print(f"  Run with: msfconsole -r {save_path}")
            click.pause()
            return False
        except Exception as e:
            console.print(f"\n  [red]Error saving script: {e}[/red]")
            click.pause()
            return False

    else:
        # Clean up temp file
        try:
            os.unlink(rc_file)
        except:
            pass
        return False


def _batch_execute_critical(engagement_id: int, host: Dict) -> int:
    """
    Execute all critical exploits with one-click actionability.

    Returns:
        Number of exploits executed
    """
    # Get all critical one-click exploits that are untried
    all_exploits = []
    for service in host.get("services", []):
        for exploit in service.get("exploits", []):
            if (
                exploit.get("severity") == "critical"
                and exploit.get("attempt_status") == "not_tried"
                and _determine_actionability(exploit) == "one_click"
            ):
                all_exploits.append((exploit, service))

    if not all_exploits:
        console.print("  [yellow]No critical one-click untried exploits found[/yellow]")
        click.pause()
        return 0

    # Show what will be executed
    console.print(
        f"\n  [red bold]⚠️  BATCH EXECUTION - {len(all_exploits)} Exploits[/red bold]\n"
    )
    console.print("  The following critical exploits will be executed:\n")

    for idx, (exploit, service) in enumerate(all_exploits, 1):
        console.print(f"    {idx}. {exploit.get('title', 'Unknown')[:50]}")
        console.print(f"       Target: {service.get('service')}:{service.get('port')}")
        console.print()

    console.print(
        "  [yellow]This will launch MSF console for each exploit sequentially.[/yellow]"
    )
    console.print("  [yellow]Each exploit will require manual confirmation.[/yellow]\n")

    if not click.confirm("  Proceed with batch execution?", default=False):
        return 0

    # Execute each one
    executed = 0
    for idx, (exploit, service) in enumerate(all_exploits, 1):
        console.print(f"\n  [cyan]═══ Exploit {idx}/{len(all_exploits)} ═══[/cyan]")
        console.print(f"  {exploit.get('title', 'Unknown')}\n")

        if _execute_msf_module(exploit, host, service, engagement_id):
            executed += 1

        if idx < len(all_exploits):
            if not click.confirm("\n  Continue to next exploit?", default=True):
                break

    console.print(
        f"\n  [green]✓ Batch execution complete: {executed}/{len(all_exploits)} executed[/green]"
    )
    click.pause()
    return executed


def _build_exploit_index(host: Dict) -> List[Tuple[Dict, Dict]]:
    """Build a flat index of exploits with their service context for easy selection."""
    exploit_index = []
    for service in host.get("services", []):
        for exploit in service.get("exploits", []):
            exploit_index.append((exploit, service))
    return exploit_index


def _mark_exploit_interactive(engagement_id: int, host: Dict) -> bool:
    """
    Interactive exploit marking workflow.

    Returns True if a mark was made (requiring refresh), False otherwise.
    """
    # Build exploit index
    exploit_index = _build_exploit_index(host)

    if not exploit_index:
        console.print("  [yellow]No exploits available to mark[/yellow]")
        click.pause()
        return False

    console.print(f"\n  [cyan bold]Mark Exploit Attempt Status[/cyan bold]\n")
    console.print(f"  Available exploits: 1-{len(exploit_index)}")
    console.print(f"  (Row IDs from the table above)\n")

    try:
        exploit_num = click.prompt("  Enter exploit ID", type=int)

        if exploit_num < 1 or exploit_num > len(exploit_index):
            console.print(
                f"  [red]Invalid ID. Must be between 1 and {len(exploit_index)}[/red]"
            )
            click.pause()
            return False

        exploit, service = exploit_index[exploit_num - 1]

        console.print(
            f"\n  Selected: [cyan]{exploit.get('title', 'Unknown')[:60]}[/cyan]"
        )
        console.print(
            f"  Current status: {_render_status_badge(exploit.get('attempt_status', 'not_tried'))}\n"
        )

        console.print("  New status:")
        console.print("    [n] Not tried   (⚪)")
        console.print("    [t] Attempted   (🔄)")
        console.print("    [f] Failed      (❌)")
        console.print("    [s] Success     (✅)")
        console.print()

        status_choice = click.prompt("  Select status", type=str).strip().lower()

        status_map = {"n": "not_tried", "t": "attempted", "f": "failed", "s": "success"}

        if status_choice not in status_map:
            console.print("  [red]Invalid status selection[/red]")
            click.pause()
            return False

        new_status = status_map[status_choice]

        # Optional notes
        notes = click.prompt(
            "  Add notes (optional, press Enter to skip)",
            type=str,
            default="",
            show_default=False,
        )

        # Record the attempt
        host_id = host.get("host_id")
        exploit_attempts.record_attempt(
            engagement_id=engagement_id,
            host_id=host_id,
            exploit_identifier=exploit.get("identifier"),
            exploit_title=exploit.get("title", "Unknown"),
            status=new_status,
            service_id=service.get("service_id"),
            notes=notes if notes else None,
        )

        console.print(f"\n  [green]✓ Exploit marked as {new_status}[/green]")
        click.pause()
        return True

    except (ValueError, click.Abort):
        return False


def _interactive_exploit_select(host: Dict, selected_exploits: set) -> set:
    """
    Launch interactive selector for exploits.

    Args:
        host: Host dictionary with services and exploits
        selected_exploits: Set of selected exploit identifiers (modified in-place)

    Returns:
        The modified selected_exploits set
    """
    from souleyez.ui.interactive_selector import interactive_select

    # Build flat list of exploits with service context
    exploit_index = _build_exploit_index(host)

    if not exploit_index:
        console.print("  [yellow]No exploits available[/yellow]")
        click.pause()
        return selected_exploits

    # Build items for selector
    items = []
    for idx, (exploit, service) in enumerate(exploit_index, 1):
        items.append(
            {
                "idx": idx,
                "identifier": exploit.get("identifier", ""),
                "title": exploit.get("title", "Unknown")[:45],
                "severity": exploit.get("severity", "info"),
                "status": exploit.get("attempt_status", "not_tried"),
                "service": f"{service.get('service', '?')}:{service.get('port', '?')}",
                "msf_module": exploit.get("msf_module", ""),
                "actionability": _determine_actionability(exploit),
            }
        )

    # Column definitions
    columns = [
        {"name": "ID", "width": 4, "key": "idx", "justify": "right"},
        {"name": "Severity", "width": 10, "key": "severity"},
        {"name": "Status", "width": 8, "key": "status"},
        {"name": "Service", "width": 15, "key": "service"},
        {"name": "Exploit", "width": 45, "key": "title"},
    ]

    def format_cell(item: dict, key: str) -> str:
        value = item.get(key)
        if key == "severity":
            return _format_severity_badge(value)
        elif key == "status":
            return _render_status_badge(value)
        return str(value) if value else "-"

    while True:
        interactive_select(
            items=items,
            columns=columns,
            selected_ids=selected_exploits,
            get_id=lambda e: e["identifier"],
            title="SELECT EXPLOITS",
            format_cell=format_cell,
        )

        if not selected_exploits:
            return selected_exploits

        from souleyez.core.models import get_current_engagement

        current = get_current_engagement()
        if not current:
            return selected_exploits

        result = _exploit_bulk_action_menu(
            current["engagement_id"], host, selected_exploits
        )
        if result == "back":
            return selected_exploits
        elif result == "clear":
            selected_exploits.clear()


def _exploit_bulk_action_menu(
    engagement_id: int, host: Dict, selected_exploits: set
) -> str:
    """
    Show bulk action menu for selected exploits.

    Args:
        engagement_id: Current engagement ID
        host: Host dictionary
        selected_exploits: Set of selected exploit identifiers

    Returns:
        'continue' to stay in view, 'clear' to clear selection
    """
    if not selected_exploits:
        return "continue"

    # Get exploit details for selected items
    exploit_index = _build_exploit_index(host)
    selected_items = [
        (exploit, service)
        for exploit, service in exploit_index
        if exploit.get("identifier") in selected_exploits
    ]

    # Count by category
    executable_count = len(
        [
            e
            for e, s in selected_items
            if e.get("msf_module")
            and _determine_actionability(e) in ["one_click", "setup_needed"]
        ]
    )

    console.print()
    console.print(f"  [bold]Selected: {len(selected_exploits)} exploit(s)[/bold]")
    console.print("    \\[s] Mark as success")
    console.print("    \\[t] Mark as tried")
    console.print("    \\[f] Mark as failed")
    console.print("    \\[n] Mark as not tried")
    if executable_count > 0:
        console.print(f"    \\[q] Queue {executable_count} for execution")
    console.print("    \\[c] Clear selection")
    console.print("    \\[q] Back")

    try:
        action = (
            click.prompt("  Select option", default="q", show_default=False)
            .strip()
            .lower()
        )

        if action in ["s", "t", "f", "n"]:
            # Mark exploits with status
            status_map = {
                "s": "success",
                "t": "attempted",
                "f": "failed",
                "n": "not_tried",
            }
            new_status = status_map[action]

            marked = 0
            for exploit, service in selected_items:
                exploit_attempts.record_attempt(
                    engagement_id=engagement_id,
                    host_id=host.get("host_id"),
                    exploit_identifier=exploit.get("identifier"),
                    exploit_title=exploit.get("title", "Unknown"),
                    status=new_status,
                    service_id=service.get("service_id"),
                    notes=f"Bulk marked via multi-select",
                )
                marked += 1

            console.print(
                f"\n  [green]Marked {marked} exploit(s) as {new_status}[/green]"
            )
            click.pause()

            # Clear cache to force refresh
            from souleyez.intelligence.exploit_suggestions import _SUGGESTION_CACHE

            _SUGGESTION_CACHE.clear()

            return "clear"  # Clear selection after marking

        elif action == "q":
            if executable_count > 0:
                # Queue exploits for execution
                return _queue_selected_exploits(engagement_id, host, selected_items)
            else:
                # Back
                return "back"

        elif action == "c":
            selected_exploits.clear()
            console.print("\n  [dim]Selection cleared[/dim]")
            return "continue"

    except (KeyboardInterrupt, click.Abort):
        pass

    return "continue"


def _queue_selected_exploits(
    engagement_id: int, host: Dict, selected_items: List[Tuple[Dict, Dict]]
) -> str:
    """
    Queue selected exploits as background jobs for sequential execution.

    Args:
        engagement_id: Current engagement ID
        host: Host dictionary
        selected_items: List of (exploit, service) tuples

    Returns:
        'clear' to clear selection, 'continue' otherwise
    """
    from souleyez.engine.background import enqueue_job

    # Filter to executable exploits
    executable = [
        (e, s)
        for e, s in selected_items
        if e.get("msf_module")
        and _determine_actionability(e) in ["one_click", "setup_needed"]
    ]

    if not executable:
        console.print(
            "  [yellow]No executable exploits selected (need MSF module)[/yellow]"
        )
        click.pause()
        return "continue"

    # Show confirmation
    console.print(
        f"\n  [cyan]Queue {len(executable)} exploit(s) for execution?[/cyan]\n"
    )

    for idx, (exploit, service) in enumerate(executable[:5], 1):
        console.print(f"    {idx}. {exploit.get('title', 'Unknown')[:50]}")
        console.print(f"       Module: {exploit.get('msf_module', 'N/A')}")

    if len(executable) > 5:
        console.print(f"    ... and {len(executable) - 5} more")

    console.print()
    console.print(
        "  [yellow]Note: Exploits will run sequentially as background jobs[/yellow]"
    )
    console.print("  [dim]Check Job Queue to monitor progress[/dim]")
    console.print()

    if not click.confirm("  Proceed?", default=False):
        return "continue"

    # Queue each exploit as a job
    queued = 0
    first_job_id = None
    prev_job_id = None

    for exploit, service in executable:
        # Create MSF resource script content
        msf_module = exploit.get("msf_module")
        target_ip = host.get("ip")
        target_port = service.get("port")

        # Build args for MSF execution
        args = f"--module {msf_module} --rhost {target_ip}"
        if target_port:
            args += f" --rport {target_port}"

        # Queue the job with metadata
        job_id = enqueue_job(
            tool="msf_exploit",
            target=target_ip,
            args=args,
            label=f"Exploit: {exploit.get('title', 'Unknown')[:40]}",
            engagement_id=engagement_id,
            parent_id=prev_job_id if prev_job_id else None,
        )

        if first_job_id is None:
            first_job_id = job_id
        prev_job_id = job_id
        queued += 1

        # Mark as attempted
        exploit_attempts.record_attempt(
            engagement_id=engagement_id,
            host_id=host.get("host_id"),
            exploit_identifier=exploit.get("identifier"),
            exploit_title=exploit.get("title", "Unknown"),
            status="attempted",
            service_id=service.get("service_id"),
            notes=f"Queued as job #{job_id}",
        )

    console.print(f"\n  [green]Queued {queued} exploit(s) as background jobs[/green]")
    console.print(f"  [dim]Starting job ID: #{first_job_id}[/dim]")
    console.print(f"  [dim]View progress: Job Queue menu[/dim]")
    click.pause()

    # Clear cache
    from souleyez.intelligence.exploit_suggestions import _SUGGESTION_CACHE

    _SUGGESTION_CACHE.clear()

    return "clear"


def view_exploit_suggestions(engagement_id):
    """Display enhanced exploit suggestions dashboard."""
    from souleyez.intelligence.exploit_suggestions import ExploitSuggestionEngine
    from souleyez.storage.engagements import EngagementManager

    em = EngagementManager()
    engine = ExploitSuggestionEngine(
        use_searchsploit=False
    )  # Disable SearchSploit for instant loading

    current = em.get_by_id(engagement_id)
    if not current:
        click.echo(click.style("Engagement not found", fg="red"))
        click.pause()
        return

    # View state
    selected_host = None
    show_additional_vectors = False
    filter_untried = False
    collapsed_services = set()
    selected_exploits = set()  # Track selected exploit identifiers for multi-select

    while True:
        DesignSystem.clear_screen()

        # Header
        width = DesignSystem.get_terminal_width()
        console.print("\n┌" + "─" * (width - 2) + "┐")
        # Account for emoji taking 2 display characters but counting as 1 in string length
        header_text = " 💣 EXPLOIT SUGGESTIONS - INTELLIGENCE DASHBOARD "
        # Calculate padding (emoji displays as 2 chars but len counts as 1, so we need -1 adjustment)
        text_display_width = len(header_text) + 1  # +1 for emoji extra width
        padding_total = width - 2 - text_display_width
        padding_left = padding_total // 2
        padding_right = padding_total - padding_left
        console.print(
            "│" + " " * padding_left + header_text + " " * padding_right + "│"
        )
        console.print("└" + "─" * (width - 2) + "┘\n")

        # Get suggestions
        suggestions = engine.generate_suggestions(engagement_id)

        if not suggestions or not suggestions.get("hosts"):
            # No suggestions available
            hm = HostManager()
            all_hosts = hm.list_hosts(engagement_id=engagement_id)

            console.print("  [yellow]No exploit suggestions available[/yellow]\n")

            if not all_hosts:
                console.print("  💡 [cyan]Reason:[/cyan]")
                console.print("     • No hosts discovered yet\n")
                console.print("  [cyan]Try running:[/cyan]")
                console.print("     • theHarvester for subdomain discovery")
                console.print("     • nmap for network scanning")
            else:
                console.print("  💡 [cyan]Reason:[/cyan]")
                console.print(
                    f"     • {len(all_hosts)} host(s) discovered but no exploits found"
                )
                console.print(
                    "     • Services may be patched or require manual research\n"
                )

            console.print()
        else:
            if selected_host is None:
                # Summary view
                console.print(f"  Engagement: [cyan]{current['name']}[/cyan]")
                console.print(
                    f"  Filter: {'[yellow]Untried Only[/yellow]' if filter_untried else 'All Exploits'}\n"
                )

                # Summary table
                table = Table(
                    show_header=True,
                    header_style="bold cyan",
                    box=DesignSystem.TABLE_BOX,
                    padding=(0, 1),
                )
                table.add_column("#", justify="right", width=4)
                table.add_column("Host", style="cyan")
                table.add_column("Services", justify="center", width=10)
                table.add_column("Crit", justify="center", width=6, style="red bold")
                table.add_column("High", justify="center", width=6, style="yellow")
                table.add_column("Med", justify="center", width=6)
                table.add_column("Low", justify="center", width=6, style="dim")
                table.add_column(
                    "⚪", justify="center", width=4, style="dim"
                )  # Untried
                table.add_column(
                    "✅", justify="center", width=4, style="green"
                )  # Success

                for idx, host in enumerate(suggestions["hosts"], 1):
                    ip = host.get("ip", "Unknown")
                    hostname = host.get("hostname", "")
                    display_name = f"{ip} ({hostname})" if hostname else ip

                    services = host.get("services", [])
                    services_with_exploits = [s for s in services if s.get("exploits")]

                    # Count by severity and status
                    crit_count = 0
                    high_count = 0
                    med_count = 0
                    low_count = 0
                    untried_count = 0
                    success_count = 0

                    for service in services_with_exploits:
                        for exploit in service.get("exploits", []):
                            severity = exploit.get("severity", "")
                            status = exploit.get("attempt_status", "not_tried")

                            if severity == "critical":
                                crit_count += 1
                            elif severity == "high":
                                high_count += 1
                            elif severity == "medium":
                                med_count += 1
                            elif severity == "low":
                                low_count += 1

                            if status == "not_tried":
                                untried_count += 1
                            elif status == "success":
                                success_count += 1

                    table.add_row(
                        str(idx),
                        display_name,
                        str(len(services_with_exploits)),
                        str(crit_count) if crit_count > 0 else "-",
                        str(high_count) if high_count > 0 else "-",
                        str(med_count) if med_count > 0 else "-",
                        str(low_count) if low_count > 0 else "-",
                        str(untried_count) if untried_count > 0 else "-",
                        str(success_count) if success_count > 0 else "-",
                    )

                console.print(table)
                console.print()

            else:
                # Detail view for selected host
                host = suggestions["hosts"][selected_host - 1]
                ip = host.get("ip") or host.get("host") or "Unknown"
                hostname = host.get("hostname", "")
                display_name = f"{ip} ({hostname})" if hostname else ip

                console.print(f"  🎯 [cyan bold]{display_name}[/cyan bold]")
                console.print(
                    f"  Filter: {'[yellow]Untried Only[/yellow]' if filter_untried else 'All Exploits'}"
                )

                # Show selection count if any
                if selected_exploits:
                    console.print(
                        f"  [cyan bold]Selected:[/cyan bold] {len(selected_exploits)} exploit(s)  [dim](\\[x] to manage)[/dim]"
                    )
                console.print()

                # Quick Wins section
                _render_quick_wins(host, width, selected_exploits)

                # Service groups with exploits
                _render_service_groups(
                    host, width, collapsed_services, filter_untried, selected_exploits
                )

                # Additional attack vectors
                if show_additional_vectors:
                    _render_additional_attack_vectors(host, width, True)

        # Menu
        console.print()
        console.print("  " + "═" * (width - 4))
        console.print("  OPTIONS")
        console.print("  " + "─" * (width - 4))
        console.print()

        if selected_host is None:
            console.print(
                "    [cyan]\\[1-9][/cyan] Select Host - View exploit suggestions for specific target"
            )
            console.print(
                "    [cyan]\\[u][/cyan] Toggle Untried Filter - Show only unexploited vulnerabilities"
            )
            console.print(
                "    [cyan]\\[r][/cyan] Refresh - Reload exploit suggestions from database"
            )
            console.print(
                "    [cyan]\\[h][/cyan] Help - Show keyboard shortcuts and usage guide"
            )
            console.print()
            console.print("  " + "═" * (width - 4))
            console.print()
            console.print("    [cyan]\\[q][/cyan] ← Back to Main Menu")
        else:
            # Multi-select options
            if selected_exploits:
                console.print(
                    f"    [cyan]\\[x][/cyan] Bulk Actions - Manage {len(selected_exploits)} selected exploit(s)"
                )
            console.print(
                "    [cyan]\\[i][/cyan] Interactive Select - Multi-select exploits with keyboard"
            )
            console.print(
                "    [cyan]\\[c][/cyan] Clear Selection - Deselect all exploits"
            )
            console.print()
            console.print(
                "    [cyan]\\[m][/cyan] Mark Exploit - Record attempt status (tried/failed/success)"
            )
            console.print(
                "    [cyan]\\[v][/cyan] View Details - Full exploit information and prerequisites"
            )
            console.print(
                "    [cyan]\\[e][/cyan] Execute Exploit - Launch MSF module for selected exploit"
            )
            console.print(
                "    [cyan]\\[a][/cyan] Execute All Critical - Batch execute all critical exploits"
            )
            console.print(
                "    [cyan]\\[g][/cyan] Toggle Vectors - Show/hide additional attack techniques"
            )
            console.print()
            console.print(
                "    [cyan]\\[u][/cyan] Toggle Untried Filter - Show only unexploited vulnerabilities"
            )
            console.print(
                "    [cyan]\\[b][/cyan] Back to Host List - Return to target selection"
            )
            console.print(
                "    [cyan]\\[h][/cyan] Help - Show keyboard shortcuts and usage guide"
            )
            console.print()
            console.print("  " + "═" * (width - 4))
            console.print()
            console.print("    [cyan]\\[q][/cyan] ← Back to Main Menu")

        console.print()

        try:
            choice = (
                click.prompt(
                    "  Select option", type=str, default="q", show_default=False
                )
                .strip()
                .lower()
            )

            if choice == "q":
                return
            elif choice == "h" or choice == "?":
                _show_help_overlay()
            elif choice == "r":
                continue  # Refresh
            elif choice == "u":
                filter_untried = not filter_untried
            elif choice == "b" and selected_host is not None:
                selected_host = None
                collapsed_services.clear()
                selected_exploits.clear()  # Clear selection when leaving host
            elif choice == "g" and selected_host is not None:
                show_additional_vectors = not show_additional_vectors
            elif choice.isdigit() and selected_host is None:
                host_num = int(choice)
                if 1 <= host_num <= len(suggestions.get("hosts", [])):
                    selected_host = host_num
                    selected_exploits.clear()  # Clear selection when switching hosts
                else:
                    console.print("[red]Invalid host number[/red]")
                    click.pause()
            elif choice == "m" and selected_host is not None:
                # Mark exploit interactively
                host = suggestions["hosts"][selected_host - 1]
                if _mark_exploit_interactive(engagement_id, host):
                    # Refresh suggestions after marking
                    from souleyez.intelligence.exploit_suggestions import (
                        _SUGGESTION_CACHE,
                    )

                    _SUGGESTION_CACHE.clear()  # Clear cache to force refresh
            elif choice == "i" and selected_host is not None:
                # Interactive mode - launch interactive selector
                host = suggestions["hosts"][selected_host - 1]
                _interactive_exploit_select(host, selected_exploits)
            elif choice == "x" and selected_host is not None and selected_exploits:
                # Bulk actions menu
                host = suggestions["hosts"][selected_host - 1]
                result = _exploit_bulk_action_menu(
                    engagement_id, host, selected_exploits
                )
                if result == "clear":
                    selected_exploits.clear()
                    from souleyez.intelligence.exploit_suggestions import (
                        _SUGGESTION_CACHE,
                    )

                    _SUGGESTION_CACHE.clear()
            elif choice == "c" and selected_host is not None:
                # Clear selection
                selected_exploits.clear()
                console.print("  [dim]Selection cleared[/dim]")
            elif choice == "v" and selected_host is not None:
                # Show detailed info for an exploit (renamed from [i])
                host = suggestions["hosts"][selected_host - 1]
                exploit_index = _build_exploit_index(host)

                if not exploit_index:
                    console.print("  [yellow]No exploits available[/yellow]")
                    click.pause()
                    continue

                console.print(f"\n  [cyan]View Exploit Details[/cyan]")
                console.print(f"  Available exploits: 1-{len(exploit_index)}\n")

                try:
                    exploit_num = click.prompt("  Enter exploit ID", type=int)
                    if 1 <= exploit_num <= len(exploit_index):
                        exploit, _ = exploit_index[exploit_num - 1]
                        _show_detailed_info(exploit)
                    else:
                        console.print("  [red]Invalid ID[/red]")
                        click.pause()
                except (ValueError, click.Abort):
                    pass
            elif choice == "e" and selected_host is not None:
                # Execute MSF module
                host = suggestions["hosts"][selected_host - 1]
                exploit_index = _build_exploit_index(host)

                if not exploit_index:
                    console.print("  [yellow]No exploits available[/yellow]")
                    click.pause()
                    continue

                console.print(f"\n  [cyan]Execute Exploit[/cyan]")
                console.print(f"  Available exploits: 1-{len(exploit_index)}\n")

                try:
                    exploit_num = click.prompt("  Enter exploit ID", type=int)
                    if 1 <= exploit_num <= len(exploit_index):
                        exploit, service = exploit_index[exploit_num - 1]

                        if not exploit.get("msf_module"):
                            console.print(
                                "  [red]This exploit doesn't have an MSF module[/red]"
                            )
                            console.print(
                                f"  [dim]Actionability: {_render_actionability_badge(_determine_actionability(exploit))}[/dim]"
                            )
                            click.pause()
                        else:
                            if _execute_msf_module(
                                exploit, host, service, engagement_id
                            ):
                                # Refresh after execution
                                from souleyez.intelligence.exploit_suggestions import (
                                    _SUGGESTION_CACHE,
                                )

                                _SUGGESTION_CACHE.clear()
                    else:
                        console.print("  [red]Invalid ID[/red]")
                        click.pause()
                except (ValueError, click.Abort):
                    pass
            elif choice == "a" and selected_host is not None:
                # Batch execute all critical
                host = suggestions["hosts"][selected_host - 1]
                if _batch_execute_critical(engagement_id, host) > 0:
                    # Refresh after batch execution
                    from souleyez.intelligence.exploit_suggestions import (
                        _SUGGESTION_CACHE,
                    )

                    _SUGGESTION_CACHE.clear()
            else:
                console.print("[red]Invalid command[/red]")
                click.pause()

        except (KeyboardInterrupt, click.Abort):
            return
