"""Tests for data models."""

import pytest
from ghostty_tui.models import (
    OptionType,
    OptionDefinition,
    ConfigValue,
    OptionModel,
    AppState,
)


class TestOptionType:
    """Tests for OptionType enum."""

    def test_all_types_exist(self):
        """Test all expected option types exist."""
        assert OptionType.BOOLEAN
        assert OptionType.STRING
        assert OptionType.INTEGER
        assert OptionType.FLOAT
        assert OptionType.COLOR
        assert OptionType.ENUM
        assert OptionType.PATH
        assert OptionType.KEYBIND

    def test_enum_values(self):
        """Test enum values are correct."""
        assert OptionType.BOOLEAN.value == "boolean"
        assert OptionType.STRING.value == "string"
        assert OptionType.INTEGER.value == "integer"
        assert OptionType.FLOAT.value == "float"
        assert OptionType.COLOR.value == "color"
        assert OptionType.ENUM.value == "enum"
        assert OptionType.PATH.value == "path"
        assert OptionType.KEYBIND.value == "keybind"


class TestOptionDefinition:
    """Tests for OptionDefinition dataclass."""

    def test_create_option(self):
        """Test creating an option definition."""
        opt = OptionDefinition(
            name="font-size",
            option_type=OptionType.INTEGER,
            default_value="12",
            description="Font size in points",
            category="Font"
        )
        assert opt.name == "font-size"
        assert opt.option_type == OptionType.INTEGER
        assert opt.default_value == "12"
        assert opt.description == "Font size in points"
        assert opt.category == "Font"

    def test_frozen(self):
        """Test option definition is immutable."""
        opt = OptionDefinition(
            name="test",
            option_type=OptionType.STRING,
            default_value=None,
            description="",
            category="Other"
        )
        with pytest.raises(Exception):  # FrozenInstanceError
            opt.name = "changed"

    def test_hash(self):
        """Test option definition is hashable."""
        opt = OptionDefinition(
            name="test",
            option_type=OptionType.STRING,
            default_value=None,
            description="",
            category="Other"
        )
        assert hash(opt) == hash("test")

    def test_enum_values_default(self):
        """Test enum_values defaults to empty tuple."""
        opt = OptionDefinition(
            name="test",
            option_type=OptionType.STRING,
            default_value=None,
            description="",
            category="Other"
        )
        assert opt.enum_values == ()

    def test_enum_values_provided(self):
        """Test enum_values can be provided."""
        opt = OptionDefinition(
            name="cursor-style",
            option_type=OptionType.ENUM,
            default_value="block",
            description="",
            category="Cursor",
            enum_values=("block", "bar", "underline")
        )
        assert opt.enum_values == ("block", "bar", "underline")

    def test_is_deprecated_default(self):
        """Test is_deprecated defaults to False."""
        opt = OptionDefinition(
            name="test",
            option_type=OptionType.STRING,
            default_value=None,
            description="",
            category="Other"
        )
        assert opt.is_deprecated is False


class TestConfigValue:
    """Tests for ConfigValue dataclass."""

    def test_create_value(self):
        """Test creating a config value."""
        val = ConfigValue(name="test", value="hello")
        assert val.name == "test"
        assert val.value == "hello"
        assert not val.is_modified

    def test_reset(self):
        """Test resetting modification state."""
        val = ConfigValue(name="test", value="hello", is_modified=True)
        val.reset()
        assert not val.is_modified

    def test_default_values(self):
        """Test default values are correct."""
        val = ConfigValue(name="test")
        assert val.name == "test"
        assert val.value is None
        assert val.line_number is None
        assert val.comment is None
        assert val.is_modified is False
        assert val.is_commented_out is False

    def test_line_number(self):
        """Test line_number field."""
        val = ConfigValue(name="test", value="hello", line_number=42)
        assert val.line_number == 42

    def test_comment(self):
        """Test comment field."""
        val = ConfigValue(name="test", value="hello", comment="This is a comment")
        assert val.comment == "This is a comment"

    def test_is_commented_out(self):
        """Test is_commented_out field."""
        val = ConfigValue(name="test", value="hello", is_commented_out=True)
        assert val.is_commented_out is True


class TestOptionModel:
    """Tests for OptionModel dataclass."""

    def test_display_value_current(self):
        """Test display_value returns current value."""
        defn = OptionDefinition(
            name="test",
            option_type=OptionType.STRING,
            default_value="default",
            description="",
            category="Other"
        )
        current = ConfigValue(name="test", value="current")
        model = OptionModel(definition=defn, current=current)
        assert model.display_value == "current"

    def test_display_value_default(self):
        """Test display_value returns default when no current."""
        defn = OptionDefinition(
            name="test",
            option_type=OptionType.STRING,
            default_value="default",
            description="",
            category="Other"
        )
        current = ConfigValue(name="test", value=None)
        model = OptionModel(definition=defn, current=current)
        assert model.display_value == "default"

    def test_display_value_empty_when_no_default(self):
        """Test display_value returns empty string when no default."""
        defn = OptionDefinition(
            name="test",
            option_type=OptionType.STRING,
            default_value=None,
            description="",
            category="Other"
        )
        current = ConfigValue(name="test", value=None)
        model = OptionModel(definition=defn, current=current)
        assert model.display_value == ""

    def test_is_default_true(self):
        """Test is_default when value matches default."""
        defn = OptionDefinition(
            name="test",
            option_type=OptionType.STRING,
            default_value="same",
            description="",
            category="Other"
        )
        current = ConfigValue(name="test", value="same")
        model = OptionModel(definition=defn, current=current)
        assert model.is_default

    def test_is_default_false(self):
        """Test is_default when value differs."""
        defn = OptionDefinition(
            name="test",
            option_type=OptionType.STRING,
            default_value="default",
            description="",
            category="Other"
        )
        current = ConfigValue(name="test", value="different")
        model = OptionModel(definition=defn, current=current)
        assert not model.is_default

    def test_is_default_when_none(self):
        """Test is_default when current value is None."""
        defn = OptionDefinition(
            name="test",
            option_type=OptionType.STRING,
            default_value="default",
            description="",
            category="Other"
        )
        current = ConfigValue(name="test", value=None)
        model = OptionModel(definition=defn, current=current)
        assert model.is_default

    def test_is_modified_property(self):
        """Test is_modified property delegates to current."""
        defn = OptionDefinition(
            name="test",
            option_type=OptionType.STRING,
            default_value="default",
            description="",
            category="Other"
        )
        current = ConfigValue(name="test", value="modified", is_modified=True)
        model = OptionModel(definition=defn, current=current)
        assert model.is_modified


class TestAppState:
    """Tests for AppState dataclass."""

    def test_modified_options(self):
        """Test modified_options property."""
        state = AppState()
        state.values["opt1"] = ConfigValue(name="opt1", value="a", is_modified=True)
        state.values["opt2"] = ConfigValue(name="opt2", value="b", is_modified=False)
        state.values["opt3"] = ConfigValue(name="opt3", value="c", is_modified=True)

        modified = state.modified_options
        assert "opt1" in modified
        assert "opt2" not in modified
        assert "opt3" in modified

    def test_has_unsaved_changes(self):
        """Test has_unsaved_changes property."""
        state = AppState()
        assert not state.has_unsaved_changes

        state.values["opt1"] = ConfigValue(name="opt1", is_modified=True)
        assert state.has_unsaved_changes

    def test_default_values(self):
        """Test default values are correct."""
        state = AppState()
        assert state.schema == {}
        assert state.values == {}
        assert state.current_category == ""
        assert state.search_query == ""
        assert state.config_mtime == 0.0

    def test_schema_field(self):
        """Test schema field."""
        state = AppState()
        opt = OptionDefinition(
            name="test",
            option_type=OptionType.STRING,
            default_value=None,
            description="",
            category="Other"
        )
        state.schema["test"] = opt
        assert "test" in state.schema
        assert state.schema["test"] == opt

    def test_current_category(self):
        """Test current_category field."""
        state = AppState(current_category="Font")
        assert state.current_category == "Font"

    def test_search_query(self):
        """Test search_query field."""
        state = AppState(search_query="cursor")
        assert state.search_query == "cursor"

    def test_config_mtime(self):
        """Test config_mtime field."""
        state = AppState(config_mtime=12345.6789)
        assert state.config_mtime == 12345.6789
