"""
Tests for engine.config module.
"""

import sys
import tempfile
from pathlib import Path
from unittest.mock import patch

import pytest

from engine import config


class TestArgValue:
    """Tests for _arg_value helper."""

    def test_returns_value_when_flag_present(self):
        """Returns the value following the flag."""
        with patch.object(sys, "argv", ["prog", "--font-file", "test.otf"]):
            result = config._arg_value("--font-file")
            assert result == "test.otf"

    def test_returns_none_when_flag_missing(self):
        """Returns None when flag is not present."""
        with patch.object(sys, "argv", ["prog"]):
            result = config._arg_value("--font-file")
            assert result is None

    def test_returns_none_when_no_value(self):
        """Returns None when flag is last."""
        with patch.object(sys, "argv", ["prog", "--font-file"]):
            result = config._arg_value("--font-file")
            assert result is None


class TestArgInt:
    """Tests for _arg_int helper."""

    def test_parses_valid_int(self):
        """Parses valid integer."""
        with patch.object(sys, "argv", ["prog", "--font-index", "5"]):
            result = config._arg_int("--font-index", 0)
            assert result == 5

    def test_returns_default_on_invalid(self):
        """Returns default on invalid input."""
        with patch.object(sys, "argv", ["prog", "--font-index", "abc"]):
            result = config._arg_int("--font-index", 0)
            assert result == 0

    def test_returns_default_when_missing(self):
        """Returns default when flag missing."""
        with patch.object(sys, "argv", ["prog"]):
            result = config._arg_int("--font-index", 10)
            assert result == 10


class TestResolveFontPath:
    """Tests for _resolve_font_path helper."""

    def test_returns_absolute_paths(self):
        """Absolute paths are returned as-is."""
        result = config._resolve_font_path("/absolute/path.otf")
        assert result == "/absolute/path.otf"

    def test_resolves_relative_paths(self):
        """Relative paths are resolved to repo root."""
        result = config._resolve_font_path("fonts/test.otf")
        assert str(config._REPO_ROOT) in result

    def test_expands_user_home(self):
        """Tilde paths are expanded."""
        with patch("pathlib.Path.expanduser", return_value=Path("/home/user/fonts")):
            result = config._resolve_font_path("~/fonts/test.otf")
            assert isinstance(result, str)


class TestListFontFiles:
    """Tests for _list_font_files helper."""

    def test_returns_empty_for_missing_dir(self):
        """Returns empty list for missing directory."""
        result = config._list_font_files("/nonexistent/directory")
        assert result == []

    def test_filters_by_extension(self):
        """Only returns valid font extensions."""
        with tempfile.TemporaryDirectory() as tmpdir:
            Path(tmpdir, "valid.otf").touch()
            Path(tmpdir, "valid.ttf").touch()
            Path(tmpdir, "invalid.txt").touch()
            Path(tmpdir, "image.png").touch()

            result = config._list_font_files(tmpdir)
            assert len(result) == 2
            assert all(f.endswith((".otf", ".ttf")) for f in result)

    def test_sorts_alphabetically(self):
        """Results are sorted alphabetically."""
        with tempfile.TemporaryDirectory() as tmpdir:
            Path(tmpdir, "zfont.otf").touch()
            Path(tmpdir, "afont.otf").touch()

            result = config._list_font_files(tmpdir)
            filenames = [Path(f).name for f in result]
            assert filenames == ["afont.otf", "zfont.otf"]


class TestDefaults:
    """Tests for default configuration values."""

    def test_headline_limit(self):
        """HEADLINE_LIMIT has sensible default."""
        assert config.HEADLINE_LIMIT > 0

    def test_feed_timeout(self):
        """FEED_TIMEOUT has sensible default."""
        assert config.FEED_TIMEOUT > 0

    def test_font_extensions(self):
        """Font extensions are defined."""
        assert ".otf" in config._FONT_EXTENSIONS
        assert ".ttf" in config._FONT_EXTENSIONS
        assert ".ttc" in config._FONT_EXTENSIONS


class TestGlyphs:
    """Tests for glyph constants."""

    def test_glitch_glyphs_defined(self):
        """GLITCH glyphs are defined."""
        assert len(config.GLITCH) > 0

    def test_kata_glyphs_defined(self):
        """KATA glyphs are defined."""
        assert len(config.KATA) > 0


class TestSetFontSelection:
    """Tests for set_font_selection function."""

    def test_updates_font_path(self):
        """Updates FONT_PATH globally."""
        original = config.FONT_PATH
        config.set_font_selection(font_path="/new/path.otf")
        assert config.FONT_PATH == "/new/path.otf"
        config.FONT_PATH = original

    def test_updates_font_index(self):
        """Updates FONT_INDEX globally."""
        original = config.FONT_INDEX
        config.set_font_selection(font_index=5)
        assert config.FONT_INDEX == 5
        config.FONT_INDEX = original

    def test_handles_none_values(self):
        """Handles None values gracefully."""
        original_path = config.FONT_PATH
        original_index = config.FONT_INDEX

        config.set_font_selection(font_path=None, font_index=None)
        assert original_path == config.FONT_PATH
        assert original_index == config.FONT_INDEX


class TestConfigDataclass:
    """Tests for Config dataclass."""

    def test_config_has_required_fields(self):
        """Config has all required fields."""
        c = config.Config()
        assert hasattr(c, "headline_limit")
        assert hasattr(c, "feed_timeout")
        assert hasattr(c, "mic_threshold_db")
        assert hasattr(c, "mode")
        assert hasattr(c, "firehose")
        assert hasattr(c, "ntfy_topic")
        assert hasattr(c, "ntfy_reconnect_delay")
        assert hasattr(c, "message_display_secs")
        assert hasattr(c, "font_dir")
        assert hasattr(c, "font_path")
        assert hasattr(c, "font_index")
        assert hasattr(c, "font_picker")
        assert hasattr(c, "font_sz")
        assert hasattr(c, "render_h")
        assert hasattr(c, "ssaa")
        assert hasattr(c, "scroll_dur")
        assert hasattr(c, "frame_dt")
        assert hasattr(c, "firehose_h")
        assert hasattr(c, "grad_speed")
        assert hasattr(c, "glitch_glyphs")
        assert hasattr(c, "kata_glyphs")
        assert hasattr(c, "script_fonts")

    def test_config_defaults(self):
        """Config has sensible defaults."""
        c = config.Config()
        assert c.headline_limit == 1000
        assert c.feed_timeout == 10
        assert c.mic_threshold_db == 50
        assert c.mode == "news"
        assert c.firehose is False
        assert c.ntfy_reconnect_delay == 5
        assert c.message_display_secs == 30

    def test_config_is_immutable(self):
        """Config is frozen (immutable)."""
        c = config.Config()
        with pytest.raises(AttributeError):
            c.headline_limit = 500  # type: ignore

    def test_config_custom_values(self):
        """Config accepts custom values."""
        c = config.Config(
            headline_limit=500,
            mode="poetry",
            firehose=True,
            ntfy_topic="https://ntfy.sh/test",
        )
        assert c.headline_limit == 500
        assert c.mode == "poetry"
        assert c.firehose is True
        assert c.ntfy_topic == "https://ntfy.sh/test"


class TestConfigFromArgs:
    """Tests for Config.from_args method."""

    def test_from_args_defaults(self):
        """from_args creates config with defaults from empty argv."""
        c = config.Config.from_args(["prog"])
        assert c.mode == "news"
        assert c.firehose is False
        assert c.font_picker is True

    def test_from_args_poetry_mode(self):
        """from_args detects --poetry flag."""
        c = config.Config.from_args(["prog", "--poetry"])
        assert c.mode == "poetry"

    def test_from_args_poetry_short_flag(self):
        """from_args detects -p short flag."""
        c = config.Config.from_args(["prog", "-p"])
        assert c.mode == "poetry"

    def test_from_args_firehose(self):
        """from_args detects --firehose flag."""
        c = config.Config.from_args(["prog", "--firehose"])
        assert c.firehose is True

    def test_from_args_no_font_picker(self):
        """from_args detects --no-font-picker flag."""
        c = config.Config.from_args(["prog", "--no-font-picker"])
        assert c.font_picker is False

    def test_from_args_font_index(self):
        """from_args parses --font-index."""
        c = config.Config.from_args(["prog", "--font-index", "3"])
        assert c.font_index == 3


class TestGetSetConfig:
    """Tests for get_config and set_config functions."""

    def test_get_config_returns_config(self):
        """get_config returns a Config instance."""
        c = config.get_config()
        assert isinstance(c, config.Config)

    def test_set_config_allows_injection(self):
        """set_config allows injecting a custom config."""
        custom = config.Config(mode="poetry", headline_limit=100)
        config.set_config(custom)
        assert config.get_config().mode == "poetry"
        assert config.get_config().headline_limit == 100

    def test_set_config_then_get_config(self):
        """set_config followed by get_config returns the set config."""
        original = config.get_config()
        test_config = config.Config(headline_limit=42)
        config.set_config(test_config)
        result = config.get_config()
        assert result.headline_limit == 42
        config.set_config(original)


class TestPlatformFontPaths:
    """Tests for platform font path detection."""

    def test_get_platform_font_paths_returns_dict(self):
        """_get_platform_font_paths returns a dictionary."""
        fonts = config._get_platform_font_paths()
        assert isinstance(fonts, dict)

    def test_platform_font_paths_common_languages(self):
        """Common language font mappings exist."""
        fonts = config._get_platform_font_paths()
        common = {"ja", "zh-cn", "ko", "ru", "ar", "hi"}
        found = set(fonts.keys()) & common
        assert len(found) > 0
