from __future__ import annotations

from typing import TYPE_CHECKING, ClassVar, TypedDict

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 Static

from vibe.cli.textual_ui.widgets.no_markup_static import NoMarkupStatic

if TYPE_CHECKING:
    from vibe.core.config import VibeConfig


class SettingDefinition(TypedDict):
    key: str
    label: str
    type: str
    options: list[str]


class VoiceApp(Container):
    can_focus = True
    can_focus_children = False

    BINDINGS: ClassVar[list[BindingType]] = [
        Binding("up", "move_up", "Up", show=False),
        Binding("down", "move_down", "Down", show=False),
        Binding("space", "toggle_setting", "Toggle", show=False),
        Binding("enter", "cycle", "Next", show=False),
    ]

    class SettingChanged(Message):
        def __init__(self, key: str, value: str) -> None:
            super().__init__()
            self.key = key
            self.value = value

    class ConfigClosed(Message):
        def __init__(self, changes: dict[str, str | bool]) -> None:
            super().__init__()
            self.changes = changes

    def __init__(self, config: VibeConfig) -> None:
        super().__init__(id="voice-app")
        self.config = config
        self.selected_index = 0
        self.changes: dict[str, str] = {}

        self.settings: list[SettingDefinition] = [
            {
                "key": "voice_mode_enabled",
                "label": "Voice mode",
                "type": "cycle",
                "options": ["On", "Off"],
            },
            {
                "key": "narrator_enabled",
                "label": "Narrator (experimental)",
                "type": "cycle",
                "options": ["On", "Off"],
            },
        ]

        self.title_widget: Static | None = None
        self.setting_widgets: list[Static] = []
        self.help_widget: Static | None = None

    def compose(self) -> ComposeResult:
        with Vertical(id="voice-content"):
            self.title_widget = NoMarkupStatic(
                "Voice Settings", classes="settings-title"
            )
            yield self.title_widget

            yield NoMarkupStatic("")

            for _ in self.settings:
                widget = NoMarkupStatic("", classes="settings-option")
                self.setting_widgets.append(widget)
                yield widget

            yield NoMarkupStatic("")

            self.help_widget = NoMarkupStatic(
                "↑↓ navigate  Space/Enter toggle  ESC exit", classes="settings-help"
            )
            yield self.help_widget

    def on_mount(self) -> None:
        self._update_display()
        self.focus()

    def _get_display_value(self, setting: SettingDefinition) -> str:
        key = setting["key"]
        if key in self.changes:
            return self.changes[key]
        raw_value = getattr(self.config, key, "")
        if isinstance(raw_value, bool):
            return "On" if raw_value else "Off"
        return str(raw_value)

    def _update_display(self) -> None:
        for i, (setting, widget) in enumerate(
            zip(self.settings, self.setting_widgets, strict=True)
        ):
            is_selected = i == self.selected_index
            cursor = "› " if is_selected else "  "

            label: str = setting["label"]
            value: str = self._get_display_value(setting)

            text = f"{cursor}{label}: {value}"

            widget.update(text)

            widget.remove_class("settings-cursor-selected")
            widget.remove_class("settings-value-cycle-selected")
            widget.remove_class("settings-value-cycle-unselected")

            if is_selected:
                widget.add_class("settings-value-cycle-selected")
            else:
                widget.add_class("settings-value-cycle-unselected")

    def action_move_up(self) -> None:
        self.selected_index = (self.selected_index - 1) % len(self.settings)
        self._update_display()

    def action_move_down(self) -> None:
        self.selected_index = (self.selected_index + 1) % len(self.settings)
        self._update_display()

    def action_toggle_setting(self) -> None:
        setting = self.settings[self.selected_index]
        key: str = setting["key"]
        current: str = self._get_display_value(setting)

        options: list[str] = setting["options"]
        new_value = ""
        try:
            current_idx = options.index(current)
            next_idx = (current_idx + 1) % len(options)
            new_value = options[next_idx]
        except (ValueError, IndexError):
            new_value = options[0] if options else current

        self.changes[key] = new_value

        self.post_message(self.SettingChanged(key=key, value=new_value))

        self._update_display()

    def action_cycle(self) -> None:
        self.action_toggle_setting()

    def _convert_changes_for_save(self) -> dict[str, str | bool]:
        result: dict[str, str | bool] = {}
        for key, value in self.changes.items():
            if value in {"On", "Off"}:
                result[key] = value == "On"
            else:
                result[key] = value
        return result

    def action_close(self) -> None:
        self.post_message(self.ConfigClosed(changes=self._convert_changes_for_save()))

    def on_blur(self, event: events.Blur) -> None:
        self.call_after_refresh(self.focus)
