"""Benchmark tests for pysourcepack."""

from __future__ import annotations

from pathlib import Path

import pytest

from ..components.sourcepacker import (
    PySourcePacker,
    should_exclude,
    should_include,
)


@pytest.fixture
def large_project_structure(tmp_path: Path) -> Path:
    """Create a large project structure for benchmarking."""
    project_dir = tmp_path / "large_project"
    project_dir.mkdir()

    # Create pyproject.toml
    pyproject_content = """[project]
name = "large_project"
version = "1.0.0"
description = "A large project for benchmarking"
readme = "README.md"
requires-python = ">=3.8"
dependencies = []
"""
    (project_dir / "pyproject.toml").write_text(pyproject_content, encoding="utf-8")

    # Create many source files
    for i in range(100):
        (project_dir / f"module_{i:03d}.py").write_text(
            f"# Module {i}\ndef function_{i}():\n    return {i}\n", encoding="utf-8"
        )

    # Create subdirectories with files
    for subdir_num in range(10):
        subdir = project_dir / f"subdir_{subdir_num:02d}"
        subdir.mkdir()
        for file_num in range(20):
            (subdir / f"file_{file_num:03d}.py").write_text(
                f"# File {file_num} in subdir {subdir_num}\n", encoding="utf-8"
            )

    # Create some files to be excluded
    (project_dir / "tests").mkdir()
    for i in range(50):
        (project_dir / "tests" / f"test_{i:03d}.py").write_text(f"def test_{i}(): pass\n", encoding="utf-8")

    (project_dir / "__pycache__").mkdir()
    for i in range(30):
        (project_dir / "__pycache__" / f"module_{i}.cpython-38.pyc").write_text("", encoding="utf-8")

    return tmp_path


class TestBenchmark:
    """Benchmark tests for pysourcepack performance."""

    @pytest.mark.benchmark
    def test_pack_large_project_performance(self, large_project_structure, benchmark):
        """Benchmark packing a large project."""

        def pack_operation():
            packer = PySourcePacker(root_dir=large_project_structure)
            return packer.pack_project("large_project")

        result = benchmark(pack_operation)
        assert result is True

        # Verify output was created
        output_dir = large_project_structure / "dist" / "src" / "large_project"
        assert output_dir.exists()
        assert len(list(output_dir.rglob("*.py"))) > 0

    @pytest.mark.benchmark
    def test_multiple_projects_performance(self, large_project_structure, benchmark):
        """Benchmark packing multiple projects."""
        # Create additional projects
        for i in range(5):
            project_dir = large_project_structure / f"project_{i}"
            project_dir.mkdir()
            (project_dir / "pyproject.toml").write_text(f"[project]\nname = 'project_{i}'\n", encoding="utf-8")
            (project_dir / "main.py").write_text(f"print('Project {i}')\n", encoding="utf-8")

        def pack_all_operation():
            packer = PySourcePacker(root_dir=large_project_structure)
            # Simulate packing all projects
            results = []
            for i in range(6):  # 1 large + 5 small projects
                project_name = "large_project" if i == 0 else f"project_{i - 1}"
                results.append(packer.pack_project(project_name))
            return all(results)

        result = benchmark(pack_all_operation)
        assert result is True

    def test_pattern_matching_performance(self, large_project_structure, benchmark):
        """Benchmark pattern matching performance."""
        test_paths = [
            Path("tests/test_something.py"),
            Path("main.py"),
            Path("__pycache__/module.pyc"),
            Path("subdir/file.py"),
            Path("README.md"),
        ] * 100  # Repeat for more iterations

        def match_operation():
            results = []
            for path in test_paths:
                exclude_result = should_exclude(path, {"tests", "*.pyc", "__pycache__"})
                include_result = should_include(path, {"*.py", "README.md"}, {"tests", "*.pyc"})
                results.append((exclude_result, include_result))
            return results

        benchmark(match_operation)
