"""Engine settings screen — configure Stockfish engine parameters."""

from __future__ import annotations

from dataclasses import dataclass

from rich.text import Text
from textual.app import ComposeResult
from textual.screen import Screen
from textual.reactive import reactive
from textual.widgets import Static, Footer, Header, Button
from textual.containers import Vertical

from chess_cli.tui.screens.menu_widgets import MenuOption
from chess_cli.main import DIFFICULTY_CHOICES
from chess_cli.settings import load_settings, save_settings


# ── Constants ────────────────────────────────────────────────────────

@dataclass
class EngineProfile:
    name: str
    threads: int
    hash_mb: int
    move_time: float | None
    description: str


ENGINE_PROFILES = [
    EngineProfile("Quick",    1,  16,  None, "1 thread, 16MB hash — fast & light"),
    EngineProfile("Standard", 2,  64,  None, "2 threads, 64MB hash — balanced"),
    EngineProfile("Strong",   4, 256,  5.0,  "4 threads, 256MB hash, 5s/move"),
    EngineProfile("Maximum",  16, 1024, 10.0, "16 threads, 1GB hash, 10s/move"),
    EngineProfile("Custom",   0,  0,   None, "Configure manually"),
]

THREAD_OPTIONS = [1, 2, 4, 8, 12, 16]
HASH_OPTIONS = [16, 64, 256, 512, 1024]
MOVE_TIME_OPTIONS: list[float | None] = [None, 1.0, 2.0, 5.0, 10.0, 30.0]
MOVE_TIME_LABELS = ["Auto", "1s", "2s", "5s", "10s", "30s"]
_BAR_WIDTH = 20

_BASE_FOCUSABLE = ["opt-difficulty", "opt-engine"]
_CUSTOM_FOCUSABLE = ["opt-threads", "opt-hash", "opt-movetime"]


# ── EngineSettingsScreen ─────────────────────────────────────────────

class EngineSettingsScreen(Screen):
    """Configure Stockfish engine settings."""

    DEFAULT_CSS = """
EngineSettingsScreen {
    align: center middle;
}

#engine-container {
    width: 60;
    height: auto;
    max-height: 100%;
    border: heavy $primary;
    padding: 2 3;
    background: $surface;
    overflow-y: auto;
}

#engine-title {
    text-align: center;
    width: 100%;
    margin-bottom: 2;
}

.section-header {
    width: 100%;
    height: 1;
    color: $text-muted;
    margin: 2 0 1 0;
    padding: 0 1;
}

#engine-description {
    width: 100%;
    height: 1;
    color: $text-muted;
    padding: 0 1 0 17;
}

#btn-back {
    width: 100%;
    margin-top: 1;
}

.hidden, .hidden-section {
    display: none;
}
"""

    BINDINGS = [
        ("up", "focus_prev", "Up"),
        ("down", "focus_next", "Down"),
        ("left", "cycle_left", "\u25c0"),
        ("right", "cycle_right", "\u25b6"),
        ("enter", "select", "Select"),
        ("escape", "back", "Back"),
        ("q", "back", "Back"),
    ]

    difficulty_index: reactive[int] = reactive(5)
    engine_index: reactive[int] = reactive(0)
    threads_index: reactive[int] = reactive(0)
    hash_index: reactive[int] = reactive(0)
    movetime_index: reactive[int] = reactive(0)
    focus_index: reactive[int] = reactive(0)

    @property
    def _is_custom(self) -> bool:
        return ENGINE_PROFILES[self.engine_index].name == "Custom"

    @property
    def _focusable_ids(self) -> list[str]:
        ids = list(_BASE_FOCUSABLE)
        if self._is_custom:
            ids.extend(_CUSTOM_FOCUSABLE)
        ids.append("btn-back")
        return ids

    def compose(self) -> ComposeResult:
        yield Header(show_clock=False)
        with Vertical(id="engine-container"):
            yield Static("\u2699  Engine Settings", id="engine-title")
            yield Static("\u2500\u2500 Difficulty \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500", classes="section-header")
            yield MenuOption("Difficulty", "opt-difficulty")
            yield Static("\u2500\u2500 Engine Profile \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500", classes="section-header")
            yield MenuOption("Profile", "opt-engine")
            yield Static("", id="engine-description")
            yield MenuOption("Threads", "opt-threads")
            yield MenuOption("Hash", "opt-hash")
            yield MenuOption("Move time", "opt-movetime")
            yield Static("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500", classes="section-header")
            yield Button("\u2190 Back", id="btn-back")
        yield Footer()

    # ------------------------------------------------------------------
    # Lifecycle
    # ------------------------------------------------------------------

    def on_mount(self) -> None:
        settings = load_settings()

        # Restore difficulty index from saved name
        saved_difficulty = settings.difficulty
        if saved_difficulty in DIFFICULTY_CHOICES:
            self.difficulty_index = DIFFICULTY_CHOICES.index(saved_difficulty)

        # Restore engine profile index from saved name
        profile_names = [p.name for p in ENGINE_PROFILES]
        if settings.engine_profile in profile_names:
            self.engine_index = profile_names.index(settings.engine_profile)

        # Restore custom options from saved values
        if settings.custom_threads in THREAD_OPTIONS:
            self.threads_index = THREAD_OPTIONS.index(settings.custom_threads)
        if settings.custom_hash_mb in HASH_OPTIONS:
            self.hash_index = HASH_OPTIONS.index(settings.custom_hash_mb)
        if 0 <= settings.custom_move_time_index < len(MOVE_TIME_OPTIONS):
            self.movetime_index = settings.custom_move_time_index

        # Initial render
        self._update_custom_visibility()
        self._render_difficulty()
        self._render_engine()
        self._render_custom_options()
        self._update_highlight()

    # ------------------------------------------------------------------
    # Focus navigation
    # ------------------------------------------------------------------

    def _update_highlight(self) -> None:
        focusable = self._focusable_ids
        all_options = list(_BASE_FOCUSABLE) + list(_CUSTOM_FOCUSABLE)
        for opt_id in all_options:
            try:
                widget = self.query_one(f"#{opt_id}", MenuOption)
                widget.highlighted = (
                    opt_id in focusable
                    and focusable.index(opt_id) == self.focus_index
                )
            except Exception:
                pass

        # Button highlight
        any_button_focused = False
        try:
            btn = self.query_one("#btn-back", Button)
            idx = focusable.index("btn-back") if "btn-back" in focusable else -1
            if idx == self.focus_index:
                btn.focus()
                btn.add_class("highlighted")
                any_button_focused = True
            else:
                btn.remove_class("highlighted")
        except Exception:
            pass

        if not any_button_focused:
            self.set_focus(None)

    def watch_focus_index(self, _value: int) -> None:
        self._update_highlight()

    def action_focus_prev(self) -> None:
        total = len(self._focusable_ids)
        self.focus_index = (self.focus_index - 1) % total

    def action_focus_next(self) -> None:
        total = len(self._focusable_ids)
        self.focus_index = (self.focus_index + 1) % total

    def action_cycle_left(self) -> None:
        self._cycle_focused(-1)

    def action_cycle_right(self) -> None:
        self._cycle_focused(1)

    def _cycle_focused(self, direction: int) -> None:
        focusable = self._focusable_ids
        if self.focus_index >= len(focusable):
            return
        current_id = focusable[self.focus_index]
        if current_id == "opt-difficulty":
            self.difficulty_index = (self.difficulty_index + direction) % len(DIFFICULTY_CHOICES)
        elif current_id == "opt-engine":
            self.engine_index = (self.engine_index + direction) % len(ENGINE_PROFILES)
        elif current_id == "opt-threads":
            self.threads_index = (self.threads_index + direction) % len(THREAD_OPTIONS)
        elif current_id == "opt-hash":
            self.hash_index = (self.hash_index + direction) % len(HASH_OPTIONS)
        elif current_id == "opt-movetime":
            self.movetime_index = (self.movetime_index + direction) % len(MOVE_TIME_OPTIONS)

    def action_select(self) -> None:
        focusable = self._focusable_ids
        if self.focus_index >= len(focusable):
            return
        current_id = focusable[self.focus_index]
        if current_id == "btn-back":
            self.action_back()
        else:
            # On option rows, Enter cycles forward (same as right arrow)
            self._cycle_focused(1)

    # ------------------------------------------------------------------
    # Rendering helpers
    # ------------------------------------------------------------------

    def _render_difficulty(self) -> None:
        from chess_cli.engine import DIFFICULTY_PRESETS

        idx = self.difficulty_index
        name = DIFFICULTY_CHOICES[idx]
        elo = DIFFICULTY_PRESETS[name]
        total = len(DIFFICULTY_CHOICES)

        filled = int((idx + 1) / total * _BAR_WIDTH)
        text = Text()
        for i in range(_BAR_WIDTH):
            if i < filled:
                frac = i / _BAR_WIDTH
                if frac < 0.5:
                    r = int(100 + 155 * (frac * 2))
                    g = 200
                else:
                    r = 255
                    g = int(200 * (1 - (frac - 0.5) * 2))
                text.append("\u2588", style=f"rgb({r},{g},50)")
            else:
                text.append("\u2591", style="dim")

        text.append(f" {name.capitalize()}")
        if elo is None:
            text.append(" (max)", style="dim")
        else:
            text.append(f" ({elo})", style="dim")

        try:
            self.query_one("#opt-difficulty", MenuOption).set_value(text)
        except Exception:
            pass

    def _render_engine(self) -> None:
        profile = ENGINE_PROFILES[self.engine_index]
        text = Text()
        text.append(f"\u25c0 {profile.name} \u25b6")

        try:
            self.query_one("#opt-engine", MenuOption).set_value(text)
        except Exception:
            pass

        # Description line
        if not self._is_custom:
            desc = profile.description
        else:
            desc = "Configure threads, hash, and move time below"
        try:
            self.query_one("#engine-description", Static).update(desc)
        except Exception:
            pass

    def _render_custom_options(self) -> None:
        if not self._is_custom:
            return
        try:
            self.query_one("#opt-threads", MenuOption).set_value(
                f"\u25c0 {THREAD_OPTIONS[self.threads_index]} \u25b6"
            )
        except Exception:
            pass
        try:
            self.query_one("#opt-hash", MenuOption).set_value(
                f"\u25c0 {HASH_OPTIONS[self.hash_index]} MB \u25b6"
            )
        except Exception:
            pass
        try:
            self.query_one("#opt-movetime", MenuOption).set_value(
                f"\u25c0 {MOVE_TIME_LABELS[self.movetime_index]} \u25b6"
            )
        except Exception:
            pass

    def _update_custom_visibility(self) -> None:
        is_custom = self._is_custom
        for opt_id in _CUSTOM_FOCUSABLE:
            try:
                widget = self.query_one(f"#{opt_id}", MenuOption)
                widget.set_class(not is_custom, "hidden")
            except Exception:
                pass
        # Clamp focus if custom options disappeared
        total = len(self._focusable_ids)
        if self.focus_index >= total:
            self.focus_index = total - 1

    # ------------------------------------------------------------------
    # Watchers
    # ------------------------------------------------------------------

    def watch_difficulty_index(self, _value: int) -> None:
        self._render_difficulty()
        self._save_settings()

    def watch_engine_index(self, _value: int) -> None:
        self._render_engine()
        self._update_custom_visibility()
        self._render_custom_options()
        self._update_highlight()
        self._save_settings()

    def watch_threads_index(self, _value: int) -> None:
        self._render_custom_options()
        self._save_settings()

    def watch_hash_index(self, _value: int) -> None:
        self._render_custom_options()
        self._save_settings()

    def watch_movetime_index(self, _value: int) -> None:
        self._render_custom_options()
        self._save_settings()

    # ------------------------------------------------------------------
    # Persistence
    # ------------------------------------------------------------------

    def _save_settings(self) -> None:
        settings = load_settings()
        settings.difficulty = DIFFICULTY_CHOICES[self.difficulty_index]
        settings.engine_profile = ENGINE_PROFILES[self.engine_index].name
        settings.custom_threads = THREAD_OPTIONS[self.threads_index]
        settings.custom_hash_mb = HASH_OPTIONS[self.hash_index]
        settings.custom_move_time_index = self.movetime_index
        save_settings(settings)

    # ------------------------------------------------------------------
    # Actions
    # ------------------------------------------------------------------

    def action_back(self) -> None:
        self.app.pop_screen()

    def on_button_pressed(self, event: Button.Pressed) -> None:
        if event.button.id == "btn-back":
            self.action_back()
