"""Interview widget for interactive user questioning in TUI.

Renders an interactive multi-step questionnaire with:
- Tab bar navigation between questions
- Multiple-choice option selection with descriptions
- Free text input via Textual Input widget (supports paste, IME, cursor movement)
- Keyboard navigation (arrows, tab, enter, esc)
"""

from __future__ import annotations

import asyncio
from typing import Any, ClassVar

from textual import events
from textual.app import ComposeResult
from textual.binding import Binding, BindingType
from textual.containers import Container, Vertical
from textual.message import Message
from textual.widgets import Input, Static

from ieum.i18n import t


class InterviewWidget(Container):
    """Interactive interview widget for asking user questions.

    Displays questions as tabs with selectable options.
    Inspired by Claude Code's AskUserQuestion pattern.

    Layout:
        ┌────────────────────────────────────────────────────┐
        │ ← □ Tab1  □ Tab2  □ Tab3  ✔ Submit →              │
        ├────────────────────────────────────────────────────┤
        │ Question text?                                     │
        │ › 1. Option A                                      │
        │        Description of option A                     │
        │   2. Option B                                      │
        │        Description of option B                     │
        │   3. Type something.                               │
        ├────────────────────────────────────────────────────┤
        │ Enter select · Tab/Arrow navigate · Esc cancel     │
        └────────────────────────────────────────────────────┘
    """

    can_focus = True
    can_focus_children = True

    BINDINGS: ClassVar[list[BindingType]] = [
        Binding("up", "move_up", "Up", show=False),
        Binding("k", "move_up", "Up", show=False),
        Binding("down", "move_down", "Down", show=False),
        Binding("j", "move_down", "Down", show=False),
        Binding("enter", "select_option", "Select", show=False),
        Binding("tab", "next_tab", "Next Tab", show=False),
        Binding("shift+tab", "prev_tab", "Previous Tab", show=False),
        Binding("right", "next_tab", "Next", show=False),
        Binding("left", "prev_tab", "Previous", show=False),
        Binding("escape", "cancel", "Cancel", show=False),
    ]

    class Answered(Message):
        """Message sent when user completes the interview."""

        def __init__(self, answers: dict[str, Any]) -> None:
            super().__init__()
            self.answers = answers

    class Cancelled(Message):
        """Message sent when user cancels the interview."""

    def __init__(
        self,
        questions: list[dict[str, Any]],
        id: str | None = None,  # noqa: A002
        **kwargs: Any,
    ) -> None:
        super().__init__(id=id or "interview-widget", classes="interview-widget", **kwargs)
        self._questions = questions
        self._current_tab = 0
        self._current_option = 0
        self._answers: dict[int, str | list[str]] = {}  # question_idx -> answer
        self._custom_text_mode = False
        self._custom_text_input: Input | None = None  # Textual Input widget for free text
        self._future: asyncio.Future[dict[str, Any]] | None = None

        # Tab count = questions + submit tab
        self._tab_count = len(questions) + 1

        # Widgets
        self._tab_bar: Static | None = None
        self._question_text: Static | None = None
        self._options_container: Vertical | None = None
        self._help_text: Static | None = None
        self._option_widgets: list[Static] = []

    def set_future(self, future: asyncio.Future[dict[str, Any]]) -> None:
        """Set the future to resolve when user submits answers."""
        self._future = future

    @property
    def _current_question(self) -> dict[str, Any] | None:
        """Get the current question data."""
        if 0 <= self._current_tab < len(self._questions):
            return self._questions[self._current_tab]
        return None

    @property
    def _is_submit_tab(self) -> bool:
        """Check if current tab is the Submit tab."""
        return self._current_tab >= len(self._questions)

    @property
    def _option_count(self) -> int:
        """Number of options for current question (including free text)."""
        q = self._current_question
        if q is None:
            return 0
        return len(q.get("options", [])) + 1  # +1 for free text

    def compose(self) -> ComposeResult:
        """Compose the interview widget."""
        # Title
        yield Static(t("interview.title"), classes="interview-title")

        # Tab bar
        self._tab_bar = Static("", classes="interview-tab-bar")
        yield self._tab_bar

        # Separator
        yield Static("─" * 50, classes="interview-separator")

        # Question area
        self._question_text = Static("", classes="interview-question")
        yield self._question_text

        # Options container
        self._options_container = Vertical(classes="interview-options")
        yield self._options_container

        # Separator
        yield Static("─" * 50, classes="interview-separator")

        # Help text (Cancel = abort entire interview)
        self._help_text = Static(
            t("interview.help"),
            classes="interview-help",
        )
        yield self._help_text

    async def on_mount(self) -> None:
        """Initialize and render the first question."""
        await self._render_tab_bar()
        await self._render_question()
        self.focus()

    async def _render_tab_bar(self) -> None:
        """Render the tab bar showing all question headers."""
        if not self._tab_bar:
            return

        parts = ["← "]
        for i, q in enumerate(self._questions):
            header = q.get("header", f"Q{i + 1}")
            # Check if this question has been answered
            if i in self._answers:
                marker = "■"
            elif i == self._current_tab:
                marker = "▶"
            else:
                marker = "□"

            if i == self._current_tab:
                parts.append(f"[bold reverse] {marker} {header} [/bold reverse]")
            else:
                parts.append(f" {marker} {header} ")

            if i < len(self._questions) - 1:
                parts.append("│")

        # Submit tab
        parts.append(" │ ")
        submit_label = t("interview.submit_tab")
        if self._is_submit_tab:
            parts.append(f"[bold reverse] ✔ {submit_label} [/bold reverse]")
        else:
            parts.append(f" ✔ {submit_label} ")
        parts.append(" →")

        self._tab_bar.update("".join(parts))

    async def _render_question(self) -> None:
        """Render the current question and its options."""
        if not self._question_text or not self._options_container:
            return

        # Clear existing options
        self._option_widgets.clear()
        await self._options_container.remove_children()

        if self._is_submit_tab:
            # Show summary of answers
            await self._render_submit_tab()
            return

        q = self._current_question
        if q is None:
            return

        # Question text
        self._question_text.update(f"[bold]{q['question']}[/bold]")

        # Options
        options = q.get("options", [])
        for i, opt in enumerate(options):
            widget = Static("", classes="interview-option")
            self._option_widgets.append(widget)
            await self._options_container.mount(widget)

        # Free text option
        free_text_widget = Static("", classes="interview-option")
        self._option_widgets.append(free_text_widget)
        await self._options_container.mount(free_text_widget)

        self._update_options()

    async def _render_submit_tab(self) -> None:
        """Render the submit tab with answer summary."""
        if not self._question_text or not self._options_container:
            return

        self._question_text.update(f"[bold]{t('interview.submit_prompt')}[/bold]")

        for i, q in enumerate(self._questions):
            header = q.get("header", f"Q{i + 1}")
            answer = self._answers.get(i, t("interview.unanswered"))
            if isinstance(answer, list):
                answer_str = ", ".join(answer)
            else:
                answer_str = str(answer)

            widget = Static(
                f"  [bold]{header}[/bold]: {answer_str}",
                classes="interview-option",
            )
            self._option_widgets.append(widget)
            await self._options_container.mount(widget)

    def _update_options(self) -> None:
        """Update option display based on current selection."""
        q = self._current_question
        if q is None or self._is_submit_tab:
            return

        options = q.get("options", [])
        is_multi = q.get("multi_select", False)
        current_answer = self._answers.get(self._current_tab)

        for i, opt in enumerate(options):
            if i >= len(self._option_widgets):
                break

            label = opt.get("label", "")
            description = opt.get("description", "")

            # Check if this option is selected (for multi-select)
            is_selected = False
            if is_multi and isinstance(current_answer, list):
                is_selected = label in current_answer
            elif not is_multi and current_answer == label:
                is_selected = True

            cursor = "› " if i == self._current_option else "  "
            check = "✓ " if is_selected else ""

            text = f"{cursor}{i + 1}. {check}{label}"
            if description:
                text += f"\n        {description}"

            self._option_widgets[i].update(text)

            # Update highlight class
            self._option_widgets[i].remove_class("interview-option-selected")
            if i == self._current_option:
                self._option_widgets[i].add_class("interview-option-selected")

        # Free text option (last widget)
        free_text_idx = len(options)
        if free_text_idx < len(self._option_widgets):
            cursor = "› " if self._current_option == free_text_idx else "  "
            if self._custom_text_mode:
                # Input widget is mounted separately; show label only
                text = f"{cursor}{free_text_idx + 1}. [italic]{t('interview.custom_input_active')}[/italic]"
            else:
                text = f"{cursor}{free_text_idx + 1}. {t('interview.custom_input_label')}"

            self._option_widgets[free_text_idx].update(text)
            self._option_widgets[free_text_idx].remove_class("interview-option-selected")
            if self._current_option == free_text_idx:
                self._option_widgets[free_text_idx].add_class("interview-option-selected")

    async def action_move_up(self) -> None:
        """Move option selection up."""
        if self._is_submit_tab or self._custom_text_mode:
            return
        self._current_option = (self._current_option - 1) % self._option_count
        self._update_options()

    async def action_move_down(self) -> None:
        """Move option selection down."""
        if self._is_submit_tab or self._custom_text_mode:
            return
        self._current_option = (self._current_option + 1) % self._option_count
        self._update_options()

    async def action_next_tab(self) -> None:
        """Move to next question tab."""
        if self._custom_text_mode:
            await self._exit_custom_text_mode()
        self._current_tab = min(self._current_tab + 1, self._tab_count - 1)
        self._current_option = 0
        await self._render_tab_bar()
        await self._render_question()

    async def action_prev_tab(self) -> None:
        """Move to previous question tab."""
        if self._custom_text_mode:
            await self._exit_custom_text_mode()
        self._current_tab = max(self._current_tab - 1, 0)
        self._current_option = 0
        await self._render_tab_bar()
        await self._render_question()

    async def action_select_option(self) -> None:
        """Select the current option or submit."""
        if self._is_submit_tab:
            self._submit_answers()
            return

        q = self._current_question
        if q is None:
            return

        options = q.get("options", [])
        is_multi = q.get("multi_select", False)

        # Free text option
        if self._current_option >= len(options):
            if not self._custom_text_mode:
                # Enter custom text mode: mount a Textual Input widget
                await self._enter_custom_text_mode()
            return

        # Regular option selection
        selected_label = options[self._current_option].get("label", "")

        if is_multi:
            # Toggle selection for multi-select
            current = self._answers.get(self._current_tab, [])
            if not isinstance(current, list):
                current = []
            if selected_label in current:
                current.remove(selected_label)
            else:
                current.append(selected_label)
            self._answers[self._current_tab] = current
            self._update_options()
        else:
            # Single select - set answer and auto-advance
            self._answers[self._current_tab] = selected_label
            await self._render_tab_bar()
            await self.action_next_tab()

    async def _enter_custom_text_mode(self) -> None:
        """Mount a Textual Input widget for free text entry."""
        self._custom_text_mode = True
        self._update_options()

        # Mount the Input widget inside the options container
        if self._options_container:
            text_input = Input(
                placeholder=t("interview.custom_input_placeholder"),
                id="interview-custom-input",
            )
            self._custom_text_input = text_input
            await self._options_container.mount(text_input)
            text_input.focus()

    async def _exit_custom_text_mode(self) -> None:
        """Remove the Input widget and exit custom text mode."""
        self._custom_text_mode = False
        if self._custom_text_input is not None:
            try:
                await self._custom_text_input.remove()
            except Exception:  # noqa: BLE001
                pass
            self._custom_text_input = None
        self._update_options()
        self.focus()

    async def on_input_submitted(self, event: Input.Submitted) -> None:
        """Handle submission from the custom text Input widget."""
        custom_text = event.value.strip()
        if custom_text:
            self._answers[self._current_tab] = custom_text
            await self._exit_custom_text_mode()
            # Auto-advance to next tab
            await self.action_next_tab()
        # If empty, do nothing (keep input open)

    async def action_cancel(self) -> None:
        """Cancel the entire interview (abort all questions)."""
        if self._custom_text_mode:
            # Exit custom text mode first, then user can press Esc again to cancel
            await self._exit_custom_text_mode()
            return

        # Cancel the entire interview
        if self._future and not self._future.done():
            self._future.set_result({"action": "cancel", "_cancelled": True})
        self.post_message(self.Cancelled())

    def _submit_answers(self) -> None:
        """Submit all collected answers."""
        result: dict[str, Any] = {}
        for i, q in enumerate(self._questions):
            header = q.get("header", f"Q{i + 1}")
            answer = self._answers.get(i, None)
            if answer is not None:
                result[header] = answer
            else:
                result[header] = t("interview.unanswered")

        if self._future and not self._future.done():
            self._future.set_result(result)
        self.post_message(self.Answered(result))

    async def _on_key(self, event: events.Key) -> None:
        """Handle key events.

        When custom text mode is active, Escape exits the Input widget
        and returns to option selection. All other keys are handled
        by the mounted Input widget (supporting paste, IME, cursor movement).
        """
        if self._custom_text_mode and event.key == "escape":
            event.prevent_default()
            event.stop()
            await self._exit_custom_text_mode()

    def on_blur(self, event: events.Blur) -> None:
        """Re-focus on blur to keep focus trapped until answered.

        When in custom text mode, allow focus to go to the Input widget.
        """
        if self._custom_text_mode:
            return  # Let the Input widget keep focus
        self.call_after_refresh(self.focus)
