"""
Slash Commands - Built-in commands for the TUI

Provides slash command handling for the CLI:
- /help → Show help
- /new → Start new session
- /save → Save current session
- /export → Export to Markdown
- /agent <name> → Switch agent
- Autocomplete support

Examples:
    # Create command handler
    handler = SlashCommandHandler()

    # Process input
    result = handler.process("/help")
    if result.is_command:
        print(result.output)

    # Get autocomplete suggestions
    suggestions = handler.autocomplete("/ex")
    # Returns ["/export"]
"""

import re
from dataclasses import dataclass, field
from enum import Enum, auto
from typing import Any, Callable, Optional

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


class CommandType(Enum):
    """Types of slash commands"""

    HELP = auto()
    NEW = auto()
    SAVE = auto()
    EXPORT = auto()
    AGENT = auto()
    CLEAR = auto()
    QUIT = auto()
    UNKNOWN = auto()


@dataclass
class CommandResult:
    """Result of executing a slash command"""

    is_command: bool  # Whether input was a slash command
    command_type: CommandType
    success: bool
    output: str
    data: dict[str, Any] = field(default_factory=dict)

    @classmethod
    def not_a_command(cls) -> "CommandResult":
        """Create result for non-command input"""
        return cls(
            is_command=False,
            command_type=CommandType.UNKNOWN,
            success=True,
            output="",
        )

    @classmethod
    def unknown_command(cls, command: str) -> "CommandResult":
        """Create result for unknown command"""
        return cls(
            is_command=True,
            command_type=CommandType.UNKNOWN,
            success=False,
            output=f"Unknown command: {command}",
        )


@dataclass
class CommandDefinition:
    """Definition of a slash command"""

    name: str
    command_type: CommandType
    description: str
    usage: str
    aliases: list[str] = field(default_factory=list)
    requires_arg: bool = False


# Built-in command definitions
BUILT_IN_COMMANDS: list[CommandDefinition] = [
    CommandDefinition(
        name="help",
        command_type=CommandType.HELP,
        description="Show help information",
        usage="/help [command]",
        aliases=["h", "?"],
    ),
    CommandDefinition(
        name="new",
        command_type=CommandType.NEW,
        description="Start a new session",
        usage="/new [name]",
        aliases=["n"],
    ),
    CommandDefinition(
        name="save",
        command_type=CommandType.SAVE,
        description="Save the current session",
        usage="/save [name]",
        aliases=["s"],
    ),
    CommandDefinition(
        name="export",
        command_type=CommandType.EXPORT,
        description="Export session to Markdown",
        usage="/export [filename]",
        aliases=["e", "exp"],
    ),
    CommandDefinition(
        name="agent",
        command_type=CommandType.AGENT,
        description="Switch to a different agent",
        usage="/agent <name>",
        aliases=["a"],
        requires_arg=True,
    ),
    CommandDefinition(
        name="clear",
        command_type=CommandType.CLEAR,
        description="Clear the screen",
        usage="/clear",
        aliases=["c", "cls"],
    ),
    CommandDefinition(
        name="quit",
        command_type=CommandType.QUIT,
        description="Exit the application",
        usage="/quit",
        aliases=["q", "exit"],
    ),
]


@dataclass
class SlashCommandHandler:
    """
    Handler for slash commands

    Processes slash commands and provides autocomplete.
    Can be extended with custom command handlers.

    Example:
        handler = SlashCommandHandler()

        # Process command
        result = handler.process("/help")
        print(result.output)

        # With callbacks
        handler.on_new_session = lambda name: create_session(name)
        result = handler.process("/new my-session")
    """

    # Callbacks for commands that need external integration
    on_new_session: Optional[Callable[[Optional[str]], Any]] = None
    on_save_session: Optional[Callable[[Optional[str]], Any]] = None
    on_export_session: Optional[Callable[[Optional[str]], Any]] = None
    on_switch_agent: Optional[Callable[[str], Any]] = None
    on_clear: Optional[Callable[[], Any]] = None
    on_quit: Optional[Callable[[], Any]] = None

    # Available agents for /agent command
    available_agents: list[str] = field(default_factory=lambda: ["build", "plan"])

    # Command definitions
    commands: list[CommandDefinition] = field(
        default_factory=lambda: list(BUILT_IN_COMMANDS)
    )

    # Pattern for detecting slash commands
    SLASH_PATTERN: re.Pattern[str] = field(
        default=re.compile(r"^/(\w+)(?:\s+(.*))?$"), init=False
    )

    def is_slash_command(self, text: str) -> bool:
        """Check if text is a slash command"""
        return text.strip().startswith("/")

    def parse_command(self, text: str) -> tuple[Optional[str], Optional[str]]:
        """
        Parse slash command text

        Args:
            text: Input text

        Returns:
            Tuple of (command_name, argument) or (None, None)
        """
        text = text.strip()
        match = self.SLASH_PATTERN.match(text)
        if not match:
            return None, None

        command = match.group(1).lower()
        arg = match.group(2)
        if arg:
            arg = arg.strip()

        return command, arg

    def find_command(self, name: str) -> Optional[CommandDefinition]:
        """
        Find command definition by name or alias

        Args:
            name: Command name or alias

        Returns:
            CommandDefinition or None
        """
        name = name.lower()
        for cmd in self.commands:
            if cmd.name == name or name in cmd.aliases:
                return cmd
        return None

    def process(self, text: str) -> CommandResult:
        """
        Process potential slash command

        Args:
            text: Input text

        Returns:
            CommandResult with execution results
        """
        if not self.is_slash_command(text):
            return CommandResult.not_a_command()

        command_name, arg = self.parse_command(text)
        if not command_name:
            return CommandResult.unknown_command(text)

        cmd_def = self.find_command(command_name)
        if not cmd_def:
            return CommandResult.unknown_command(f"/{command_name}")

        # Check required argument
        if cmd_def.requires_arg and not arg:
            return CommandResult(
                is_command=True,
                command_type=cmd_def.command_type,
                success=False,
                output=f"Command /{cmd_def.name} requires an argument.\nUsage: {cmd_def.usage}",
            )

        # Execute command
        return self._execute_command(cmd_def, arg)

    def _execute_command(
        self, cmd_def: CommandDefinition, arg: Optional[str]
    ) -> CommandResult:
        """Execute a command"""
        handlers = {
            CommandType.HELP: self._handle_help,
            CommandType.NEW: self._handle_new,
            CommandType.SAVE: self._handle_save,
            CommandType.EXPORT: self._handle_export,
            CommandType.AGENT: self._handle_agent,
            CommandType.CLEAR: self._handle_clear,
            CommandType.QUIT: self._handle_quit,
        }

        handler = handlers.get(cmd_def.command_type)
        if handler:
            return handler(arg)

        return CommandResult.unknown_command(f"/{cmd_def.name}")

    def _handle_help(self, arg: Optional[str]) -> CommandResult:
        """Handle /help command"""
        if arg:
            # Help for specific command
            cmd_def = self.find_command(arg)
            if not cmd_def:
                return CommandResult(
                    is_command=True,
                    command_type=CommandType.HELP,
                    success=False,
                    output=f"Unknown command: {arg}",
                )

            output = self._format_command_help(cmd_def)
        else:
            # General help
            output = self._format_general_help()

        return CommandResult(
            is_command=True,
            command_type=CommandType.HELP,
            success=True,
            output=output,
        )

    def _format_command_help(self, cmd_def: CommandDefinition) -> str:
        """Format help for a specific command"""
        lines = [
            f"/{cmd_def.name} - {cmd_def.description}",
            f"Usage: {cmd_def.usage}",
        ]
        if cmd_def.aliases:
            aliases = ", ".join(f"/{a}" for a in cmd_def.aliases)
            lines.append(f"Aliases: {aliases}")
        return "\n".join(lines)

    def _format_general_help(self) -> str:
        """Format general help listing all commands"""
        lines = ["Available Commands:", ""]
        for cmd in self.commands:
            aliases = ""
            if cmd.aliases:
                aliases = f" ({', '.join('/' + a for a in cmd.aliases)})"
            lines.append(f"  /{cmd.name:<10} {cmd.description}{aliases}")
        lines.append("")
        lines.append("Type /help <command> for more details")
        return "\n".join(lines)

    def _handle_new(self, arg: Optional[str]) -> CommandResult:
        """Handle /new command"""
        session_name = arg if arg else None

        if self.on_new_session:
            try:
                result = self.on_new_session(session_name)
                return CommandResult(
                    is_command=True,
                    command_type=CommandType.NEW,
                    success=True,
                    output=f"Started new session{': ' + session_name if session_name else ''}",
                    data={"session_name": session_name, "result": result},
                )
            except Exception as e:
                return CommandResult(
                    is_command=True,
                    command_type=CommandType.NEW,
                    success=False,
                    output=f"Failed to create session: {e}",
                )

        return CommandResult(
            is_command=True,
            command_type=CommandType.NEW,
            success=True,
            output=f"New session requested{': ' + session_name if session_name else ''}",
            data={"session_name": session_name},
        )

    def _handle_save(self, arg: Optional[str]) -> CommandResult:
        """Handle /save command"""
        session_name = arg if arg else None

        if self.on_save_session:
            try:
                result = self.on_save_session(session_name)
                return CommandResult(
                    is_command=True,
                    command_type=CommandType.SAVE,
                    success=True,
                    output=f"Session saved{': ' + session_name if session_name else ''}",
                    data={"session_name": session_name, "result": result},
                )
            except Exception as e:
                return CommandResult(
                    is_command=True,
                    command_type=CommandType.SAVE,
                    success=False,
                    output=f"Failed to save session: {e}",
                )

        return CommandResult(
            is_command=True,
            command_type=CommandType.SAVE,
            success=True,
            output=f"Save requested{': ' + session_name if session_name else ''}",
            data={"session_name": session_name},
        )

    def _handle_export(self, arg: Optional[str]) -> CommandResult:
        """Handle /export command"""
        filename = arg if arg else None

        if self.on_export_session:
            try:
                result = self.on_export_session(filename)
                return CommandResult(
                    is_command=True,
                    command_type=CommandType.EXPORT,
                    success=True,
                    output=f"Session exported{': ' + filename if filename else ''}",
                    data={"filename": filename, "result": result},
                )
            except Exception as e:
                return CommandResult(
                    is_command=True,
                    command_type=CommandType.EXPORT,
                    success=False,
                    output=f"Failed to export session: {e}",
                )

        return CommandResult(
            is_command=True,
            command_type=CommandType.EXPORT,
            success=True,
            output=f"Export requested{': ' + filename if filename else ''}",
            data={"filename": filename},
        )

    def _handle_agent(self, arg: Optional[str]) -> CommandResult:
        """Handle /agent command"""
        if not arg:
            return CommandResult(
                is_command=True,
                command_type=CommandType.AGENT,
                success=False,
                output="Usage: /agent <name>\nAvailable agents: "
                + ", ".join(self.available_agents),
            )

        agent_name = arg.lower()
        if agent_name not in self.available_agents:
            return CommandResult(
                is_command=True,
                command_type=CommandType.AGENT,
                success=False,
                output=f"Unknown agent: {agent_name}\nAvailable agents: "
                + ", ".join(self.available_agents),
            )

        if self.on_switch_agent:
            try:
                result = self.on_switch_agent(agent_name)
                return CommandResult(
                    is_command=True,
                    command_type=CommandType.AGENT,
                    success=True,
                    output=f"Switched to agent: {agent_name}",
                    data={"agent": agent_name, "result": result},
                )
            except Exception as e:
                return CommandResult(
                    is_command=True,
                    command_type=CommandType.AGENT,
                    success=False,
                    output=f"Failed to switch agent: {e}",
                )

        return CommandResult(
            is_command=True,
            command_type=CommandType.AGENT,
            success=True,
            output=f"Switched to agent: {agent_name}",
            data={"agent": agent_name},
        )

    def _handle_clear(self, arg: Optional[str]) -> CommandResult:
        """Handle /clear command"""
        if self.on_clear:
            try:
                self.on_clear()
            except Exception as e:
                return CommandResult(
                    is_command=True,
                    command_type=CommandType.CLEAR,
                    success=False,
                    output=f"Failed to clear: {e}",
                )

        return CommandResult(
            is_command=True,
            command_type=CommandType.CLEAR,
            success=True,
            output="",  # Clear typically produces no output
            data={"action": "clear"},
        )

    def _handle_quit(self, arg: Optional[str]) -> CommandResult:
        """Handle /quit command"""
        if self.on_quit:
            try:
                self.on_quit()
            except Exception as e:
                return CommandResult(
                    is_command=True,
                    command_type=CommandType.QUIT,
                    success=False,
                    output=f"Failed to quit: {e}",
                )

        return CommandResult(
            is_command=True,
            command_type=CommandType.QUIT,
            success=True,
            output="Goodbye!",
            data={"action": "quit"},
        )

    def autocomplete(self, text: str) -> list[str]:
        """
        Get autocomplete suggestions for partial command

        Args:
            text: Partial input text

        Returns:
            List of matching command strings
        """
        if not text.startswith("/"):
            return []

        # Extract the partial command
        partial = text[1:].lower()

        # If there's a space, autocomplete argument
        if " " in text:
            cmd_part, arg_part = text.split(" ", 1)
            cmd_name = cmd_part[1:].lower()
            cmd_def = self.find_command(cmd_name)

            if cmd_def and cmd_def.command_type == CommandType.AGENT:
                # Autocomplete agent names
                return [
                    f"/{cmd_def.name} {agent}"
                    for agent in self.available_agents
                    if agent.startswith(arg_part.lower())
                ]
            return []

        # Autocomplete command names
        suggestions = []
        for cmd in self.commands:
            if cmd.name.startswith(partial):
                suggestions.append(f"/{cmd.name}")
            for alias in cmd.aliases:
                if alias.startswith(partial):
                    suggestions.append(f"/{alias}")

        return sorted(set(suggestions))

    def get_all_commands(self) -> list[str]:
        """Get all command names including aliases"""
        commands = []
        for cmd in self.commands:
            commands.append(f"/{cmd.name}")
            for alias in cmd.aliases:
                commands.append(f"/{alias}")
        return sorted(set(commands))

    def format_help_panel(self, console: Optional[Console] = None) -> Panel:
        """
        Format help as a Rich panel

        Args:
            console: Optional Rich console

        Returns:
            Rich Panel with help table
        """
        table = Table(title="Slash Commands", show_header=True, header_style="bold")
        table.add_column("Command", style="cyan")
        table.add_column("Description")
        table.add_column("Aliases", style="dim")

        for cmd in self.commands:
            aliases = ", ".join(f"/{a}" for a in cmd.aliases) if cmd.aliases else "-"
            table.add_row(f"/{cmd.name}", cmd.description, aliases)

        return Panel(table, title="Help", border_style="blue")


# Convenience functions


def is_slash_command(text: str) -> bool:
    """Check if text is a slash command"""
    return text.strip().startswith("/")


def process_slash_command(
    text: str,
    handler: Optional[SlashCommandHandler] = None,
) -> CommandResult:
    """
    Process a slash command

    Args:
        text: Input text
        handler: Optional handler (creates default if not provided)

    Returns:
        CommandResult
    """
    if handler is None:
        handler = SlashCommandHandler()
    return handler.process(text)


def get_slash_completions(
    text: str,
    handler: Optional[SlashCommandHandler] = None,
) -> list[str]:
    """
    Get autocomplete suggestions

    Args:
        text: Partial input
        handler: Optional handler

    Returns:
        List of suggestions
    """
    if handler is None:
        handler = SlashCommandHandler()
    return handler.autocomplete(text)
