"""
Interactive Slash Command Menu

Provides an interactive menu when user types "/" in the REPL.
Displays all available slash commands with descriptions and allows
keyboard navigation and selection.

Example:
    menu = SlashCommandMenu(BUILT_IN_COMMANDS)
    selection = menu.show()
    if selection.selected:
        command_text = f"/{selection.command}"
"""

import sys
import termios
import tty
from dataclasses import dataclass
from typing import Optional

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

from groknroll.tui.slash_commands import BUILT_IN_COMMANDS, CommandDefinition


@dataclass
class MenuSelection:
    """Result of menu interaction"""

    selected: bool  # True if user selected a command
    command: Optional[str] = None  # Selected command name (e.g., "help")
    cancelled: bool = False  # True if user pressed Esc


class SlashCommandMenu:
    """
    Interactive menu for slash commands.

    Features:
    - Keyboard navigation (↑/↓ arrows)
    - Selection with Enter
    - Cancel with Esc
    - Visual feedback with Rich

    Example:
        menu = SlashCommandMenu(BUILT_IN_COMMANDS)
        selection = menu.show()
        if selection.selected:
            return f"/{selection.command}"
    """

    def __init__(
        self,
        commands: list[CommandDefinition] | None = None,
        console: Console | None = None,
    ):
        """
        Initialize menu with available commands.

        Args:
            commands: List of command definitions (defaults to BUILT_IN_COMMANDS)
            console: Rich console for rendering
        """
        self.commands = commands if commands is not None else BUILT_IN_COMMANDS
        self.console = console or Console()
        self.selected_index: int = 0
        self.filter_text: str = ""  # Current filter input

    def _fuzzy_match(self, pattern: str, text: str) -> bool:
        """
        Check if pattern characters appear in order in text.

        Args:
            pattern: Search pattern (e.g., "exp")
            text: Text to search in (e.g., "export")

        Returns:
            True if all pattern chars appear in order in text

        Example:
            _fuzzy_match("exp", "export") -> True
            _fuzzy_match("exp", "help") -> False (no 'x')
        """
        pattern_idx = 0
        for char in text.lower():
            if pattern_idx < len(pattern) and char == pattern[pattern_idx].lower():
                pattern_idx += 1
        return pattern_idx == len(pattern)

    def _filter_commands(self, filter_text: str) -> list[CommandDefinition]:
        """
        Filter commands by fuzzy matching and return scored results.

        Args:
            filter_text: Text to filter by

        Returns:
            Filtered and sorted list of commands

        Scoring:
            - Exact prefix match on name: +100
            - Fuzzy match on name: +50
            - Match in description: +10
            - Exact prefix match on alias: +100
        """
        if not filter_text:
            return self.commands

        filtered = []

        for cmd in self.commands:
            score = 0

            # Exact prefix match (highest priority)
            if cmd.name.startswith(filter_text.lower()):
                score += 100

            # Fuzzy match on name
            elif self._fuzzy_match(filter_text, cmd.name):
                score += 50

            # Match in description
            if filter_text.lower() in cmd.description.lower():
                score += 10

            # Exact alias match
            for alias in cmd.aliases:
                if alias.startswith(filter_text.lower()):
                    score += 100
                    break

            if score > 0:
                filtered.append((score, cmd))

        # Sort by score (descending), then by name (ascending)
        filtered.sort(key=lambda x: (-x[0], x[1].name))

        return [cmd for score, cmd in filtered]

    def show(self) -> MenuSelection:
        """
        Display interactive menu and wait for user selection.

        Returns:
            MenuSelection with user's choice

        Navigation:
            ↑/↓ - Navigate through commands
            Enter - Select highlighted command
            Esc - Cancel and close menu
            Type - Filter commands in real-time
            Backspace - Remove filter characters
        """
        # Reset state
        self.selected_index = 0
        self.filter_text = ""

        # Store original terminal settings
        fd = sys.stdin.fileno()
        old_settings = termios.tcgetattr(fd)

        try:
            # Set terminal to raw mode for character-by-character input
            tty.setraw(fd)

            # Initial render
            self._render_menu()

            # Event loop
            while True:
                # Read single character
                char = sys.stdin.read(1)

                # Handle escape sequences (arrow keys)
                if char == '\x1b':  # ESC
                    # Check if it's an escape sequence or just ESC key
                    next_char = sys.stdin.read(1)
                    if next_char == '[':
                        # Arrow key sequence
                        arrow = sys.stdin.read(1)
                        if arrow == 'A':  # Up arrow
                            self._move_selection(-1)
                            self._render_menu()
                        elif arrow == 'B':  # Down arrow
                            self._move_selection(1)
                            self._render_menu()
                    else:
                        # Just ESC key - cancel
                        self._clear_menu()
                        return MenuSelection(selected=False, cancelled=True)

                # Handle Enter key
                elif char in ('\r', '\n'):  # Enter
                    filtered = self._filter_commands(self.filter_text)
                    if filtered:
                        selected_cmd = filtered[self.selected_index]
                        self._clear_menu()
                        return MenuSelection(
                            selected=True,
                            command=selected_cmd.name,
                            cancelled=False,
                        )

                # Handle Backspace/Delete
                elif char in ('\x7f', '\x08'):  # Backspace/Delete
                    if self.filter_text:
                        self.filter_text = self.filter_text[:-1]
                        self.selected_index = 0  # Reset selection
                        self._render_menu()

                # Handle Ctrl+C
                elif char == '\x03':  # Ctrl+C
                    self._clear_menu()
                    raise KeyboardInterrupt

                # Handle regular characters (filter input)
                elif char.isprintable():
                    self.filter_text += char
                    self.selected_index = 0  # Reset selection
                    self._render_menu()

        finally:
            # Restore terminal settings
            termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)

    def _move_selection(self, delta: int) -> None:
        """
        Move selection up or down.

        Args:
            delta: -1 for up, +1 for down
        """
        filtered = self._filter_commands(self.filter_text)
        if filtered:
            self.selected_index = (self.selected_index + delta) % len(filtered)

    def _render_menu(self) -> None:
        """Render menu to console"""
        # Clear previous render (move cursor up)
        if hasattr(self, '_last_render_lines'):
            # Move cursor up and clear
            self.console.print(f"\033[{self._last_render_lines}A", end="")
            self.console.print("\033[J", end="")  # Clear from cursor to end

        # Get filtered commands
        filtered = self._filter_commands(self.filter_text)

        # Create table
        table = self._create_table(filtered)

        # Build title with filter indicator
        if self.filter_text:
            filter_display = f"[yellow]'{self.filter_text}'[/yellow]"
            title = f"[bold cyan]Slash Commands[/bold cyan] {filter_display} [dim]({len(filtered)} matches)[/dim]"
        else:
            title = f"[bold cyan]Slash Commands ({len(filtered)})[/bold cyan]"

        # Build subtitle
        subtitle = "[dim]Navigation: ↑/↓ • Enter • Esc • Type to filter"
        if self.filter_text:
            subtitle += " • Backspace to clear"
        subtitle += "[/dim]"

        # Wrap in panel
        panel = Panel(
            table,
            title=title,
            subtitle=subtitle,
            border_style="blue",
            padding=(1, 2),
        )

        # Render
        self.console.print(panel)

        # Track lines for clearing later (approximate)
        self._last_render_lines = min(len(filtered) + 6, 20)

    def _create_table(self, filtered_commands: list[CommandDefinition]) -> Table:
        """
        Create Rich Table with commands.

        Args:
            filtered_commands: List of commands to display (after filtering)

        Returns:
            Rich Table with formatted command rows
        """
        table = Table(
            show_header=False,
            show_edge=False,
            padding=(0, 1),
            box=None,
            expand=True,
        )

        # Columns: Selection indicator, Command name, Description, Aliases
        table.add_column("", width=2, style="")
        table.add_column("Command", width=12, style="cyan")
        table.add_column("Description", width=40, style="white")
        table.add_column("Aliases", width=12, style="dim")

        # Handle empty results
        if not filtered_commands:
            table.add_row(
                "",
                "",
                "[dim]No matching commands[/dim]",
                "",
            )
            return table

        # Add command rows
        for i, cmd in enumerate(filtered_commands):
            is_selected = (i == self.selected_index)

            # Format row
            indicator = "[bold green]>[/bold green]" if is_selected else " "
            name_style = "bold cyan" if is_selected else "cyan"
            desc_style = "bold white" if is_selected else "white"

            # Format aliases
            aliases_str = ", ".join(cmd.aliases) if cmd.aliases else ""

            # Add row
            table.add_row(
                indicator,
                f"[{name_style}]{cmd.name}[/{name_style}]",
                f"[{desc_style}]{cmd.description}[/{desc_style}]",
                f"[dim]{aliases_str}[/dim]",
            )

        return table

    def _clear_menu(self) -> None:
        """Clear menu from screen"""
        if hasattr(self, '_last_render_lines'):
            # Move cursor up and clear
            self.console.print(f"\033[{self._last_render_lines}A", end="")
            self.console.print("\033[J", end="")  # Clear from cursor to end
