"""Unit tests for simplified zipencrypt module."""

from __future__ import annotations

from pathlib import Path
from unittest.mock import patch

import pytest

from pytola_office.zipencrypt.cli import (
    _create_unencrypted_zip,
    _execute_command,
    _get_valid_entries,
)


class TestExecuteCommand:
    """Tests for _execute_command function."""

    def test_successful_command_execution(self) -> None:
        """Test successful command execution."""
        # Mock subprocess.run to simulate successful execution
        with patch("subprocess.run") as mock_run:
            mock_run.return_value.stdout = "Success"
            mock_run.return_value.stderr = ""

            _execute_command(["echo", "test"])

            mock_run.assert_called_once_with(
                ["echo", "test"],
                capture_output=True,
                text=True,
                check=True,
                timeout=300,
                shell=False,
            )

    def test_command_failure(self) -> None:
        """Test command execution failure handling."""
        # Mock subprocess.run to simulate failure
        with patch("subprocess.run") as mock_run:
            from subprocess import CalledProcessError  # noqa: S404

            mock_run.side_effect = CalledProcessError(1, ["failed", "command"])

            # Test that exception is raised properly
            with pytest.raises(CalledProcessError):
                _execute_command(["failed", "command"])


class TestHelperFunctions:
    """Tests for helper functions."""

    def test_get_valid_entries_with_files_only(self, tmp_path: Path) -> None:
        """Test _get_valid_entries with only files."""
        # Create test files
        (tmp_path / "file1.txt").touch()
        (tmp_path / "file2.py").touch()

        entries = _get_valid_entries(tmp_path)
        entry_names = [entry.name for entry in entries]

        assert len(entries) == 2
        assert "file1.txt" in entry_names
        assert "file2.py" in entry_names

    def test_get_valid_entries_skips_hidden_directories(self, tmp_path: Path) -> None:
        """Test _get_valid_entries skips hidden directories."""
        # Create test files and directories
        (tmp_path / "file1.txt").touch()
        (tmp_path / ".hidden_dir").mkdir()
        (tmp_path / "__pycache__").mkdir()
        (tmp_path / "normal_dir").mkdir()

        entries = _get_valid_entries(tmp_path)
        entry_names = [entry.name for entry in entries]

        assert len(entries) == 2  # Only file1.txt and normal_dir
        assert "file1.txt" in entry_names
        assert "normal_dir" in entry_names
        assert ".hidden_dir" not in entry_names
        assert "__pycache__" not in entry_names

    def test_get_valid_entries_mixed_content(self, tmp_path: Path) -> None:
        """Test _get_valid_entries with mixed files and directories."""
        # Create mixed content
        (tmp_path / "file1.txt").touch()
        (tmp_path / "script.py").touch()
        (tmp_path / ".git").mkdir()
        (tmp_path / "src").mkdir()
        (tmp_path / "__pycache__").mkdir()

        entries = _get_valid_entries(tmp_path)
        entry_names = [entry.name for entry in entries]

        assert len(entries) == 3  # file1.txt, script.py, src
        assert set(entry_names) == {"file1.txt", "script.py", "src"}

    def test_create_unencrypted_zip_single_file(self, tmp_path: Path) -> None:
        """Test _create_unencrypted_zip with single file."""
        # Create test file
        test_file = tmp_path / "test.txt"
        test_file.write_text("Hello World")

        # Create zip file
        zip_path = tmp_path / "test.zip"
        _create_unencrypted_zip(test_file, zip_path)

        # Verify zip was created
        assert zip_path.exists()

        # Verify content can be extracted
        import zipfile

        with zipfile.ZipFile(zip_path, "r") as zf:
            assert "test.txt" in zf.namelist()
            content = zf.read("test.txt").decode()
            assert content == "Hello World"

    def test_create_unencrypted_zip_directory(self, tmp_path: Path) -> None:
        """Test _create_unencrypted_zip with directory."""
        # Create test directory structure
        test_dir = tmp_path / "test_dir"
        test_dir.mkdir()

        # Create files in directory
        (test_dir / "file1.txt").write_text("Content 1")
        (test_dir / "file2.txt").write_text("Content 2")
        sub_dir = test_dir / "subdir"
        sub_dir.mkdir()
        (sub_dir / "file3.txt").write_text("Content 3")

        # Create zip file
        zip_path = tmp_path / "test_dir.zip"
        _create_unencrypted_zip(test_dir, zip_path)

        # Verify zip was created
        assert zip_path.exists()

        # Verify content structure
        import zipfile

        with zipfile.ZipFile(zip_path, "r") as zf:
            namelist = zf.namelist()
            assert "file1.txt" in namelist
            assert "file2.txt" in namelist
            assert "subdir/file3.txt" in namelist
            assert len(namelist) == 3  # Only the files, no directories


class TestIntegration:
    """Integration tests for zipencrypt functionality."""

    def test_complete_encryption_workflow(self, tmp_path: Path) -> None:
        """Test complete encryption workflow with mocked external tools."""
        # Create test directory with files
        test_dir = tmp_path / "project"
        test_dir.mkdir()
        (test_dir / "main.py").write_text("print('hello')")
        (test_dir / "README.md").write_text("# Project")
        (test_dir / ".git").mkdir()  # Hidden directory

        # Mock configuration
        with patch("pytola_office.zipencrypt.cli.conf") as mock_conf:
            mock_conf.SKIP_PREFIXES = (".", "__")
            mock_conf.available_tools = []  # Force fallback to zipfile
            mock_conf.has_encryption_support = False

            # Test that valid entries are correctly identified
            entries = _get_valid_entries(test_dir)
            entry_names = [entry.name for entry in entries]
            assert set(entry_names) == {"main.py", "README.md"}
            assert ".git" not in entry_names


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