"""Unit tests for filelevel module."""

import logging
import tempfile
from pathlib import Path
from unittest.mock import patch

import pytest

from pytola_system.filelevel.cli import FileLevelConfig, FileProcessor, conf, process_file

logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)


class TestFileProcessor:
    """Test cases for FileProcessor class."""

    @pytest.fixture
    def sample_config(self):
        """Sample configuration fixture."""
        return FileLevelConfig()

    @pytest.fixture
    def temp_file(self):
        """Create temporary file for testing."""
        with tempfile.NamedTemporaryFile(suffix=".txt", delete=False) as tmp:
            tmp.write(b"test content")
            tmp_path = Path(tmp.name)
        yield tmp_path
        # 安全地删除文件,如果文件已被移动则忽略错误
        try:
            if tmp_path.exists():
                tmp_path.unlink()
        except FileNotFoundError:
            pass  # 文件可能已被重命名,忽略错误

    def test_processor_initialization(self, temp_file: Path, sample_config: FileLevelConfig):
        """Test FileProcessor initialization."""
        processor = FileProcessor(temp_file, sample_config)
        assert processor.src == temp_file
        assert processor.filestem == temp_file.stem  # Now uses the stem from the file path
        assert processor.config == sample_config

    @pytest.mark.parametrize(
        ("level", "expected_marker"),
        [
            (0, ""),
            (1, "(PUB)"),
            (2, "(INT)"),
            (3, "(CON)"),
            (4, "(CLA)"),
        ],
    )
    def test_add_level_mark(self, temp_file, sample_config, level, expected_marker):
        """Test adding level markers to filenames."""
        processor = FileProcessor(temp_file, sample_config)
        processor.filestem = "document"  # Set the stem manually
        processor._add_level_mark(level)

        if level == 0:
            assert processor.filestem == "document"
        else:
            assert expected_marker in processor.filestem

    def test_remove_marks(self, temp_file, sample_config):
        """Test removing marks from filenames."""
        processor = FileProcessor(temp_file, sample_config)
        processor.filestem = "document(PUB)"  # Set the stem manually
        processor._remove_marks(marks=["PUB", "INT", "CON", "CLA"])
        assert processor.filestem == "document"

    @pytest.mark.parametrize(
        ("input_stem", "mark", "expected_result"),
        [
            ("document(PUB)", "PUB", "document"),
            ("file(INT)", "INT", "file"),
            ("report(CON)", "CON", "report"),
            ("secret(CLA)", "CLA", "secret"),
            ("normal_file", "PUB", "normal_file"),  # No mark to remove
        ],
    )
    def test_remove_mark_static(self, input_stem: str, mark: str, expected_result: str):
        """Test static method for removing marks."""
        result = FileProcessor._remove_mark(input_stem, mark, conf.BRACKETS)
        assert result == expected_result

    def test_rename_integration(self, temp_file: Path, sample_config: FileLevelConfig):
        """Integration test for rename functionality."""
        original_name = temp_file.stem
        processor = FileProcessor(temp_file, sample_config)
        # Ensure the filestem is what we expect
        assert processor.filestem == original_name

        # Add level 2 marking
        processor.rename(level=2)

        # Verify file was renamed
        new_path = temp_file.parent / f"{original_name}(INT){temp_file.suffix}"
        assert new_path.exists()
        # Note: Original file path object still points to old location
        # but the actual file has been moved


class TestProcessFile:
    """Test cases for process_file function."""

    def test_process_file_invalid_level(self):
        """Test processing with invalid level logs error."""
        with tempfile.NamedTemporaryFile(suffix=".txt", delete=False) as tmp:
            tmp_path = Path(tmp.name)

        # Mock the logger to capture log calls
        with patch("pytola_system.filelevel.cli.logger") as mock_logger:
            process_file(tmp_path, level=5)  # Invalid level
            # Verify that error was logged
            mock_logger.error.assert_called_once()
            # Check that the error message contains the expected text
            args, _kwargs = mock_logger.error.call_args
            assert "Invalid level 5, must be between 0 and 4" in args[0]

        # Clean up
        tmp_path.unlink(missing_ok=True)

    def test_process_file_nonexistent_file(self):
        """Test processing nonexistent file generates error."""
        nonexistent_file = Path("nonexistent.txt")

        # Mock the logger to capture log calls
        with patch("pytola_system.filelevel.cli.logger") as mock_logger:
            process_file(nonexistent_file, level=1)
            # Verify that error was logged
            mock_logger.error.assert_called_once()
            # Check that the error message contains the expected text
            args, _kwargs = mock_logger.error.call_args
            assert "File does not exist: nonexistent.txt" in args[0]

    def test_process_single_file(self):
        """Test processing a single file."""
        with tempfile.TemporaryDirectory() as temp_dir:
            temp_path = Path(temp_dir)

            # Create test file
            file1 = temp_path / "document1.txt"
            file1.write_text("content1")

            # Process file
            process_file(file1, level=3)

            # Verify results
            assert not file1.exists()

            new_file1 = temp_path / "document1(CON).txt"
            assert new_file1.exists()


# Removed - no longer exists in the main module


class TestPropertyBased:
    """Property-based tests for file renaming."""

    @pytest.mark.parametrize("stem", ["abc", "hello", "myfile", "testdoc", "z"])
    @pytest.mark.parametrize("level", [0, 1, 2, 3, 4])
    @pytest.mark.parametrize("extension", ["txt", "pdf", "docx"])
    def test_rename_property_based(self, stem, level, extension):
        """Property-based test for file renaming."""
        filename = f"{stem}.{extension}"

        with tempfile.TemporaryDirectory() as temp_dir:
            temp_path = Path(temp_dir)
            test_file = temp_path / filename
            test_file.write_text("test content")

            config = FileLevelConfig()
            processor = FileProcessor(test_file, config)
            # Store original filestem for verification
            original_stem = processor.filestem
            assert original_stem == stem  # Verify initial state

            # Process the file
            processor.rename(level=level)

            # Check that a file with the expected name exists
            if level == 0:
                # Level 0 should preserve original name (no markers added)
                expected_files = list(temp_path.glob(f"{stem}.{extension}"))
                assert len(expected_files) >= 1
                # Original file should still exist (or be renamed to same name)
            else:
                level_marker = ["", "PUB", "INT", "CON", "CLA"][level]
                expected_files = list(temp_path.glob(f"{stem}({level_marker}).{extension}"))
                assert len(expected_files) >= 1
                # Original file should not exist with original name
                original_files = list(temp_path.glob(f"{stem}.{extension}"))
                # Filter out files that might have the same stem but different suffix
                original_exact = [f for f in original_files if f.name == filename]
                assert len(original_exact) == 0
