"""Tests for ID slugification and validation."""

import pytest

from legionnaire.ids import InvalidIdError, slugify, slugify_and_validate, validate_id


class TestSlugify:
    """Tests for the slugify function."""

    def test_single_space_to_dash(self):
        assert slugify("perf logging") == "perf-logging"

    def test_multiple_spaces_to_single_dash(self):
        assert slugify("schema   v2") == "schema-v2"

    def test_tabs_to_dash(self):
        assert slugify("test\ttab") == "test-tab"

    def test_mixed_whitespace(self):
        assert slugify("test \t\n value") == "test-value"

    def test_preserves_slash(self):
        assert slugify("team a/perf logging") == "team-a/perf-logging"

    def test_no_whitespace_unchanged(self):
        assert slugify("already-valid") == "already-valid"

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


class TestValidateId:
    """Tests for the validate_id function."""

    def test_valid_simple_id(self):
        validate_id("perf-logging")  # Should not raise

    def test_valid_with_slash(self):
        validate_id("team-a/perf-logging")  # Should not raise

    def test_valid_with_numbers(self):
        validate_id("feature-123")  # Should not raise

    def test_empty_id_raises(self):
        with pytest.raises(InvalidIdError, match="cannot be empty"):
            validate_id("")

    def test_leading_slash_raises(self):
        with pytest.raises(InvalidIdError, match="cannot start with"):
            validate_id("/leading")

    def test_trailing_slash_raises(self):
        with pytest.raises(InvalidIdError, match="cannot end with"):
            validate_id("trailing/")

    def test_double_slash_raises(self):
        with pytest.raises(InvalidIdError, match="empty segments"):
            validate_id("double//slash")

    def test_dot_segment_raises(self):
        with pytest.raises(InvalidIdError, match="'.' segment"):
            validate_id("path/./here")

    def test_dotdot_segment_raises(self):
        with pytest.raises(InvalidIdError, match="'..' segment"):
            validate_id("path/../here")

    def test_dot_lock_suffix_raises(self):
        with pytest.raises(InvalidIdError, match=".lock"):
            validate_id("branch.lock")

    def test_dot_lock_in_segment_raises(self):
        with pytest.raises(InvalidIdError, match=".lock"):
            validate_id("refs/heads/branch.lock/sub")

    def test_control_char_raises(self):
        with pytest.raises(InvalidIdError, match="control character"):
            validate_id("test\x00null")

    def test_tab_raises(self):
        with pytest.raises(InvalidIdError, match="control character"):
            validate_id("test\ttab")

    @pytest.mark.parametrize("char", ["~", "^", ":", "?", "*", "[", "\\"])
    def test_git_hazard_chars_raise(self, char):
        with pytest.raises(InvalidIdError, match="git-unsafe"):
            validate_id(f"test{char}value")

    def test_at_brace_raises(self):
        with pytest.raises(InvalidIdError, match="@{"):
            validate_id("ref@{upstream}")

    def test_space_raises(self):
        with pytest.raises(InvalidIdError, match="spaces"):
            validate_id("has space")


class TestSlugifyAndValidate:
    """Tests for the combined slugify_and_validate function."""

    def test_slugify_and_validate_success(self):
        result = slugify_and_validate("perf logging")
        assert result == "perf-logging"

    def test_slugify_and_validate_with_slash(self):
        result = slugify_and_validate("team a/perf logging")
        assert result == "team-a/perf-logging"

    def test_slugify_cannot_fix_invalid(self):
        with pytest.raises(InvalidIdError):
            slugify_and_validate("test~invalid")
