"""Terminal UI adapter used by the CLI controller.

Uses prompt_toolkit for input (history, slash command completion).
Uses rich for terminal rendering and event output.
"""

from __future__ import annotations

import getpass
from typing import Any, Protocol

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

from krim import __version__
from krim.event_handler import RichEventHandler


# -- banner --
MASCOT = [
   "                    ",
   "      ●      ●      ",
   "                    ",
   "    ▄████◣◢████▄    ",
   " ◥▄████▀▀  ▀▀████▄◤ ",
   "    ▀▀        ▀▀    ",
]

LOGO = [
    " ██╗  ██╗██████╗ ██╗███╗   ███╗",
    " ██║ ██╔╝██╔══██╗██║████╗ ████║",
    " █████╔╝ ██████╔╝██║██╔████╔██║",
    " ██╔═██╗ ██╔══██╗██║██║╚██╔╝██║",
    " ██║  ██╗██║  ██║██║██║ ╚═╝ ██║",
    " ╚═╝  ╚═╝╚═╝  ╚═╝╚═╝╚═╝     ╚═╝",
]

TAGLINE = "trust the model, keep the harness light."

SLASH_COMMANDS = ["/help", "/tokens", "/compact", "/config", "/undo", "/verbose"]

PROVIDERS = [
    ("claude", "Anthropic Claude"),
    ("openai", "OpenAI"),
    ("openrouter", "OpenRouter"),
]

MODEL_CHOICES = {
    "claude": [
        ("claude-opus-4-6", "Claude Opus 4.6 (Default)"),
        ("custom", "Custom model id"),
    ],
    "openai": [
        ("gpt-5.2", "GPT-5.2 (Default)"),
        ("custom", "Custom model id"),
    ],
    "openrouter": [
        ("minimax/minimax-m2.5", "MiniMax M2.5 (Recommended)"),
        ("z-ai/glm-5-turbo", "GLM 5 Turbo"),
        ("z-ai/glm-4.7-flash", "GLM 4.7 Flash"),
        ("custom", "Custom model id"),
    ],
}


class CliUI(Protocol):
    """UI contract for the CLI controller."""

    def select_provider(self) -> str:
        """Return the selected model provider."""

    def select_model(self, provider: str, default_model: str) -> str:
        """Return the selected model id for a provider."""

    def prompt_secret(self, label: str) -> str | None:
        """Read a hidden secret value from the user."""

    def show_header(
        self,
        *,
        interactive: bool,
        provider: str,
        model_name: str,
        max_turns: int,
        project_dir: str | None = None,
    ) -> None:
        """Render the initial CLI header."""

    def show_runtime(
        self,
        *,
        verbose: bool,
        ask_by_default: bool,
        deny_pattern_count: int,
        allow_command_count: int,
    ) -> None:
        """Render runtime state after the header."""

    def write(self, message: str = "") -> None:
        """Print a UI message."""

    def show_bye(self, *, newline: bool = False) -> None:
        """Render the interactive goodbye message."""

    def create_session(self) -> Any:
        """Create the current input session object."""

    def prompt_input(self, session: Any = None) -> str | None:
        """Read input from the active UI."""

    def create_event_handler(self, verbose: bool):
        """Create an event handler appropriate for the UI."""


class RichCliUI:
    """Rich/prompt_toolkit-backed terminal UI."""

    def __init__(self, console: Console | None = None):
        self.console = console or Console()

    def select_provider(self) -> str:
        self.console.print("\n[bold cyan]Select provider:[/]\n")
        for i, (name, desc) in enumerate(PROVIDERS, 1):
            self.console.print(f"  [bold white][{i}][/] {name} [dim]({desc})[/]")
        self.console.print()

        while True:
            try:
                max_choice = str(len(PROVIDERS))
                choice = self.console.input(f"[bold green]Enter number (1-{max_choice}): [/]").strip()
                if choice.isdigit() and 1 <= int(choice) <= len(PROVIDERS):
                    selected = PROVIDERS[int(choice) - 1][0]
                    self.console.print(f"[dim]→ {selected}[/]\n")
                    return selected
                self.console.print(f"[red]Invalid choice. Enter 1 to {max_choice}.[/]")
            except (EOFError, KeyboardInterrupt):
                self.console.print("\n[dim]cancelled[/]")
                import sys

                sys.exit(0)

    def select_model(self, provider: str, default_model: str) -> str:
        choices = MODEL_CHOICES.get(provider, [(default_model, f"{default_model} (Default)"), ("custom", "Custom model id")])

        self.console.print(f"[bold cyan]Select model for {provider}:[/]\n")
        for i, (_, desc) in enumerate(choices, 1):
            self.console.print(f"  [bold white][{i}][/] {desc}")
        self.console.print()

        while True:
            try:
                max_choice = str(len(choices))
                choice = self.console.input(f"[bold green]Enter number (1-{max_choice}): [/]").strip()
                if choice.isdigit() and 1 <= int(choice) <= len(choices):
                    model_id = choices[int(choice) - 1][0]
                    if model_id == "custom":
                        custom = self.console.input("[bold green]Enter model id: [/]").strip()
                        if custom:
                            self.console.print(f"[dim]→ {custom}[/]\n")
                            return custom
                        self.console.print("[red]Model id cannot be empty.[/]")
                        continue
                    self.console.print(f"[dim]→ {model_id}[/]\n")
                    return model_id
                self.console.print(f"[red]Invalid choice. Enter 1 to {max_choice}.[/]")
            except (EOFError, KeyboardInterrupt):
                self.console.print("\n[dim]cancelled[/]")
                import sys

                sys.exit(0)

    def prompt_secret(self, label: str) -> str | None:
        self.console.print(label)
        try:
            value = getpass.getpass("> ").strip()
        except (EOFError, KeyboardInterrupt):
            self.console.print()
            return None
        return value or None

    def show_header(
        self,
        *,
        interactive: bool,
        provider: str,
        model_name: str,
        max_turns: int,
        project_dir: str | None = None,
    ) -> None:
        if interactive:
            lines = [f"{mascot}  {logo}" for mascot, logo in zip(MASCOT, LOGO)]
            lines.append("")
            lines.append(f"  {TAGLINE}")
            lines.append("")
            lines.append(f"  {provider}/{model_name}  max_turns={max_turns}")
            if project_dir:
                lines.append(f"  config: {project_dir}")
            lines.append("  type /help for commands, 'exit' to quit")

            self.console.print(
                Panel(
                    "\n".join(lines),
                    title=f"v{__version__}",
                    title_align="left",
                    border_style="white",
                    style="white",
                )
            )
            self.console.print()
            return

        self.console.print(
            f"[bold cyan]krim[/] v{__version__}  [dim]{provider}/{model_name}  max_turns={max_turns}[/]"
        )
        if project_dir:
            self.console.print(f"[dim]config: {project_dir}[/]")

    def show_runtime(
        self,
        *,
        verbose: bool,
        ask_by_default: bool,
        deny_pattern_count: int,
        allow_command_count: int,
    ) -> None:
        if not verbose:
            return

        self.console.print(f"[dim]verbose: on | safety: {'ask' if ask_by_default else 'off'}[/]")
        self.console.print(
            f"[dim]deny_patterns: {deny_pattern_count} | allow_commands: {allow_command_count}[/]"
        )

    def write(self, message: str = "") -> None:
        self.console.print(message)

    def show_bye(self, *, newline: bool = False) -> None:
        prefix = "\n" if newline else ""
        self.console.print(f"{prefix}[dim]bye[/]")

    def create_session(self):
        try:
            from prompt_toolkit import PromptSession
            from prompt_toolkit.completion import WordCompleter
            from prompt_toolkit.history import InMemoryHistory
            from prompt_toolkit.styles import Style

            style = Style.from_dict({
                "prompt": "bold green",
            })

            completer = WordCompleter(
                SLASH_COMMANDS + ["exit", "quit"],
                sentence=True,
            )

            return PromptSession(
                history=InMemoryHistory(),
                completer=completer,
                style=style,
                complete_while_typing=False,
                enable_history_search=True,
                multiline=False,
            )
        except ImportError:
            return None

    def prompt_input(self, session=None) -> str | None:
        if session:
            try:
                from prompt_toolkit.formatted_text import HTML

                return session.prompt(HTML("<prompt>&gt; </prompt>"))
            except (EOFError, KeyboardInterrupt):
                return None

        try:
            return self.console.input("[bold green]> [/]")
        except (EOFError, KeyboardInterrupt):
            return None

    def create_event_handler(self, verbose: bool):
        return RichEventHandler(verbose=verbose)


_DEFAULT_UI = RichCliUI()


def select_provider() -> str:
    return _DEFAULT_UI.select_provider()


def print_banner(provider: str, model_name: str, max_turns: int, project_dir: str | None = None):
    _DEFAULT_UI.show_header(
        interactive=True,
        provider=provider,
        model_name=model_name,
        max_turns=max_turns,
        project_dir=project_dir,
    )


def print_banner_oneliner(provider: str, model_name: str, max_turns: int):
    _DEFAULT_UI.show_header(
        interactive=False,
        provider=provider,
        model_name=model_name,
        max_turns=max_turns,
    )


def create_session():
    return _DEFAULT_UI.create_session()


def prompt_input(session=None) -> str | None:
    return _DEFAULT_UI.prompt_input(session)
