"""Tests for meshcore.conf file parsing."""

import os

from meshmon.env import _parse_config_value


def _load_config_from_content(tmp_path, monkeypatch, content: str | None) -> None:
    import meshmon.env as env

    config_path = tmp_path / "meshcore.conf"
    if content is not None:
        config_path.write_text(content)

    fake_env_path = tmp_path / "src" / "meshmon" / "env.py"
    fake_env_path.parent.mkdir(parents=True, exist_ok=True)
    fake_env_path.write_text("")

    monkeypatch.setattr(env, "__file__", str(fake_env_path))
    env._load_config_file()

class TestParseConfigValueDetailed:
    """Detailed tests for _parse_config_value."""

    # ==========================================================================
    # Empty/whitespace handling
    # ==========================================================================

    def test_empty_string(self):
        assert _parse_config_value("") == ""

    def test_only_spaces(self):
        assert _parse_config_value("   ") == ""

    def test_only_tabs(self):
        assert _parse_config_value("\t\t") == ""

    # ==========================================================================
    # Unquoted values
    # ==========================================================================

    def test_simple_value(self):
        assert _parse_config_value("hello") == "hello"

    def test_value_with_leading_trailing_space(self):
        assert _parse_config_value("  hello  ") == "hello"

    def test_value_with_internal_spaces(self):
        assert _parse_config_value("hello world") == "hello world"

    def test_numeric_value(self):
        assert _parse_config_value("12345") == "12345"

    def test_path_value(self):
        assert _parse_config_value("/dev/ttyUSB0") == "/dev/ttyUSB0"

    # ==========================================================================
    # Double-quoted strings
    # ==========================================================================

    def test_double_quoted_simple(self):
        assert _parse_config_value('"hello"') == "hello"

    def test_double_quoted_with_spaces(self):
        assert _parse_config_value('"hello world"') == "hello world"

    def test_double_quoted_with_special_chars(self):
        assert _parse_config_value('"hello #world"') == "hello #world"

    def test_double_quoted_unclosed(self):
        assert _parse_config_value('"hello') == "hello"

    def test_double_quoted_empty(self):
        assert _parse_config_value('""') == ""

    def test_double_quoted_with_trailing_content(self):
        # Only extracts content within first pair of quotes
        assert _parse_config_value('"hello" # comment') == "hello"

    # ==========================================================================
    # Single-quoted strings
    # ==========================================================================

    def test_single_quoted_simple(self):
        assert _parse_config_value("'hello'") == "hello"

    def test_single_quoted_with_spaces(self):
        assert _parse_config_value("'hello world'") == "hello world"

    def test_single_quoted_unclosed(self):
        assert _parse_config_value("'hello") == "hello"

    def test_single_quoted_empty(self):
        assert _parse_config_value("''") == ""

    # ==========================================================================
    # Inline comments
    # ==========================================================================

    def test_inline_comment_with_space(self):
        assert _parse_config_value("hello # comment") == "hello"

    def test_inline_comment_multiple_spaces(self):
        assert _parse_config_value("hello   # comment here") == "hello"

    def test_hash_without_space_kept(self):
        # Hash without preceding space is kept (not a comment)
        assert _parse_config_value("color#ffffff") == "color#ffffff"

    def test_hash_at_start_kept(self):
        # Hash at start is kept (though unusual for a value)
        assert _parse_config_value("#ffffff") == "#ffffff"

    # ==========================================================================
    # Mixed scenarios
    # ==========================================================================

    def test_quoted_preserves_hash_comment_style(self):
        assert _parse_config_value('"test # not a comment"') == "test # not a comment"

    def test_value_ending_with_hash(self):
        # "test#" has no space before #, so kept
        assert _parse_config_value("test#") == "test#"


class TestLoadConfigFileBehavior:
    """Tests for _load_config_file behavior."""

    def test_nonexistent_file_no_error(self, tmp_path, monkeypatch, isolate_config_loading):
        """Missing config file doesn't raise error."""
        _load_config_from_content(tmp_path, monkeypatch, content=None)

        assert "MESH_TRANSPORT" not in os.environ

    def test_skips_empty_lines(self, tmp_path, monkeypatch, isolate_config_loading):
        """Empty lines are skipped."""
        config_content = """
MESH_TRANSPORT=tcp

MESH_DEBUG=1

"""
        _load_config_from_content(tmp_path, monkeypatch, config_content)

        assert os.environ["MESH_TRANSPORT"] == "tcp"
        assert os.environ["MESH_DEBUG"] == "1"

    def test_skips_comment_lines(self, tmp_path, monkeypatch, isolate_config_loading):
        """Lines starting with # are skipped."""
        config_content = """# This is a comment
MESH_TRANSPORT=tcp
# Another comment
"""
        _load_config_from_content(tmp_path, monkeypatch, config_content)

        assert os.environ["MESH_TRANSPORT"] == "tcp"

    def test_handles_export_prefix(self, tmp_path, monkeypatch, isolate_config_loading):
        """Lines with 'export ' prefix are handled."""
        config_content = "export MESH_TRANSPORT=tcp\n"
        _load_config_from_content(tmp_path, monkeypatch, config_content)

        assert os.environ["MESH_TRANSPORT"] == "tcp"

    def test_skips_lines_without_equals(self, tmp_path, monkeypatch, isolate_config_loading):
        """Lines without = are skipped."""
        config_content = """MESH_TRANSPORT=tcp
this line has no equals
MESH_DEBUG=1
"""
        _load_config_from_content(tmp_path, monkeypatch, config_content)

        assert os.environ["MESH_TRANSPORT"] == "tcp"
        assert os.environ["MESH_DEBUG"] == "1"

    def test_env_vars_take_precedence(self, tmp_path, monkeypatch, isolate_config_loading):
        """Environment variables override config file values."""
        # Set env var first
        monkeypatch.setenv("MESH_TRANSPORT", "ble")

        # Config file has different value
        config_content = "MESH_TRANSPORT=serial\n"
        _load_config_from_content(tmp_path, monkeypatch, config_content)

        # After loading, env var should still be "ble"
        assert os.environ.get("MESH_TRANSPORT") == "ble"


class TestConfigFileFormats:
    """Test various config file format scenarios."""

    def test_standard_format(self):
        """Standard KEY=value format."""
        assert _parse_config_value("value") == "value"

    def test_spaces_around_equals(self):
        """Key = value with spaces (handled by partition)."""
        # Note: _parse_config_value only handles the value part
        # The key=value split happens in _load_config_file
        assert _parse_config_value(" value ") == "value"

    def test_quoted_path_with_spaces(self):
        """Path with spaces must be quoted."""
        assert _parse_config_value('"/path/with spaces/file.txt"') == "/path/with spaces/file.txt"

    def test_url_value(self):
        """URL values work correctly."""
        assert _parse_config_value("https://example.com:8080/path") == "https://example.com:8080/path"

    def test_email_value(self):
        """Email values work correctly."""
        assert _parse_config_value("user@example.com") == "user@example.com"

    def test_json_like_value(self):
        """JSON-like values need quoting if they have spaces."""
        # Without spaces, works fine
        assert _parse_config_value("{key:value}") == "{key:value}"
        # With spaces, needs quotes
        assert _parse_config_value('"{key: value}"') == "{key: value}"


class TestValidKeyPatterns:
    """Test key validation patterns."""

    def test_valid_key_patterns(self):
        """Valid shell identifier patterns."""
        # These would be tested in _load_config_file
        # Valid: starts with letter or underscore, contains letters/numbers/underscores
        valid_keys = [
            "MESH_TRANSPORT",
            "_PRIVATE",
            "var123",
            "MY_VAR_2",
        ]
        # All should match: ^[A-Za-z_][A-Za-z0-9_]*$
        import re
        pattern = r"^[A-Za-z_][A-Za-z0-9_]*$"
        for key in valid_keys:
            assert re.match(pattern, key), f"{key} should be valid"

    def test_invalid_key_patterns(self):
        """Invalid key patterns are rejected."""
        invalid_keys = [
            "123_starts_with_number",
            "has-dash",
            "has.dot",
            "has space",
            "",
        ]
        import re
        pattern = r"^[A-Za-z_][A-Za-z0-9_]*$"
        for key in invalid_keys:
            assert not re.match(pattern, key), f"{key} should be invalid"
