"""Unit tests for the pure-stdlib parsers module (#82 Phase 1).

These tests depend only on `:parsers` so they re-run only when
parsers.py itself changes — handler edits in server.py do not
invalidate them.
"""
from __future__ import annotations

import pytest

from active_claude_github_mcp.parsers import (
    parse_changelog,
    parse_owner_repo,
    parse_skill_version,
    semver_gt,
)


class TestParseSkillVersion:
    def test_finds_header(self):
        content = "# active-claude-github\n**Version:** 1.2.3\n\n..."
        assert parse_skill_version(content) == "1.2.3"

    def test_two_component_version(self):
        # "1.2" is accepted as 1.2 (regex tolerates X.Y)
        content = "**Version:** 1.2\n"
        assert parse_skill_version(content) == "1.2"

    def test_returns_none_on_missing_header(self):
        assert parse_skill_version("# some file with no version\n") is None

    def test_returns_none_on_empty(self):
        assert parse_skill_version("") is None

    def test_pre_release_alpha(self):
        # PEP-440 pre-release suffixes — surfaced as null pre-2.0.0a3 (#159)
        content = "**Version:** 2.0.0a2\n"
        assert parse_skill_version(content) == "2.0.0a2"

    def test_pre_release_beta(self):
        content = "**Version:** 2.0.0b1\n"
        assert parse_skill_version(content) == "2.0.0b1"

    def test_pre_release_rc(self):
        content = "**Version:** 2.1.0rc3\n"
        assert parse_skill_version(content) == "2.1.0rc3"


class TestParseChangelog:
    def test_single_entry(self):
        content = (
            "## Changelog\n"
            "- **1.0.0** (2026-04-21) — Initial release.\n"
            "\n## Trigger\n"
        )
        entries = parse_changelog(content)
        assert len(entries) == 1
        assert entries[0] == {
            "version": "1.0.0",
            "date": "2026-04-21",
            "summary": "Initial release.",
        }

    def test_multiple_entries_multi_line(self):
        content = (
            "## Changelog\n"
            "- **1.1.0** (2026-04-23) — MCP-native rewrite. "
            "Includes session lifecycle, recording, and TOC.\n"
            "- **1.0.1** (2026-04-23) — Bootstrap bug-fix batch.\n"
            "- **1.0.0** (2026-04-21) — Initial release.\n"
            "\n## Something Else\n"
        )
        entries = parse_changelog(content)
        assert [e["version"] for e in entries] == ["1.1.0", "1.0.1", "1.0.0"]
        assert "MCP-native rewrite" in entries[0]["summary"]

    def test_returns_empty_when_no_section(self):
        assert parse_changelog("no changelog here") == []

    def test_returns_empty_on_empty_input(self):
        assert parse_changelog("") == []

    def test_pre_release_entries(self):
        # Mirrors the active changelog as of 2.0.0a2 (#159)
        content = (
            "## Changelog\n"
            "- **2.0.0a2** (2026-04-26) — Tight alpha-feedback fix.\n"
            "- **2.0.0a1** (2026-04-26) — First alpha of 2.0.\n"
            "- **1.4.1** (2026-04-26) — Hotfix.\n"
            "\n## Trigger\n"
        )
        entries = parse_changelog(content)
        assert [e["version"] for e in entries] == [
            "2.0.0a2", "2.0.0a1", "1.4.1"
        ]


class TestSemverGt:
    def test_greater(self):
        assert semver_gt("1.2.0", "1.1.0")
        assert semver_gt("2.0.0", "1.9.9")
        assert semver_gt("1.0.1", "1.0.0")

    def test_equal_is_not_greater(self):
        assert not semver_gt("1.0.0", "1.0.0")

    def test_less_is_not_greater(self):
        assert not semver_gt("1.0.0", "1.0.1")
        assert not semver_gt("1.0.0", "2.0.0")

    def test_missing_components_treated_as_zero(self):
        # "1.2" == "1.2.0" per padding
        assert not semver_gt("1.2", "1.2.0")
        assert semver_gt("1.2.1", "1.2")

    def test_final_greater_than_pre_release(self):
        # "2.0.0" > "2.0.0rc1" > "2.0.0b1" > "2.0.0a2" (#159)
        assert semver_gt("2.0.0", "2.0.0rc1")
        assert semver_gt("2.0.0", "2.0.0a2")
        assert semver_gt("2.0.0rc1", "2.0.0b1")
        assert semver_gt("2.0.0b1", "2.0.0a9")

    def test_pre_release_ordering_within_kind(self):
        assert semver_gt("2.0.0a2", "2.0.0a1")
        assert semver_gt("2.0.0rc2", "2.0.0rc1")
        assert not semver_gt("2.0.0a1", "2.0.0a2")

    def test_pre_release_less_than_final(self):
        assert not semver_gt("2.0.0a2", "2.0.0")
        assert not semver_gt("2.0.0rc1", "2.0.0")

    def test_higher_minor_beats_pre_release(self):
        # "2.1.0a1" > "2.0.0" (major/minor dominates the pre-release tail)
        assert semver_gt("2.1.0a1", "2.0.0")
        assert not semver_gt("2.0.0", "2.1.0a1")


class TestParseOwnerRepo:
    def test_valid(self):
        owner, repo = parse_owner_repo("telejester/claude-skills-dev")
        assert owner == "telejester"
        assert repo == "claude-skills-dev"

    def test_no_slash_raises(self):
        with pytest.raises(ValueError, match="owner/repo"):
            parse_owner_repo("noslash")

    def test_empty_owner_raises(self):
        with pytest.raises(ValueError, match="owner/repo"):
            parse_owner_repo("/repo")

    def test_empty_repo_raises(self):
        with pytest.raises(ValueError, match="owner/repo"):
            parse_owner_repo("owner/")

    def test_too_many_slashes_raises(self):
        with pytest.raises(ValueError, match="owner/repo"):
            parse_owner_repo("owner/repo/extra")


if __name__ == "__main__":
    import sys
    sys.exit(pytest.main([__file__, "-v"]))
