"""Command router — parse and dispatch commands.

Handles:
- Themed alias resolution (e.g., "investigate" → "next")
- Multi-word aliases (e.g., "file report" → "complete")
- Argument extraction (e.g., "break in 42" → ("unlock", ["42"]))
- Unknown command handling
"""

from __future__ import annotations

from dataclasses import dataclass
from typing import TYPE_CHECKING

from unfamiliar.tui.views import CommandInfo

if TYPE_CHECKING:
    from unfamiliar.tui.themes import ThemeConfig


@dataclass(frozen=True)
class CommandDef:
    """Definition of a canonical command.

    Attributes:
        name: Canonical command name (e.g., "next").
        description: Brief description of what the command does.
        accepts_args: Whether this command accepts arguments.
    """

    name: str
    description: str
    accepts_args: bool = False


# Canonical command definitions
COMMANDS: tuple[CommandDef, ...] = (
    CommandDef(
        name="next",
        description="Show available missions or start one by number",
        accepts_args=True,
    ),
    CommandDef(
        name="complete",
        description="Mark current mission as complete",
        accepts_args=False,
    ),
    CommandDef(
        name="status",
        description="Show campaign overview and progress",
        accepts_args=False,
    ),
    CommandDef(
        name="briefing",
        description="Re-read the codebase briefing",
        accepts_args=False,
    ),
    CommandDef(
        name="mission",
        description="Show current or numbered mission details",
        accepts_args=True,
    ),
    CommandDef(
        name="notebook",
        description="Print notebook file path (v1.1)",
        accepts_args=False,
    ),
    CommandDef(
        name="unlock",
        description="Force unlock a locked mission by ID",
        accepts_args=True,
    ),
    CommandDef(
        name="help",
        description="Show all available commands",
        accepts_args=False,
    ),
    CommandDef(
        name="quit",
        description="End session and exit",
        accepts_args=False,
    ),
    CommandDef(
        name="end campaign",
        description="End campaign with final report",
        accepts_args=False,
    ),
    CommandDef(
        name="reopen",
        description="Reopen a closed campaign",
        accepts_args=False,
    ),
)


class CommandRouter:
    """Router for parsing and dispatching commands.

    Resolves themed aliases to canonical names and extracts arguments.

    Attributes:
        theme: Theme configuration for alias resolution.
    """

    def __init__(self, theme: ThemeConfig) -> None:
        """Initialize the command router.

        Args:
            theme: Theme configuration containing command aliases.
        """
        self.theme = theme
        self._build_alias_map()

    def _build_alias_map(self) -> None:
        """Build reverse alias map from theme."""
        # Map alias -> canonical (lowercase for case-insensitive matching)
        self._alias_to_canonical: dict[str, str] = {}
        for canonical, alias in self.theme.aliases.items():
            self._alias_to_canonical[alias.lower()] = canonical.lower()

    def parse(self, raw_input: str) -> tuple[str, list[str]]:
        """Parse raw input into canonical command and arguments.

        Args:
            raw_input: Raw user input string.

        Returns:
            Tuple of (canonical_command, args). Returns ("unknown", [raw_input])
            for unrecognized commands.

        Examples:
            >>> router.parse("investigate")
            ("next", [])
            >>> router.parse("file report")
            ("complete", [])
            >>> router.parse("break in 42")
            ("unlock", ["42"])
            >>> router.parse("  STATUS  ")
            ("status", [])
        """
        # Normalize input
        raw_input = raw_input.strip().lower()

        if not raw_input:
            return ("unknown", [""])

        # Try multi-word aliases first (longest match)
        words = raw_input.split()
        for i in range(len(words), 0, -1):
            potential_alias = " ".join(words[:i])
            if potential_alias in self._alias_to_canonical:
                canonical = self._alias_to_canonical[potential_alias]
                args = words[i:]
                return (canonical, args)

        # Try canonical names (multi-word canonicals checked longest-first)
        for i in range(len(words), 0, -1):
            potential_canonical = " ".join(words[:i])
            for cmd in COMMANDS:
                if cmd.name == potential_canonical:
                    return (cmd.name, words[i:])

        # Unknown command
        return ("unknown", [raw_input])

    def get_commands(self) -> list[CommandInfo]:
        """Get all commands with their themed aliases.

        Returns:
            List of CommandInfo for all commands, with themed aliases.
        """
        return [
            CommandInfo(
                canonical=cmd.name,
                alias=self.theme.aliases.get(cmd.name, cmd.name),
                description=cmd.description,
            )
            for cmd in COMMANDS
        ]

    def get_command_def(self, canonical: str) -> CommandDef | None:
        """Get command definition by canonical name.

        Args:
            canonical: Canonical command name.

        Returns:
            CommandDef if found, None otherwise.
        """
        for cmd in COMMANDS:
            if cmd.name == canonical:
                return cmd
        return None


__all__ = ["CommandRouter", "CommandDef", "COMMANDS"]
