"""Tests for the Ghostty schema parser."""

import pytest
from ghostty_tui.parser import GhosttyParser, GhosttyParserError
from ghostty_tui.models import OptionType


class TestGhosttyParser:
    """Tests for GhosttyParser class."""

    def test_parse_sample_file(self, parser, sample_schema_path):
        """Test parsing sample schema file."""
        options = parser.parse_from_file(sample_schema_path)
        assert len(options) > 0

    def test_parse_boolean_option(self, parser):
        """Test parsing boolean option."""
        schema = "# Enable feature\nmy-option = true"
        options = parser.parse(schema)
        assert len(options) == 1
        assert options[0].option_type == OptionType.BOOLEAN
        assert options[0].default_value == "true"

    def test_parse_boolean_false(self, parser):
        """Test parsing boolean false option."""
        schema = "# Disable feature\ndisable-option = false"
        options = parser.parse(schema)
        assert len(options) == 1
        assert options[0].option_type == OptionType.BOOLEAN
        assert options[0].default_value == "false"

    def test_parse_integer_option(self, parser):
        """Test parsing integer option."""
        schema = "# Size in pixels\nfont-size = 12"
        options = parser.parse(schema)
        assert len(options) == 1
        assert options[0].option_type == OptionType.INTEGER
        assert options[0].default_value == "12"

    def test_parse_negative_integer(self, parser):
        """Test parsing negative integer option."""
        schema = "# Offset value\noffset = -10"
        options = parser.parse(schema)
        assert len(options) == 1
        assert options[0].option_type == OptionType.INTEGER
        assert options[0].default_value == "-10"

    def test_parse_float_option(self, parser):
        """Test parsing float option."""
        schema = "# Opacity value\nopacity = 0.8"
        options = parser.parse(schema)
        assert len(options) == 1
        assert options[0].option_type == OptionType.FLOAT
        assert options[0].default_value == "0.8"

    def test_parse_enum_option(self, parser):
        """Test parsing enum option with valid values."""
        schema = "# The style\n# Valid values: block, bar, underline\ncursor-style = block"
        options = parser.parse(schema)
        assert len(options) == 1
        assert options[0].option_type == OptionType.ENUM
        assert "block" in options[0].enum_values
        assert "bar" in options[0].enum_values
        assert "underline" in options[0].enum_values

    def test_parse_enum_single_value_becomes_string(self, parser):
        """Test that single valid value is treated as STRING, not ENUM.

        A single value isn't really a choice, so it should be STRING type.
        """
        schema = "# The mode\n# Valid value: auto\nmode = auto"
        options = parser.parse(schema)
        assert len(options) == 1
        # Single value is not a real enum, should be STRING
        assert options[0].option_type == OptionType.STRING
        assert options[0].enum_values == ()

    def test_parse_color_option(self, parser):
        """Test parsing color option with hex format."""
        schema = "# The color\nbackground = #1a1b26"
        options = parser.parse(schema)
        assert len(options) == 1
        assert options[0].option_type == OptionType.COLOR
        assert options[0].default_value == "#1a1b26"

    def test_parse_color_short_hex(self, parser):
        """Test parsing color with short hex format."""
        schema = "# The color\nfg = #fff"
        options = parser.parse(schema)
        assert len(options) == 1
        assert options[0].option_type == OptionType.COLOR

    def test_parse_color_by_name(self, parser):
        """Test parsing option with 'color' in name."""
        schema = "# Custom color\ntext-color = red"
        options = parser.parse(schema)
        assert len(options) == 1
        assert options[0].option_type == OptionType.COLOR

    def test_parse_string_option(self, parser):
        """Test parsing string option."""
        schema = "# The title\nwindow-title = Ghostty"
        options = parser.parse(schema)
        assert len(options) == 1
        assert options[0].option_type == OptionType.STRING
        assert options[0].default_value == "Ghostty"

    def test_parse_path_option(self, parser):
        """Test parsing path option."""
        schema = "# Config file\nconfig-file = /path/to/config"
        options = parser.parse(schema)
        assert len(options) == 1
        assert options[0].option_type == OptionType.PATH
        assert options[0].default_value == "/path/to/config"

    def test_parse_path_by_name(self, parser):
        """Test parsing option with 'path' in name."""
        schema = "# The path\nsocket-path = /tmp/socket"
        options = parser.parse(schema)
        assert len(options) == 1
        assert options[0].option_type == OptionType.PATH

    def test_parse_keybind_option(self, parser):
        """Test parsing keybind option."""
        schema = "# Keyboard binding\nkeybind = ctrl+shift+t>new_tab"
        options = parser.parse(schema)
        assert len(options) == 1
        assert options[0].option_type == OptionType.KEYBIND

    def test_parse_empty_value(self, parser):
        """Test parsing option with empty value."""
        schema = "# Font family\nfont-family = "
        options = parser.parse(schema)
        assert len(options) == 1
        assert options[0].default_value is None or options[0].default_value == ""

    def test_parse_multiline_description(self, parser):
        """Test parsing multiline description."""
        schema = "# First line\n# Second line\n# Third line\ntest = value"
        options = parser.parse(schema)
        assert len(options) == 1
        assert "First line" in options[0].description
        assert "Second line" in options[0].description
        assert "Third line" in options[0].description

    def test_parse_description_preservation(self, parser):
        """Test that descriptions are properly joined."""
        schema = "# Line one\n# Line two\ntest = value"
        options = parser.parse(schema)
        assert "Line one\nLine two" == options[0].description

    def test_parse_category_inference_font(self, parser):
        """Test category inference for font options."""
        schema = "# Test\nfont-size = 12"
        options = parser.parse(schema)
        assert options[0].category == "Font"

    def test_parse_category_inference_window(self, parser):
        """Test category inference for window options."""
        schema = "# Test\nwindow-width = 800"
        options = parser.parse(schema)
        assert options[0].category == "Window"

    def test_parse_category_inference_cursor(self, parser):
        """Test category inference for cursor options."""
        schema = "# Test\ncursor-blink = true"
        options = parser.parse(schema)
        assert options[0].category == "Cursor"

    def test_parse_deprecated_option(self, parser):
        """Test parsing deprecated option."""
        schema = "# This option is deprecated\nold-option = value"
        options = parser.parse(schema)
        assert len(options) == 1
        assert options[0].is_deprecated

    def test_parse_no_content_raises(self, parser):
        """Test parsing empty content raises error."""
        with pytest.raises(GhosttyParserError):
            parser.parse(None)

    def test_parse_empty_string_raises(self, parser):
        """Test parsing empty string raises error."""
        with pytest.raises(GhosttyParserError):
            parser.parse("")

    def test_parse_from_file_not_found(self, parser):
        """Test parsing non-existent file raises error."""
        with pytest.raises(GhosttyParserError):
            parser.parse_from_file("/nonexistent/file.txt")

    def test_parse_multiple_options(self, parser):
        """Test parsing multiple options."""
        schema = """# Option one
font-size = 12

# Option two
cursor-blink = true

# Option three
window-title = Terminal
"""
        options = parser.parse(schema)
        assert len(options) == 3
        assert options[0].name == "font-size"
        assert options[1].name == "cursor-blink"
        assert options[2].name == "window-title"

    def test_parse_blank_lines_reset_docs(self, parser):
        """Test that blank lines reset documentation accumulation."""
        schema = """# This is for option 1
option1 = value1

# This is for option 2
option2 = value2
"""
        options = parser.parse(schema)
        assert len(options) == 2
        assert "option 1" in options[0].description
        assert "option 1" not in options[1].description
        assert "option 2" in options[1].description

    def test_parse_ignores_malformed_options(self, parser):
        """Test that malformed options are skipped."""
        schema = """# Valid option
font-size = 12

# Invalid option (no equals)
bad option

# Another valid option
cursor-blink = true
"""
        options = parser.parse(schema)
        assert len(options) == 2
        assert options[0].name == "font-size"
        assert options[1].name == "cursor-blink"

    def test_parse_option_with_hyphen(self, parser):
        """Test parsing option names with hyphens."""
        schema = "# Test\nfont-family-bold = Monospace"
        options = parser.parse(schema)
        assert len(options) == 1
        assert options[0].name == "font-family-bold"

    def test_parse_option_with_underscore(self, parser):
        """Test parsing option names with underscores."""
        schema = "# Test\nsome_option = value"
        options = parser.parse(schema)
        assert len(options) == 1
        assert options[0].name == "some_option"

    def test_parse_strips_whitespace(self, parser):
        """Test that values are stripped of whitespace."""
        schema = "# Test\nfont-size =   12   "
        options = parser.parse(schema)
        assert len(options) == 1
        assert options[0].default_value == "12"

    def test_parse_enum_values_extracted(self, parser):
        """Test enum values are properly extracted and cleaned."""
        schema = "# The style\n# Valid values: block, bar, underline\ncursor-style = block"
        options = parser.parse(schema)
        assert len(options) == 1
        values = options[0].enum_values
        assert len(values) == 3
        # Check values are cleaned (no extra quotes/spaces)
        assert all(isinstance(v, str) and v.strip() == v for v in values)
