"""Tests for glreview.discovery module."""

from __future__ import annotations

from pathlib import Path

import pytest

from glreview.discovery import discover_modules, sync_registry


class TestDiscoverModules:
    """Tests for discover_modules function."""

    def test_discovers_matching_files(self, temp_dir):
        """Discovers files matching patterns."""
        src = temp_dir / "src"
        src.mkdir()
        (src / "main.py").write_text("# main")
        (src / "utils.py").write_text("# utils")

        modules = discover_modules(temp_dir, ["src/*.py"])
        assert modules == ["src/main.py", "src/utils.py"]

    def test_discovers_nested_files(self, temp_dir):
        """Discovers files in nested directories."""
        src = temp_dir / "src" / "pkg" / "sub"
        src.mkdir(parents=True)
        (src / "module.py").write_text("# module")
        (src.parent / "other.py").write_text("# other")

        modules = discover_modules(temp_dir, ["src/**/*.py"])
        assert "src/pkg/sub/module.py" in modules
        assert "src/pkg/other.py" in modules

    def test_multiple_patterns(self, temp_dir):
        """Discovers files matching multiple patterns."""
        src = temp_dir / "src"
        lib = temp_dir / "lib"
        src.mkdir()
        lib.mkdir()
        (src / "main.py").write_text("# main")
        (lib / "utils.py").write_text("# utils")

        modules = discover_modules(temp_dir, ["src/*.py", "lib/*.py"])
        assert modules == ["lib/utils.py", "src/main.py"]

    def test_excludes_patterns(self, temp_dir):
        """Excludes files matching exclude patterns."""
        src = temp_dir / "src"
        src.mkdir()
        (src / "main.py").write_text("# main")
        (src / "_version.py").write_text("# version")
        (src / "test_main.py").write_text("# test")

        modules = discover_modules(
            temp_dir,
            ["src/*.py"],
            exclude=["**/_version.py", "**/test_*.py"],
        )
        assert modules == ["src/main.py"]

    def test_ignores_directories(self, temp_dir):
        """Ignores directories matching patterns."""
        src = temp_dir / "src"
        src.mkdir()
        (src / "main.py").write_text("# main")
        # Create a directory that matches *.py pattern
        pkg_dir = src / "package.py"
        pkg_dir.mkdir()
        (pkg_dir / "module.py").write_text("# module")

        modules = discover_modules(temp_dir, ["src/*.py"])
        # Should only find the file, not the directory
        assert modules == ["src/main.py"]

    def test_returns_sorted(self, temp_dir):
        """Returns modules in sorted order."""
        src = temp_dir / "src"
        src.mkdir()
        (src / "zebra.py").write_text("# z")
        (src / "alpha.py").write_text("# a")
        (src / "middle.py").write_text("# m")

        modules = discover_modules(temp_dir, ["src/*.py"])
        assert modules == ["src/alpha.py", "src/middle.py", "src/zebra.py"]

    def test_no_duplicates(self, temp_dir):
        """Returns unique modules even with overlapping patterns."""
        src = temp_dir / "src"
        src.mkdir()
        (src / "main.py").write_text("# main")

        modules = discover_modules(temp_dir, ["src/*.py", "src/main.py"])
        assert modules == ["src/main.py"]

    def test_empty_when_no_matches(self, temp_dir):
        """Returns empty list when no matches."""
        modules = discover_modules(temp_dir, ["src/**/*.py"])
        assert modules == []

    def test_exclude_none_handled(self, temp_dir):
        """Handles None exclude parameter."""
        src = temp_dir / "src"
        src.mkdir()
        (src / "main.py").write_text("# main")

        modules = discover_modules(temp_dir, ["src/*.py"], exclude=None)
        assert modules == ["src/main.py"]


class TestSyncRegistry:
    """Tests for sync_registry function."""

    def test_detects_added_modules(self, temp_dir):
        """Detects newly added modules."""
        src = temp_dir / "src"
        src.mkdir()
        (src / "main.py").write_text("# main")
        (src / "new.py").write_text("# new")

        added, removed = sync_registry(
            temp_dir,
            ["src/*.py"],
            existing={"src/main.py": {}},
        )
        assert added == ["src/new.py"]
        assert removed == []

    def test_detects_removed_modules(self, temp_dir):
        """Detects removed modules."""
        src = temp_dir / "src"
        src.mkdir()
        (src / "main.py").write_text("# main")

        added, removed = sync_registry(
            temp_dir,
            ["src/*.py"],
            existing={"src/main.py": {}, "src/deleted.py": {}},
        )
        assert added == []
        assert removed == ["src/deleted.py"]

    def test_detects_both_added_and_removed(self, temp_dir):
        """Detects both added and removed modules."""
        src = temp_dir / "src"
        src.mkdir()
        (src / "main.py").write_text("# main")
        (src / "new.py").write_text("# new")

        added, removed = sync_registry(
            temp_dir,
            ["src/*.py"],
            existing={"src/main.py": {}, "src/old.py": {}},
        )
        assert added == ["src/new.py"]
        assert removed == ["src/old.py"]

    def test_no_changes_when_synced(self, temp_dir):
        """Returns empty lists when already synced."""
        src = temp_dir / "src"
        src.mkdir()
        (src / "main.py").write_text("# main")

        added, removed = sync_registry(
            temp_dir,
            ["src/*.py"],
            existing={"src/main.py": {}},
        )
        assert added == []
        assert removed == []

    def test_handles_empty_existing(self, temp_dir):
        """Handles empty or None existing registry."""
        src = temp_dir / "src"
        src.mkdir()
        (src / "main.py").write_text("# main")

        added, removed = sync_registry(temp_dir, ["src/*.py"], existing=None)
        assert added == ["src/main.py"]
        assert removed == []

    def test_respects_exclude_patterns(self, temp_dir):
        """Respects exclude patterns."""
        src = temp_dir / "src"
        src.mkdir()
        (src / "main.py").write_text("# main")
        (src / "_version.py").write_text("# version")

        added, removed = sync_registry(
            temp_dir,
            ["src/*.py"],
            exclude=["**/_version.py"],
            existing={},
        )
        assert added == ["src/main.py"]
        assert "src/_version.py" not in added

    def test_returns_sorted_lists(self, temp_dir):
        """Returns sorted added and removed lists."""
        src = temp_dir / "src"
        src.mkdir()
        (src / "zebra.py").write_text("# z")
        (src / "alpha.py").write_text("# a")

        added, removed = sync_registry(
            temp_dir,
            ["src/*.py"],
            existing={"src/yak.py": {}, "src/bat.py": {}},
        )
        assert added == ["src/alpha.py", "src/zebra.py"]
        assert removed == ["src/bat.py", "src/yak.py"]
