"""Interactive terminal prompts with arrow-key selection (gh-style).

Zero external dependencies — uses ``tty``/``termios`` for raw input on
Unix systems.  Falls back to plain ``input()`` when stdin is not a TTY
or on unsupported platforms.
"""

from __future__ import annotations

import sys

# ANSI helpers
_BOLD = "\033[1m"
_CYAN = "\033[36m"
_GREEN = "\033[32m"
_DIM = "\033[2m"
_RESET = "\033[0m"
_CLEAR_LINE = "\033[2K"
_HIDE_CURSOR = "\033[?25l"
_SHOW_CURSOR = "\033[?25h"


def _is_interactive() -> bool:
    """Return True when stdin/stdout are both TTYs."""
    try:
        return sys.stdin.isatty() and sys.stdout.isatty()
    except Exception:
        return False


def _read_key() -> str:
    """Read a single keypress (or escape sequence) in raw mode."""
    import termios
    import tty

    fd = sys.stdin.fileno()
    old = termios.tcgetattr(fd)
    try:
        tty.setraw(fd)
        ch = sys.stdin.read(1)
        if ch == "\x1b":
            ch2 = sys.stdin.read(1)
            if ch2 == "[":
                ch3 = sys.stdin.read(1)
                return f"\x1b[{ch3}"
            return ch + ch2
        return ch
    finally:
        termios.tcsetattr(fd, termios.TCSADRAIN, old)


def prompt_text(label: str, default: str = "") -> str:
    """Prompt for free-form text input with a default value.

    Example output::

        ? Plugin name (My Plugin):
    """
    if not _is_interactive():
        return default

    suffix = f" ({default})" if default else ""
    try:
        answer = input(f"{_GREEN}?{_RESET} {_BOLD}{label}{_RESET}{_DIM}{suffix}{_RESET}: ").strip()
    except (EOFError, KeyboardInterrupt):
        print()
        sys.exit(1)
    return answer or default


def prompt_select(label: str, choices: list[str], default: int = 0) -> int:
    """Arrow-key selection from a list of choices.  Returns the chosen index.

    Example output::

        ? Plugin type:
        > Analysis
          Experiment Design

    Falls back to numbered input when not interactive.
    """
    if not _is_interactive():
        return default

    cursor = default

    def _render(first: bool = False) -> None:
        if not first:
            # Move cursor up to overwrite previous render
            sys.stdout.write(f"\033[{len(choices)}A")
        for i, choice in enumerate(choices):
            sys.stdout.write(_CLEAR_LINE)
            if i == cursor:
                sys.stdout.write(f"  {_CYAN}> {choice}{_RESET}\n")
            else:
                sys.stdout.write(f"    {choice}\n")
        sys.stdout.flush()

    sys.stdout.write(f"{_GREEN}?{_RESET} {_BOLD}{label}{_RESET}\n")
    sys.stdout.write(_HIDE_CURSOR)
    sys.stdout.flush()

    try:
        _render(first=True)
        while True:
            key = _read_key()
            if key == "\x1b[A":  # Up
                cursor = (cursor - 1) % len(choices)
                _render()
            elif key == "\x1b[B":  # Down
                cursor = (cursor + 1) % len(choices)
                _render()
            elif key in ("\r", "\n"):  # Enter
                break
            elif key == "\x03":  # Ctrl+C
                raise KeyboardInterrupt
    except (KeyboardInterrupt, EOFError):
        sys.stdout.write(_SHOW_CURSOR)
        sys.stdout.flush()
        print()
        sys.exit(1)

    # Replace the choice list with the final answer
    sys.stdout.write(f"\033[{len(choices)}A")
    for _ in choices:
        sys.stdout.write(f"{_CLEAR_LINE}\n")
    sys.stdout.write(f"\033[{len(choices)}A")
    sys.stdout.write(f"  {_CYAN}{choices[cursor]}{_RESET}\n")
    sys.stdout.write(_SHOW_CURSOR)
    sys.stdout.flush()

    return cursor


def prompt_multiselect(
    label: str,
    choices: list[str],
    defaults: list[int] | None = None,
) -> list[int]:
    """Arrow-key multi-select with space to toggle.  Returns selected indices.

    Example output::

        ? AI coding assistant  (space = toggle, enter = confirm)
          [x] Claude Code
          [ ] Codex (OpenAI)
          [x] Cursor
          [ ] Windsurf
          [ ] None

    Falls back to returning *defaults* when not interactive.
    """
    if not _is_interactive():
        return list(defaults) if defaults else []

    selected = set(defaults) if defaults else set()
    cursor = 0

    hint = f"{_DIM}  (space = toggle, enter = confirm){_RESET}"

    def _render(first: bool = False) -> None:
        if not first:
            sys.stdout.write(f"\033[{len(choices)}A")
        for i, choice in enumerate(choices):
            sys.stdout.write(_CLEAR_LINE)
            check = "x" if i in selected else " "
            if i == cursor:
                sys.stdout.write(f"  {_CYAN}>[{check}] {choice}{_RESET}\n")
            else:
                sys.stdout.write(f"   [{check}] {choice}\n")
        sys.stdout.flush()

    sys.stdout.write(f"{_GREEN}?{_RESET} {_BOLD}{label}{_RESET}{hint}\n")
    sys.stdout.write(_HIDE_CURSOR)
    sys.stdout.flush()

    try:
        _render(first=True)
        while True:
            key = _read_key()
            if key == "\x1b[A":  # Up
                cursor = (cursor - 1) % len(choices)
                _render()
            elif key == "\x1b[B":  # Down
                cursor = (cursor + 1) % len(choices)
                _render()
            elif key == " ":  # Space — toggle
                if cursor in selected:
                    selected.discard(cursor)
                else:
                    selected.add(cursor)
                _render()
            elif key in ("\r", "\n"):  # Enter — confirm
                break
            elif key == "\x03":  # Ctrl+C
                raise KeyboardInterrupt
    except (KeyboardInterrupt, EOFError):
        sys.stdout.write(_SHOW_CURSOR)
        sys.stdout.flush()
        print()
        sys.exit(1)

    # Collapse to summary line
    sys.stdout.write(f"\033[{len(choices)}A")
    for _ in choices:
        sys.stdout.write(f"{_CLEAR_LINE}\n")
    sys.stdout.write(f"\033[{len(choices)}A")
    picked = [choices[i] for i in sorted(selected)]
    summary = ", ".join(picked) if picked else "None"
    sys.stdout.write(f"  {_CYAN}{summary}{_RESET}\n")
    sys.stdout.write(_SHOW_CURSOR)
    sys.stdout.flush()

    return sorted(selected)


def prompt_confirm(label: str, default: bool = True) -> bool:
    """Yes/No selection with arrow keys.

    Example output::

        ? Include frontend?
        > Yes
          No
    """
    choices = ["Yes", "No"]
    idx = prompt_select(label, choices, default=0 if default else 1)
    return idx == 0
