"""Benchmark tests for pyloadergen module.

These tests measure performance of key operations and are excluded from regular test runs.
Run with: pytest --benchmark-only
"""

from __future__ import annotations

import tempfile
from pathlib import Path
from unittest import mock

import pytest

from sfi.pyloadergen import pyloadergen
from sfi.pyloadergen.pyloadergen import ProjectInfo


class BenchmarkDetectLoaderType:
    """Benchmark loader type detection performance."""

    @pytest.fixture(scope="module")
    def project_info_large(self):
        """Create ProjectInfo with large dependency list for benchmarking."""
        return ProjectInfo(
            name="benchmark_project",
            dependencies=[f"package_{i}" for i in range(1000)],
            keywords=["gui"] * 50 + ["test"] * 50,
        )

    @pytest.fixture(scope="module")
    def project_info_qt(self):
        """Create ProjectInfo with Qt dependencies."""
        return ProjectInfo(
            name="qt_project",
            dependencies=["pyside2", "requests", "numpy", "pandas"],
            keywords=["gui", "desktop"],
        )

    @pytest.mark.benchmark
    def test_detect_loader_type_large_project(self, benchmark, project_info_large):
        """Benchmark loader type detection for large project with many dependencies."""
        result = benchmark(pyloadergen._detect_loader_type, project_info_large)
        assert result == ("gui", False)

    @pytest.mark.benchmark
    def test_detect_loader_type_qt_project(self, benchmark, project_info_qt):
        """Benchmark loader type detection for Qt project."""
        result = benchmark(pyloadergen._detect_loader_type, project_info_qt)
        assert result == ("gui", True)


class BenchmarkPrepareEntryFile:
    """Benchmark entry file preparation performance."""

    @pytest.mark.benchmark
    def test_prepare_entry_file_without_qt(self, benchmark):
        """Benchmark entry file preparation without Qt configuration."""
        result = benchmark(pyloadergen.prepare_entry_file, "test_project", False)
        assert "test_project" in result
        assert "$QT_CONFIG" not in result

    @pytest.mark.benchmark
    def test_prepare_entry_file_with_qt(self, benchmark):
        """Benchmark entry file preparation with Qt configuration."""
        result = benchmark(pyloadergen.prepare_entry_file, "test_project", True)
        assert "test_project" in result
        assert "qt_dir" in result


class BenchmarkPrepareCSource:
    """Benchmark C source preparation performance."""

    @pytest.mark.benchmark
    def test_prepare_c_source_debug_mode(self, benchmark):
        """Benchmark C source preparation in debug mode."""
        template = pyloadergen._WINDOWS_GUI_TEMPLATE
        result = benchmark(pyloadergen.prepare_c_source, template, "test.ent", True)
        assert "${DEBUG_MODE}" not in result
        assert "${ENTRY_FILE}" not in result

    @pytest.mark.benchmark
    def test_prepare_c_source_release_mode(self, benchmark):
        """Benchmark C source preparation in release mode."""
        template = pyloadergen._WINDOWS_GUI_TEMPLATE
        result = benchmark(pyloadergen.prepare_c_source, template, "test.ent", False)
        assert "${DEBUG_MODE}" not in result
        assert "${ENTRY_FILE}" not in result


class BenchmarkGetCompilerArgs:
    """Benchmark compiler argument retrieval performance."""

    @pytest.mark.benchmark
    def test_get_compiler_args_gcc(self, benchmark):
        """Benchmark GCC compiler arguments retrieval."""
        args = benchmark(pyloadergen.get_compiler_args, "gcc")
        assert "-std=c99" in args

    @pytest.mark.benchmark
    def test_get_compiler_args_clang(self, benchmark):
        """Benchmark Clang compiler arguments retrieval."""
        args = benchmark(pyloadergen.get_compiler_args, "clang")
        assert "-std=c99" in args

    @pytest.mark.benchmark
    def test_get_compiler_args_cl(self, benchmark):
        """Benchmark MSVC compiler arguments retrieval."""
        args = benchmark(pyloadergen.get_compiler_args, "cl")
        assert "/std:c99" in args


class BenchmarkProjectInfoOperations:
    """Benchmark ProjectInfo dataclass operations."""

    @pytest.fixture(scope="module")
    def project_data(self):
        """Sample project data for benchmarking."""
        return {
            "version": "1.0.0",
            "description": "Benchmark test project",
            "dependencies": [f"package_{i}" for i in range(100)],
            "keywords": ["gui", "test", "benchmark"],
            "scripts": {"main": "src.main:main", "cli": "src.cli:main"},
        }

    @pytest.mark.benchmark
    def test_create_project_info_from_dict(self, benchmark, project_data):
        """Benchmark ProjectInfo creation from dictionary."""
        project_info = benchmark(ProjectInfo.from_dict, "benchmark_project", project_data)
        assert project_info.name == "benchmark_project"

    @pytest.mark.benchmark
    def test_project_info_to_dict(self, benchmark, project_data):
        """Benchmark ProjectInfo conversion to dictionary."""
        project_info = ProjectInfo.from_dict("benchmark_project", project_data)
        result = benchmark(project_info.to_dict)
        assert result["name"] == "benchmark_project"


class BenchmarkGetEntryModule:
    """Benchmark entry module retrieval performance."""

    @pytest.fixture(scope="module")
    def project_info_with_scripts(self):
        """ProjectInfo with multiple scripts."""
        return ProjectInfo(
            name="my-app",
            scripts={
                "main": "src.my_app.main:main",
                "cli": "src.my_app.cli:main",
                "gui": "src.my_app.gui:main",
            },
        )

    @pytest.mark.benchmark
    def test_get_entry_module_with_scripts(self, benchmark, project_info_with_scripts):
        """Benchmark entry module retrieval with scripts defined."""
        entry_module = benchmark(pyloadergen._get_entry_module, project_info_with_scripts)
        assert entry_module == "src.my_app.main"

    @pytest.mark.benchmark
    def test_get_entry_module_default(self, benchmark):
        """Benchmark entry module retrieval with default naming."""
        project_info = ProjectInfo(name="test-project", scripts={})
        entry_module = benchmark(pyloadergen._get_entry_module, project_info)
        assert entry_module == "test_project"


class BenchmarkGenerateLoaderForProject:
    """Benchmark complete loader generation process."""

    @pytest.fixture(scope="module")
    def project_info(self):
        """Sample project info for benchmarking."""
        return ProjectInfo(
            name="benchmark_app",
            version="1.0.0",
            description="Benchmark test application",
            dependencies=["requests", "flask", "pyside2"],
            keywords=["gui", "web"],
            scripts={"main": "src.benchmark_app:main"},
        )

    @pytest.mark.benchmark
    def test_generate_loader_mock_compilation(self, benchmark, project_info):
        """Benchmark loader generation with mocked compilation."""
        with tempfile.TemporaryDirectory() as temp_dir:
            project_dir = Path(temp_dir)

            # Mock compilation to avoid actual compiler calls
            with mock.patch("subprocess.run") as mock_run:
                mock_run.return_value = mock.Mock(returncode=0, stdout="", stderr="")

                def generate():
                    return pyloadergen.generate_loader_for_project(
                        project_dir=project_dir,
                        project_info=project_info,
                        is_debug=False,
                        compiler="gcc",
                    )

                success = benchmark(generate)
                assert success is True


class BenchmarkSelectTemplate:
    """Benchmark template selection performance."""

    @pytest.mark.benchmark
    def test_select_c_template_windows_gui(self, benchmark):
        """Benchmark Windows GUI template selection."""
        with mock.patch("sfi.pyloadergen.pyloadergen.is_windows", True), mock.patch(
            "sfi.pyloadergen.pyloadergen.is_macos", False
        ):
            template = benchmark(pyloadergen.select_c_template, "gui", False)
            assert template == pyloadergen._WINDOWS_GUI_TEMPLATE

    @pytest.mark.benchmark
    def test_select_c_template_windows_console(self, benchmark):
        """Benchmark Windows console template selection."""
        with mock.patch("sfi.pyloadergen.pyloadergen.is_windows", True), mock.patch(
            "sfi.pyloadergen.pyloadergen.is_macos", False
        ):
            template = benchmark(pyloadergen.select_c_template, "console", False)
            assert template == pyloadergen._WINDOWS_CONSOLE_TEMPLATE

    @pytest.mark.benchmark
    def test_select_c_template_macos_gui(self, benchmark):
        """Benchmark macOS GUI template selection."""
        with mock.patch("sfi.pyloadergen.pyloadergen.is_windows", False), mock.patch(
            "sfi.pyloadergen.pyloadergen.is_macos", True
        ):
            template = benchmark(pyloadergen.select_c_template, "gui", False)
            assert template == pyloadergen._MACOS_GUI_TEMPLATE
