"""Unit tests for pyarchive functionality."""

from __future__ import annotations

import tempfile
from pathlib import Path
from unittest.mock import Mock, patch

import pytest

from ..components.archiver import (
    PyArchiveConfig,
    PyArchiver,
)


class TestPyArchiveConfig:
    """Tests for PyArchiveConfig dataclass."""

    def test_default_values(self):
        """Test default configuration values."""
        # Use temporary directory to avoid loading existing config
        with tempfile.TemporaryDirectory() as temp_dir:
            import pytola.dev.pypack.components.archiver as pyarchive_module

            original_config_file = pyarchive_module.CONFIG_FILE
            temp_config_file = Path(temp_dir) / "pyarchive.json"
            pyarchive_module.CONFIG_FILE = temp_config_file

            try:
                config = PyArchiveConfig()
                assert config.compression_level == 6
                assert config.verbose is False
                assert config.preserve_permissions is True
                assert config.max_workers == 4
                assert config.output_dir == "build"
                assert config.dist_dir == "dist"
            finally:
                pyarchive_module.CONFIG_FILE = original_config_file

    def test_custom_values(self):
        """Test custom configuration values."""
        # Use temporary directory to avoid loading existing config
        with tempfile.TemporaryDirectory() as temp_dir:
            import pytola.dev.pypack.components.archiver as pyarchive_module

            original_config_file = pyarchive_module.CONFIG_FILE
            temp_config_file = Path(temp_dir) / "pyarchive.json"
            pyarchive_module.CONFIG_FILE = temp_config_file

            try:
                config = PyArchiveConfig(
                    compression_level=9,
                    verbose=True,
                    preserve_permissions=False,
                    max_workers=8,
                    output_dir="custom_build",
                    dist_dir="custom_dist",
                )
                assert config.compression_level == 9
                assert config.verbose is True
                assert config.preserve_permissions is False
                assert config.max_workers == 8
                assert config.output_dir == "custom_build"
                assert config.dist_dir == "custom_dist"
            finally:
                pyarchive_module.CONFIG_FILE = original_config_file

    def test_compression_level_validation(self):
        """Test compression level validation."""
        # Use temporary directory to avoid loading existing config
        with tempfile.TemporaryDirectory() as temp_dir:
            import pytola.dev.pypack.components.archiver as pyarchive_module

            original_config_file = pyarchive_module.CONFIG_FILE
            temp_config_file = Path(temp_dir) / "pyarchive.json"
            pyarchive_module.CONFIG_FILE = temp_config_file

            try:
                # Valid levels
                PyArchiveConfig(compression_level=0)
                PyArchiveConfig(compression_level=5)
                PyArchiveConfig(compression_level=9)

                # Invalid levels should raise ValueError
                with pytest.raises(ValueError, match="Compression level must be between 0 and 9"):
                    PyArchiveConfig(compression_level=-1)
                with pytest.raises(ValueError, match="Compression level must be between 0 and 9"):
                    PyArchiveConfig(compression_level=10)
            finally:
                pyarchive_module.CONFIG_FILE = original_config_file

    def test_save_and_load_config(self):
        """Test configuration persistence."""
        with tempfile.TemporaryDirectory() as temp_dir:
            # Mock the CONFIG_FILE path
            import pytola.dev.pypack.components.archiver as pyarchive_module

            original_config_file = pyarchive_module.CONFIG_FILE
            temp_config_file = Path(temp_dir) / "pyarchive.json"
            pyarchive_module.CONFIG_FILE = temp_config_file

            try:
                # Create and save config
                config = PyArchiveConfig(compression_level=7, verbose=True, max_workers=2)
                config.save()

                # Verify file was created
                assert temp_config_file.exists()

                # Create new config instance - should load saved values
                new_config = PyArchiveConfig()
                assert new_config.compression_level == 7
                assert new_config.verbose is True
                assert new_config.max_workers == 2

            finally:
                # Restore original CONFIG_FILE
                pyarchive_module.CONFIG_FILE = original_config_file


class TestPyArchiver:
    """Tests for PyArchiver class."""

    def test_initialization(self):
        """Test PyArchiver initialization."""
        with tempfile.TemporaryDirectory() as temp_dir:
            root_path = Path(temp_dir)
            archiver = PyArchiver(root_dir=root_path)

            assert archiver.root_dir == root_path
            assert isinstance(archiver.config, PyArchiveConfig)

    def test_initialization_with_custom_config(self):
        """Test PyArchiver initialization with custom config."""
        with tempfile.TemporaryDirectory() as temp_dir:
            root_path = Path(temp_dir)
            # Use temporary config file to avoid loading existing config
            import pytola.dev.pypack.components.archiver as pyarchive_module

            original_config_file = pyarchive_module.CONFIG_FILE
            temp_config_file = Path(temp_dir) / "pyarchive.json"
            pyarchive_module.CONFIG_FILE = temp_config_file

            try:
                custom_config = PyArchiveConfig(compression_level=8, verbose=True)
                archiver = PyArchiver(root_dir=root_path, config=custom_config)

                assert archiver.config.compression_level == 8
                assert archiver.config.verbose is True
            finally:
                pyarchive_module.CONFIG_FILE = original_config_file

    @patch("pytola.dev.pypack.models.Solution.from_directory")
    def test_solution_property(self, mock_from_directory):
        """Test solution property caching."""
        with tempfile.TemporaryDirectory() as temp_dir:
            root_path = Path(temp_dir)
            archiver = PyArchiver(root_dir=root_path)

            # Mock solution
            mock_solution = Mock()
            mock_from_directory.return_value = mock_solution

            # Access solution property twice
            solution1 = archiver.solution
            solution2 = archiver.solution

            # Should return same instance (cached)
            assert solution1 is solution2
            mock_from_directory.assert_called_once_with(root_path)

    @patch("pytola.dev.pypack.models.Solution.from_directory")
    def test_projects_property(self, mock_from_directory):
        """Test projects property caching."""
        with tempfile.TemporaryDirectory() as temp_dir:
            root_path = Path(temp_dir)
            archiver = PyArchiver(root_dir=root_path)

            # Mock solution with projects
            mock_solution = Mock()
            mock_projects = {"project1": Mock(), "project2": Mock()}
            mock_solution.projects = mock_projects
            mock_from_directory.return_value = mock_solution

            # Access projects property twice
            projects1 = archiver.projects
            projects2 = archiver.projects

            # Should return same instance (cached)
            assert projects1 is projects2
            assert projects1 == mock_projects

    def test_default_ignore_patterns(self):
        """Test default ignore patterns property."""
        with tempfile.TemporaryDirectory() as temp_dir:
            root_path = Path(temp_dir)
            archiver = PyArchiver(root_dir=root_path)

            patterns = archiver.default_ignore_patterns
            assert isinstance(patterns, set)
            assert "__pycache__" in patterns
            assert "*.pyc" in patterns
            assert ".git" in patterns

    def test_should_ignore_method(self):
        """Test should_ignore method."""
        with tempfile.TemporaryDirectory() as temp_dir:
            root_path = Path(temp_dir)
            archiver = PyArchiver(root_dir=root_path)

            # Test with default patterns
            assert archiver.should_ignore(Path("test.pyc")) is True
            assert archiver.should_ignore(Path("__pycache__")) is True
            assert archiver.should_ignore(Path(".git")) is True
            assert archiver.should_ignore(Path("main.py")) is False

            # Test with custom patterns
            custom_patterns = {"*.tmp", "temp"}
            assert archiver.should_ignore(Path("file.tmp"), custom_patterns) is True
            assert archiver.should_ignore(Path("temp/file.txt"), custom_patterns) is True
            assert archiver.should_ignore(Path("normal.txt"), custom_patterns) is False

    def test_check_command_available(self):
        """Test command availability checking."""
        with tempfile.TemporaryDirectory() as temp_dir:
            root_path = Path(temp_dir)
            archiver = PyArchiver(root_dir=root_path)

            # This will depend on system availability
            # Just test that it returns a boolean
            result = archiver.check_command_available("python")
            assert isinstance(result, bool)


# Integration tests would go here
# These typically require actual file system operations and external tools
