"""Tests for pysourcepack module."""

from __future__ import annotations

from pathlib import Path

import pytest

from ..components.sourcepacker import (
    DEFAULT_EXCLUDE,
    DEFAULT_INCLUDE,
    ProjectPacker,
    PySourcePacker,
    should_exclude,
    should_include,
)


@pytest.fixture
def temp_project_dir(tmp_path: Path) -> Path:
    """Create a temporary project directory with sample files."""
    project_dir = tmp_path / "test_project"
    project_dir.mkdir()

    # Create sample files
    (project_dir / "main.py").write_text("print('hello')", encoding="utf-8")
    (project_dir / "utils.py").write_text("# utility functions", encoding="utf-8")
    (project_dir / "README.md").write_text("# Test Project", encoding="utf-8")
    (project_dir / "pyproject.toml").write_text("[project]\nname = 'test'", encoding="utf-8")

    # Create sample directories
    (project_dir / "tests").mkdir()
    (project_dir / "tests" / "test_main.py").write_text("def test_main(): pass", encoding="utf-8")

    (project_dir / "__pycache__").mkdir()
    (project_dir / "__pycache__" / "main.cpython-38.pyc").write_text("", encoding="utf-8")

    return project_dir


class TestPatternMatching:
    """Tests for pattern matching functions."""

    def test_should_exclude_exact_name(self):
        """Test excluding files/directories by exact name."""
        test_path = Path("tests")
        assert should_exclude(test_path, {"tests"}) is True

    def test_should_exclude_wildcard(self):
        """Test excluding files by wildcard pattern."""
        test_path = Path("main.pyc")
        assert should_exclude(test_path, {"*.pyc"}) is True

    def test_should_not_exclude(self):
        """Test that non-matching paths are not excluded."""
        test_path = Path("main.py")
        assert should_exclude(test_path, {"*.pyc", "tests"}) is False

    def test_should_include_explicit(self):
        """Test including files that match include patterns."""
        test_path = Path("main.py")
        assert should_include(test_path, {"*.py"}, set()) is True

    def test_should_include_default_when_no_patterns(self):
        """Test that files are included when no include patterns are specified."""
        test_path = Path("main.py")
        assert should_include(test_path, set(), set()) is True

    def test_should_not_include_excluded(self):
        """Test that excluded files are not included."""
        test_path = Path("main.pyc")
        assert should_include(test_path, {"*.py"}, {"*.pyc"}) is False


class TestProjectPacker:
    """Tests for ProjectPacker class."""

    def test_project_packer_initialization(self, mocker):
        """Test ProjectPacker initialization."""
        mock_parent = mocker.Mock(spec=PySourcePacker)
        mock_project = mocker.Mock()
        mock_project.toml_path = Path("/fake/path/pyproject.toml")

        packer = ProjectPacker(
            parent=mock_parent,
            project=mock_project,
            include_patterns=DEFAULT_INCLUDE,
            exclude_patterns=DEFAULT_EXCLUDE,
        )

        assert packer.parent == mock_parent
        assert packer.project == mock_project
        assert packer.include_patterns == DEFAULT_INCLUDE
        assert packer.exclude_patterns == DEFAULT_EXCLUDE

    def test_project_path_property(self, mocker):
        """Test project_path property."""
        mock_parent = mocker.Mock(spec=PySourcePacker)
        mock_project = mocker.Mock()
        mock_project.toml_path = Path("/fake/project/pyproject.toml")

        packer = ProjectPacker(
            parent=mock_parent,
            project=mock_project,
            include_patterns=set(),
            exclude_patterns=set(),
        )

        assert packer.project_path == Path("/fake/project")

    def test_output_dir_property(self, mocker):
        """Test output_dir property."""
        mock_parent = mocker.Mock(spec=PySourcePacker)
        mock_parent.root_dir = Path("/root")
        mock_project = mocker.Mock()
        mock_project.normalized_name = "test_project"
        mock_project.toml_path = Path("/fake/path/pyproject.toml")

        packer = ProjectPacker(
            parent=mock_parent,
            project=mock_project,
            include_patterns=set(),
            exclude_patterns=set(),
        )

        expected_path = Path("/root/dist/src/test_project")
        assert packer.output_dir == expected_path


class TestPySourcePacker:
    """Tests for PySourcePacker class."""

    def test_pysourcepacker_initialization(self, tmp_path: Path):
        """Test PySourcePacker initialization."""
        packer = PySourcePacker(root_dir=tmp_path)

        assert packer.root_dir == tmp_path
        assert packer.include_patterns == DEFAULT_INCLUDE
        assert packer.exclude_patterns == DEFAULT_EXCLUDE

    def test_pysourcepacker_custom_patterns(self, tmp_path: Path):
        """Test PySourcePacker with custom include/exclude patterns."""
        custom_include = {"*.txt", "*.md"}
        custom_exclude = {"temp", "*.tmp"}

        packer = PySourcePacker(
            root_dir=tmp_path,
            include_patterns=custom_include,
            exclude_patterns=custom_exclude,
        )

        assert packer.include_patterns == custom_include
        assert packer.exclude_patterns == custom_exclude

    def test_pack_project_not_found(self, tmp_path: Path, mocker):
        """Test pack_project with non-existent project."""
        mock_solution = mocker.Mock()
        mock_solution.projects = {}

        packer = PySourcePacker(root_dir=tmp_path)
        mocker.patch.object(packer, "solution", mock_solution)

        result = packer.pack_project("nonexistent")
        assert result is False

    def test_list_projects_empty(self, tmp_path: Path, mocker, caplog):
        """Test list_projects with empty projects."""
        mock_solution = mocker.Mock()
        mock_solution.projects = {}

        packer = PySourcePacker(root_dir=tmp_path)
        mocker.patch.object(packer, "solution", mock_solution)

        packer.list_projects()
        assert "No projects found" not in caplog.text  # Just verify it runs without error
