import re
from pathlib import Path
from typing import TYPE_CHECKING, ClassVar, cast

from textual.binding import Binding, BindingType
from textual.events import Click
from textual.message import Message
from textual.widgets import TextArea

if TYPE_CHECKING:
    pass

try:
    import pyperclip
except ImportError:
    pyperclip = None


class InputController(TextArea):
    """
    Multi-line input widget with history and mode toggling.
    Supports Shift+Enter for newlines, Enter to submit.
    """

    BINDINGS: ClassVar[list[BindingType]] = [
        Binding("enter", "submit", "Submit", priority=True),
        Binding("shift+enter", "newline", "New Line", priority=True),
        Binding("ctrl+shift+c", "copy_selection", "Copy", priority=True),
    ]

    class Submitted(Message):
        """Sent when user submits input."""

        def __init__(self, value: str):
            self.value = value
            super().__init__()

    class Toggled(Message):
        """Sent when input mode is toggled."""

        def __init__(self, mode: str):
            self.mode = mode
            super().__init__()

    def __init__(self, placeholder: str = "", **kwargs):
        # TextArea doesn't have placeholder, but we store it for reference
        super().__init__(**kwargs)
        self._placeholder = placeholder
        self.cmd_history: list[str] = []
        self.cmd_history_index: int = -1
        self.current_input: str = ""
        self.mode: str = "CLI"  # "CLI" or "AI"
        self.show_line_numbers = False
        self.tab_behavior = "indent"
        # Use a dark syntax theme that matches our UI
        self.theme = "vscode_dark"

        # Apply cursor settings from config
        self._apply_cursor_settings()

    def _apply_cursor_settings(self) -> None:
        """Apply cursor settings from config."""
        try:
            from settings import get_settings

            settings = get_settings()
            self.cursor_blink = settings.terminal.cursor_blink

            # Apply cursor style class for CSS styling
            cursor_style = settings.terminal.cursor_style
            # Remove any existing cursor style classes
            self.remove_class("cursor-block", "cursor-beam", "cursor-underline")
            # Add the appropriate class
            self.add_class(f"cursor-{cursor_style}")
        except Exception:
            pass  # Use default if settings unavailable

    @property
    def value(self) -> str:
        """Compatibility property for Input-like API."""
        return self.text

    @value.setter
    def value(self, new_value: str):
        """Compatibility setter for Input-like API."""
        self.text = new_value

    @property
    def placeholder(self) -> str:
        return self._placeholder

    @placeholder.setter
    def placeholder(self, value: str):
        self._placeholder = value

    @property
    def is_ai_mode(self) -> bool:
        return self.mode == "AI"

    def action_submit(self):
        """Handle Enter key - submit input or select from suggester."""
        # Check if suggester is visible and has selection
        try:
            from widgets.suggester import CommandSuggester
            suggester = cast(CommandSuggester, self.app.query_one("CommandSuggester"))
            if suggester.display and self.text.startswith("/"):
                complete = suggester.get_selected()
                if complete:
                    parts = self.text.split(" ")
                    if len(parts) == 1:
                        # Execute the completed command directly
                        self.text = complete
                    else:
                        # Complete argument and execute
                        self.text = parts[0] + " " + complete
                    suggester.display = False
                    # Fall through to execute the command
        except Exception:
            pass

        text = self.text.strip()
        if text:
            self.post_message(self.Submitted(text))

    def action_newline(self):
        """Handle Shift+Enter - insert newline."""
        self.insert("\n")

    def add_to_history(self, command: str):
        if command and (not self.cmd_history or self.cmd_history[-1] != command):
            self.cmd_history.append(command)
        self.cmd_history_index = -1

    def action_history_up(self):
        if not self.cmd_history:
            return

        if self.cmd_history_index == -1:
            self.current_input = self.text
            self.cmd_history_index = len(self.cmd_history) - 1
        elif self.cmd_history_index > 0:
            self.cmd_history_index -= 1

        self.text = self.cmd_history[self.cmd_history_index]
        # Move cursor to end
        self.move_cursor((len(self.text), 0))

    def action_history_down(self):
        if self.cmd_history_index == -1:
            return

        if self.cmd_history_index < len(self.cmd_history) - 1:
            self.cmd_history_index += 1
            self.text = self.cmd_history[self.cmd_history_index]
        else:
            self.cmd_history_index = -1
            self.text = self.current_input

        # Move cursor to end
        self.move_cursor((len(self.text), 0))

    def toggle_mode(self):
        if self.mode == "CLI":
            self.mode = "AI"
            self.add_class("ai-mode")
            self._placeholder = "Ask AI..."
        else:
            self.mode = "CLI"
            self.remove_class("ai-mode")
            self._placeholder = "Type a command..."
        self.post_message(self.Toggled(self.mode))

    def _get_current_word(self) -> str:
        """Get the word at the current cursor position."""
        text = self.text
        words = text.split()
        if not words:
            return ""
        return words[-1]

    def _is_path_context(self) -> bool:
        """Check if current word looks like a path."""
        last_word = self._get_current_word()
        if not last_word:
            return False
        # Detect: /path, ./path, ../path, ~/path
        return bool(re.match(r"^[~./]|^\.\./", last_word))

    def _get_path_completions(self, partial: str) -> list[str]:
        """Get filesystem completions for partial path."""
        try:
            # Handle home directory
            if partial.startswith("~"):
                base = Path.home()
                if len(partial) > 1 and partial[1] == "/":
                    partial = partial[2:]
                else:
                    partial = partial[1:]
                full_path = base / partial if partial else base
            elif partial.startswith("/"):
                base = Path("/")
                partial = partial[1:]
                full_path = base / partial if partial else base
            else:
                base = Path.cwd()
                full_path = base / partial if partial else base

            # Get parent and pattern
            if full_path.exists() and full_path.is_dir():
                parent = full_path
                pattern = "*"
            else:
                parent = full_path.parent
                pattern = full_path.name + "*"

            if not parent.exists():
                return []

            matches = []
            for p in parent.glob(pattern):
                # Get relative path from current dir or show full if needed
                try:
                    if partial.startswith("~"):
                        rel = "~/" + str(p.relative_to(Path.home()))
                    elif partial.startswith("/"):
                        rel = str(p)
                    else:
                        rel = str(p.relative_to(Path.cwd()))
                except ValueError:
                    rel = str(p)

                if p.is_dir():
                    rel += "/"
                matches.append(rel)

            return sorted(matches)[:10]
        except Exception:
            return []

    def _complete_path(self, completions: list[str]):
        """Complete the current path with the given completion."""
        if not completions:
            return

        text = self.text
        words = text.split()

        if len(words) == 0:
            return

        if len(completions) == 1:
            # Single match - complete it
            words[-1] = completions[0]
            self.text = " ".join(words)
            self.move_cursor((len(self.text), 0))
        else:
            # Multiple matches - find common prefix
            common = completions[0]
            for c in completions[1:]:
                while not c.startswith(common) and common:
                    common = common[:-1]
            if common and len(common) > len(words[-1]):
                words[-1] = common
                self.text = " ".join(words)
                self.move_cursor((len(self.text), 0))

    def _on_cursor_first_line(self) -> bool:
        """Check if cursor is on first line."""
        row, _ = self.cursor_location
        return row == 0

    def _on_cursor_last_line(self) -> bool:
        """Check if cursor is on last line."""
        row, _ = self.cursor_location
        return row >= self.document.line_count - 1

    async def on_key(self, event):
        # Handle navigation keys manually to support both Suggester and History
        from widgets.suggester import CommandSuggester
        if event.key == "up":
            if self.text.startswith("/"):
                suggester = cast(CommandSuggester, self.app.query_one("CommandSuggester"))
                if suggester.display:
                    suggester.select_prev()
                    event.stop()
                    return
            # Only navigate history if on first line
            if self._on_cursor_first_line():
                self.action_history_up()
                event.stop()

        elif event.key == "down":
            if self.text.startswith("/"):
                suggester = cast(CommandSuggester, self.app.query_one("CommandSuggester"))
                if suggester.display:
                    suggester.select_next()
                    event.stop()
                    return
            # Only navigate history if on last line
            if self._on_cursor_last_line():
                self.action_history_down()
                event.stop()

        elif event.key == "tab":
            # First check slash commands
            if self.text.startswith("/"):
                suggester = cast(CommandSuggester, self.app.query_one("CommandSuggester"))
                if suggester.display:
                    complete = suggester.get_selected()
                    if complete:
                        parts = self.text.split(" ")
                        if len(parts) == 1:
                            self.text = complete + " "
                        else:
                            self.text = " ".join(parts[:-1]) + " " + complete
                        suggester.display = False
                        self.move_cursor((len(self.text), 0))
                        event.stop()
                        return
            # Then check path completion (only in CLI mode)
            elif not self.is_ai_mode and self._is_path_context():
                partial = self._get_current_word()
                completions = self._get_path_completions(partial)
                if completions:
                    self._complete_path(completions)
                    event.stop()
                    return

        elif event.key == "escape":
            suggester = cast(CommandSuggester, self.app.query_one("CommandSuggester"))
            if suggester.display:
                suggester.display = False
                event.stop()

    def on_click(self, event: Click) -> None:
        """Handle mouse clicks - right-click to paste."""
        # Right-click (button 3) to paste from clipboard
        if event.button == 3:
            event.stop()
            self._paste_from_clipboard()
            return
        # Let parent handle other clicks (left-click for positioning, etc.)

    def _paste_from_clipboard(self) -> None:
        """Paste content from clipboard at cursor position."""
        try:
            if pyperclip:
                content = pyperclip.paste()
                if content:
                    self.insert(content)
            else:
                # Fallback to xclip on Linux
                import subprocess

                try:
                    result = subprocess.run(
                        ["xclip", "-selection", "clipboard", "-o"],
                        capture_output=True,
                        text=True,
                        timeout=1,
                    )
                    if result.returncode == 0 and result.stdout:
                        self.insert(result.stdout)
                except FileNotFoundError:
                    self.notify(
                        "Install pyperclip for clipboard support", severity="warning"
                    )
        except Exception:
            pass

    def action_copy_selection(self) -> None:
        """Copy selected text to clipboard (Ctrl+Shift+C)."""
        selected = self.selected_text
        if selected:
            self._copy_to_clipboard(selected)

    def _copy_to_clipboard(self, text: str) -> None:
        """Copy text to system clipboard."""
        try:
            # Try OSC 52 first (works over SSH, in supporting terminals)
            self.app.copy_to_clipboard(text)
        except Exception:
            pass

        # Also use pyperclip for broader compatibility
        try:
            if pyperclip:
                pyperclip.copy(text)
            else:
                # Fallback to xclip on Linux
                import subprocess

                try:
                    process = subprocess.Popen(
                        ["xclip", "-selection", "clipboard"], stdin=subprocess.PIPE
                    )
                    process.communicate(text.encode("utf-8"), timeout=1)
                except FileNotFoundError:
                    pass
        except Exception:
            pass

    def on_text_area_selection_changed(self, event: TextArea.SelectionChanged) -> None:
        """Auto-copy text when selection is made (copy-on-highlight)."""
        # Only copy if there's an actual selection (not just cursor movement)
        if event.selection.start != event.selection.end:
            selected = self.selected_text
            if selected:
                self._copy_to_clipboard(selected)
