"""Tests for persistent TUI settings."""

from __future__ import annotations

import json
from pathlib import Path
from typing import Any

import pytest

from clients.terminal.tui.settings import TuiSettings, get_tui_settings, reload_tui_settings


@pytest.fixture
def tmp_state_dir(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Path:
    """Override the state dir so tests never touch ~/.navi_code."""
    from clients.terminal import config

    original = config.settings.state_dir
    config.settings.state_dir = tmp_path
    yield tmp_path
    config.settings.state_dir = original


@pytest.fixture(autouse=True)
def reset_cached_settings(monkeypatch: pytest.MonkeyPatch) -> None:
    """Clear the module-level cache before each test."""
    import clients.terminal.tui.settings as settings_module

    settings_module._tui_settings = None


def test_default_settings() -> None:
    s = TuiSettings()
    assert s.theme == "gnexus-dark"
    assert s.mouse is True
    assert s.scroll_speed == 1
    assert s.diff_style == "unified"
    assert s.max_visible_items == 200
    assert s.keybinds == {}


def test_settings_save_and_load(tmp_state_dir: Path) -> None:
    s = TuiSettings()
    s.theme = "gnexus-light"
    s.mouse = False
    s.scroll_speed = 3
    s.save()

    loaded = TuiSettings().load()
    assert loaded.theme == "gnexus-light"
    assert loaded.mouse is False
    assert loaded.scroll_speed == 3


def test_settings_migrates_unknown_fields(tmp_state_dir: Path) -> None:
    data: dict[str, Any] = {
        "theme": "gnexus-light",
        "future_field": "ignored",
    }
    (tmp_state_dir / "tui.json").write_text(json.dumps(data), encoding="utf-8")
    loaded = TuiSettings().load()
    assert loaded.theme == "gnexus-light"
    assert loaded.mouse is True  # default preserved


def test_settings_load_creates_default_file(tmp_state_dir: Path) -> None:
    loaded = TuiSettings().load()
    file_path = tmp_state_dir / "tui.json"
    assert file_path.exists()
    data = json.loads(file_path.read_text(encoding="utf-8"))
    assert data["theme"] == "gnexus-dark"
    assert loaded.theme == "gnexus-dark"


def test_get_tui_settings_caches(monkeypatch: pytest.MonkeyPatch, tmp_state_dir: Path) -> None:
    first = get_tui_settings()
    first.theme = "custom-theme"
    first.save()
    second = get_tui_settings()
    assert second is first
    assert second.theme == "custom-theme"

    fresh = reload_tui_settings()
    assert fresh is not first
    assert fresh.theme == "custom-theme"


def test_settings_coerces_string_booleans(tmp_state_dir: Path) -> None:
    data = {"mouse": "yes"}
    (tmp_state_dir / "tui.json").write_text(json.dumps(data), encoding="utf-8")
    loaded = TuiSettings().load()
    assert loaded.mouse is True


def test_settings_coerces_numeric_scroll_speed(tmp_state_dir: Path) -> None:
    data = {"scroll_speed": "5"}
    (tmp_state_dir / "tui.json").write_text(json.dumps(data), encoding="utf-8")
    loaded = TuiSettings().load()
    assert loaded.scroll_speed == 5


def test_settings_falls_back_on_bad_values(tmp_state_dir: Path) -> None:
    data = {
        "mouse": "maybe",
        "scroll_speed": "fast",
        "theme": "",
        "diff_style": 123,
    }
    (tmp_state_dir / "tui.json").write_text(json.dumps(data), encoding="utf-8")
    loaded = TuiSettings().load()
    assert loaded.mouse is True
    assert loaded.scroll_speed == 1
    assert loaded.theme == "gnexus-dark"
    assert loaded.diff_style == "unified"


def test_settings_coerces_numeric_max_visible_items(tmp_state_dir: Path) -> None:
    data = {"max_visible_items": "150"}
    (tmp_state_dir / "tui.json").write_text(json.dumps(data), encoding="utf-8")
    loaded = TuiSettings().load()
    assert loaded.max_visible_items == 150


def test_settings_rejects_too_small_max_visible_items(tmp_state_dir: Path) -> None:
    """A value below the floor falls back to the default, not the floor."""
    data = {"max_visible_items": 5}
    (tmp_state_dir / "tui.json").write_text(json.dumps(data), encoding="utf-8")
    loaded = TuiSettings().load()
    assert loaded.max_visible_items == 200


def test_settings_rejects_bad_max_visible_items(tmp_state_dir: Path) -> None:
    data = {"max_visible_items": "all"}
    (tmp_state_dir / "tui.json").write_text(json.dumps(data), encoding="utf-8")
    loaded = TuiSettings().load()
    assert loaded.max_visible_items == 200
