"""Input handling for the CLI.

Provides readline-style keybindings and conversation history navigation
using prompt_toolkit. Based on the canonical asyncio pattern from:
https://python-prompt-toolkit.readthedocs.io/en/stable/pages/advanced_topics/asyncio.html
"""

from collections.abc import Iterable
from typing import Any

from prompt_toolkit import PromptSession
from prompt_toolkit.history import History
from prompt_toolkit.key_binding import KeyBindings
from prompt_toolkit.keys import Keys
from prompt_toolkit.styles import Style

from cadecoder.core.logging import log
from cadecoder.storage.threads import BaseThreadHistory

# Prompt styling
PROMPT_STYLE = Style.from_dict(
    {
        "prompt": "bold #00ff00",
    }
)

# Commands to exclude from history navigation
EXCLUDED_COMMANDS = frozenset(
    {
        "/help",
        "/exit",
        "/quit",
        "/clear",
        "/history",
        "/model",
        "/thread",
        "/logs",
        "/tools",
        "/skills",
        "/context",
        "/pwd",
        "/cd",
    }
)


class ThreadHistory(History):
    """prompt_toolkit History backed by thread message storage.

    Loads user messages from the database for up/down arrow navigation.
    """

    def __init__(self, thread_id: str, history_manager: BaseThreadHistory):
        super().__init__()
        self.thread_id = thread_id
        self.history_manager = history_manager
        self._history: list[str] = []

    def load_history_strings(self) -> Iterable[str]:
        """Load user messages from the database."""
        try:
            messages = self.history_manager.get_messages(self.thread_id)
            self._history = [
                msg.content
                for msg in messages
                if msg.role == "user" and msg.content and not self._is_excluded_command(msg.content)
            ]
            log.debug(f"Loaded {len(self._history)} history entries")
        except Exception as e:
            log.error(f"Failed to load history: {e}")
            self._history = []
        return self._history

    def _is_excluded_command(self, text: str) -> bool:
        """Check if text is a command we should exclude from history."""
        text_lower = text.lower().strip()
        return any(text_lower.startswith(cmd) for cmd in EXCLUDED_COMMANDS)

    def store_string(self, string: str) -> None:
        """Store a string in memory (DB storage handled elsewhere)."""
        if string and string not in self._history:
            self._history.append(string)


def create_key_bindings() -> KeyBindings:
    """Create readline-style key bindings.

    Custom bindings:
        Ctrl-A: Move cursor to beginning of line
        Ctrl-E: Move cursor to end of line
        Ctrl-D: Exit on empty buffer, or delete char under cursor
        Ctrl-K: Kill (delete) to end of line
        Ctrl-U: Kill (delete) to beginning of line
        Ctrl-W: Delete word before cursor
        Ctrl-J: Insert newline (for manual multiline input)
        Enter: Submit input (or accept completion if menu open)

    Built-in bindings (provided by prompt_toolkit):
        Up/Down: Navigate history
        Left/Right: Move cursor
        Home/End: Move to line start/end
        Ctrl-R: Reverse history search
        Ctrl-L: Clear screen
    """
    kb = KeyBindings()

    @kb.add(Keys.ControlA)
    def move_to_start(event: Any) -> None:
        """Move cursor to beginning of line."""
        event.app.current_buffer.cursor_position = 0

    @kb.add(Keys.ControlE)
    def move_to_end(event: Any) -> None:
        """Move cursor to end of line."""
        buf = event.app.current_buffer
        buf.cursor_position = len(buf.text)

    @kb.add(Keys.ControlD)
    def exit_on_empty(event: Any) -> None:
        """Exit if buffer is empty, otherwise delete char."""
        buf = event.app.current_buffer
        if not buf.text:
            event.app.exit(result="/exit")
        else:
            buf.delete()

    @kb.add(Keys.ControlK)
    def kill_to_end(event: Any) -> None:
        """Delete from cursor to end of line."""
        buf = event.app.current_buffer
        buf.text = buf.text[: buf.cursor_position]

    @kb.add(Keys.ControlU)
    def kill_to_start(event: Any) -> None:
        """Delete from cursor to beginning of line."""
        buf = event.app.current_buffer
        buf.text = buf.text[buf.cursor_position :]
        buf.cursor_position = 0

    @kb.add(Keys.ControlW)
    def delete_word(event: Any) -> None:
        """Delete word before cursor."""
        buf = event.app.current_buffer
        text = buf.text[: buf.cursor_position]
        pos = len(text.rstrip())
        while pos > 0 and text[pos - 1] not in " \t\n":
            pos -= 1
        buf.text = text[:pos] + buf.text[buf.cursor_position :]
        buf.cursor_position = pos

    @kb.add(Keys.Enter, eager=True)
    def handle_enter(event: Any) -> None:
        """Accept completion or submit input."""
        buf = event.app.current_buffer
        if buf.complete_state:
            buf.apply_completion(buf.complete_state.current_completion)
        else:
            buf.validate_and_handle()

    @kb.add(Keys.ControlJ)
    def insert_newline(event: Any) -> None:
        """Insert a newline character (for manual multiline input)."""
        event.app.current_buffer.insert_text("\n")

    return kb


def create_prompt_session(
    thread_id: str,
    history_manager: BaseThreadHistory,
) -> PromptSession:
    """Create a configured prompt session.

    Args:
        thread_id: Current thread ID for history
        history_manager: Thread history manager

    Returns:
        Configured PromptSession with keybindings and history
    """
    history = ThreadHistory(thread_id, history_manager)
    key_bindings = create_key_bindings()

    return PromptSession(
        history=history,
        key_bindings=key_bindings,
        style=PROMPT_STYLE,
        multiline=True,
        enable_history_search=True,
        mouse_support=False,
        reserve_space_for_menu=0,
        prompt_continuation="  ",
    )
