"""Tests for compiler modules."""

import os
import tempfile
from pathlib import Path
from multi_lang_build.compiler.pnpm import PnpmCompiler
from multi_lang_build.compiler.go import GoCompiler
from multi_lang_build.compiler.python import PythonCompiler


class TestPnpmCompiler:
    """Tests for PnpmCompiler."""

    def test_compiler_info(self) -> None:
        """Test compiler information retrieval."""
        compiler = PnpmCompiler()
        info = compiler.get_info()
        
        assert info["name"] == "pnpm"
        assert "supported_mirrors" in info
        assert "default_mirror" in info
        assert "executable" in info

    def test_create_factory(self) -> None:
        """Test factory method create."""
        compiler = PnpmCompiler.create(Path("./test"))
        assert isinstance(compiler, PnpmCompiler)

    def test_name_property(self) -> None:
        """Test name property returns correct value."""
        compiler = PnpmCompiler()
        assert compiler.name == "pnpm"

    def test_supported_mirrors(self) -> None:
        """Test supported mirrors list."""
        compiler = PnpmCompiler()
        mirrors = compiler.supported_mirrors
        
        assert isinstance(mirrors, list)
        assert "npm" in mirrors
        assert "pnpm" in mirrors
        assert "yarn" in mirrors

    def test_find_project_root_in_current_dir(self, tmp_path: Path) -> None:
        """Test finding project root when package.json is in current directory."""
        package_json = tmp_path / "package.json"
        package_json.write_text('{"name": "test"}')
        
        compiler = PnpmCompiler()
        root = compiler.find_project_root(tmp_path)
        
        assert root == tmp_path

    def test_find_project_root_in_parent_dir(self, tmp_path: Path) -> None:
        """Test finding project root when package.json is in parent directory."""
        project_dir = tmp_path / "project"
        project_dir.mkdir()
        package_json = project_dir / "package.json"
        package_json.write_text('{"name": "test"}')
        
        subdir = project_dir / "src" / "components"
        subdir.mkdir(parents=True)
        
        compiler = PnpmCompiler()
        root = compiler.find_project_root(subdir)
        
        assert root == project_dir

    def test_find_project_root_not_found(self, tmp_path: Path) -> None:
        """Test that None is returned when no package.json is found."""
        compiler = PnpmCompiler()
        root = compiler.find_project_root(tmp_path)
        
        assert root is None

    def test_find_project_root_in_subdir(self, tmp_path: Path) -> None:
        """Test finding project root when package.json is in a subdirectory."""
        project_dir = tmp_path / "project"
        project_dir.mkdir()
        package_json = project_dir / "package.json"
        package_json.write_text('{"name": "test"}')
        
        compiler = PnpmCompiler()
        root = compiler.find_project_root(project_dir)
        
        assert root == project_dir

    def test_find_project_root_with_multiple_subdirs(self, tmp_path: Path) -> None:
        """Test finding project root when there are multiple subdirectories."""
        project_dir = tmp_path / "packages" / "ui"
        project_dir.mkdir(parents=True)
        package_json = project_dir / "package.json"
        package_json.write_text('{"name": "ui"}')
        
        sibling_dir = tmp_path / "packages" / "utils"
        sibling_dir.mkdir(parents=True)
        
        compiler = PnpmCompiler()
        root = compiler.find_project_root(sibling_dir)
        
        assert root is None  # No package.json in sibling dir

    def test_clean(self, tmp_path: Path) -> None:
        """Test cleaning pnpm artifacts."""
        project_dir = tmp_path / "project"
        project_dir.mkdir()
        
        node_modules = project_dir / "node_modules"
        node_modules.mkdir()
        (node_modules / "some_package").mkdir()
        
        lock_file = project_dir / "pnpm-lock.yaml"
        lock_file.write_text("lock file content")
        
        compiler = PnpmCompiler()
        result = compiler.clean(project_dir)
        
        assert result is True
        assert not node_modules.exists()
        assert not lock_file.exists()


class TestGoCompiler:
    """Tests for GoCompiler."""

    def test_compiler_info(self) -> None:
        """Test compiler information retrieval."""
        compiler = GoCompiler()
        info = compiler.get_info()
        
        assert info["name"] == "go"
        assert "supported_mirrors" in info
        assert "default_mirror" in info
        assert "executable" in info

    def test_create_factory(self) -> None:
        """Test factory method create."""
        compiler = GoCompiler.create(Path("./test"))
        assert isinstance(compiler, GoCompiler)

    def test_name_property(self) -> None:
        """Test name property returns correct value."""
        compiler = GoCompiler()
        assert compiler.name == "go"

    def test_default_mirror(self) -> None:
        """Test default mirror is goproxy.cn."""
        compiler = GoCompiler()
        assert compiler.DEFAULT_MIRROR == "https://goproxy.cn"

    def test_supported_mirrors(self) -> None:
        """Test supported mirrors list includes all Go mirrors."""
        compiler = GoCompiler()
        mirrors = compiler.supported_mirrors
        
        assert isinstance(mirrors, list)
        assert "go" in mirrors
        assert "go_qiniu" in mirrors
        assert "go_vip" in mirrors

    def test_current_mirror_default(self) -> None:
        """Test current mirror is None by default."""
        compiler = GoCompiler()
        assert compiler.current_mirror == "go"

    def test_set_mirror(self) -> None:
        """Test setting mirror configuration."""
        compiler = GoCompiler()
        
        compiler.set_mirror("go_qiniu")
        assert compiler.current_mirror == "go_qiniu"
        
        compiler.set_mirror("go_vip")
        assert compiler.current_mirror == "go_vip"

    def test_init_with_mirror(self) -> None:
        """Test initializing compiler with mirror."""
        compiler = GoCompiler(mirror="go_qiniu")
        assert compiler.current_mirror == "go_qiniu"

    def test_clean(self, tmp_path: Path) -> None:
        """Test cleaning Go artifacts."""
        project_dir = tmp_path / "project"
        project_dir.mkdir()
        
        go_sum = project_dir / "go.sum"
        go_sum.write_text("sum content")
        
        vendor = project_dir / "vendor"
        vendor.mkdir()
        
        bin_dir = project_dir / "bin"
        bin_dir.mkdir()
        
        compiler = GoCompiler()
        result = compiler.clean(project_dir)
        
        assert result is True
        assert not go_sum.exists()
        assert not vendor.exists()
        assert not bin_dir.exists()

    def test_build_binary_with_target_file(self, tmp_path: Path) -> None:
        """Test building with target file parameter."""
        # Create a mock project structure
        project_dir = tmp_path / "project"
        project_dir.mkdir()
        
        # Create go.mod
        go_mod = project_dir / "go.mod"
        go_mod.write_text("module test\n\ngo 1.21\n")
        
        # Create multiple main files in same directory
        server_go = project_dir / "server.go"
        server_go.write_text("package main\nfunc main() {}\n")
        
        client_go = project_dir / "client.go"
        client_go.write_text("package main\nfunc main() {}\n")
        
        compiler = GoCompiler()
        
        # Test that target parameter is accepted
        # Note: This test validates the API interface, actual Go build requires Go installation
        result = compiler.build_binary(
            source_dir=project_dir,
            output_path=tmp_path / "bin" / "server",
            target="server.go",
            mirror_enabled=False,
        )
        
        # The command should include the target
        # If Go is not installed, this will fail with executable not found
        # But we're testing the interface, not the actual build
        assert isinstance(result, dict)
        assert "success" in result

    def test_build_binary_with_target_directory(self, tmp_path: Path) -> None:
        """Test building with target directory parameter."""
        # Create a mock project structure
        project_dir = tmp_path / "project"
        project_dir.mkdir()
        
        # Create go.mod
        go_mod = project_dir / "go.mod"
        go_mod.write_text("module test\n\ngo 1.21\n")
        
        # Create cmd/server structure
        cmd_dir = project_dir / "cmd" / "server"
        cmd_dir.mkdir(parents=True)
        main_go = cmd_dir / "main.go"
        main_go.write_text("package main\nfunc main() {}\n")
        
        compiler = GoCompiler()
        
        # Test that target directory parameter is accepted
        result = compiler.build_binary(
            source_dir=project_dir,
            output_path=tmp_path / "bin" / "server",
            target="./cmd/server",
            mirror_enabled=False,
        )
        
        assert isinstance(result, dict)
        assert "success" in result


class TestPythonCompiler:
    """Tests for PythonCompiler."""

    def test_compiler_info(self) -> None:
        """Test compiler information retrieval."""
        compiler = PythonCompiler()
        info = compiler.get_info()
        
        assert info["name"] == "python"
        assert "supported_mirrors" in info
        assert "default_mirror" in info
        assert "executable" in info

    def test_create_factory(self) -> None:
        """Test factory method create."""
        compiler = PythonCompiler.create(Path("./test"))
        assert isinstance(compiler, PythonCompiler)

    def test_name_property(self) -> None:
        """Test name property returns correct value."""
        compiler = PythonCompiler()
        assert compiler.name == "python"

    def test_default_mirror(self) -> None:
        """Test default mirror is Tsinghua."""
        compiler = PythonCompiler()
        assert compiler.DEFAULT_MIRROR == "https://pypi.tuna.tsinghua.edu.cn"

    def test_supported_mirrors(self) -> None:
        """Test supported mirrors list includes all Pip mirrors."""
        compiler = PythonCompiler()
        mirrors = compiler.supported_mirrors
        
        assert isinstance(mirrors, list)
        assert "pip" in mirrors
        assert "pip_aliyun" in mirrors
        assert "pip_douban" in mirrors
        assert "pip_huawei" in mirrors

    def test_current_mirror_default(self) -> None:
        """Test current mirror is None by default."""
        compiler = PythonCompiler()
        assert compiler.current_mirror == "pip"

    def test_set_mirror(self) -> None:
        """Test setting mirror configuration."""
        compiler = PythonCompiler()
        
        compiler.set_mirror("pip_aliyun")
        assert compiler.current_mirror == "pip_aliyun"
        
        compiler.set_mirror("pip_douban")
        assert compiler.current_mirror == "pip_douban"

    def test_init_with_mirror(self) -> None:
        """Test initializing compiler with mirror."""
        compiler = PythonCompiler(mirror="pip_huawei")
        assert compiler.current_mirror == "pip_huawei"

    def test_detect_build_system_poetry(self, tmp_path: Path) -> None:
        """Test build system detection for poetry."""
        pyproject = tmp_path / "pyproject.toml"
        pyproject.write_text(
            "[tool.poetry]\n"
            "name = 'test'\n"
            "version = '0.1.0'\n"
        )
        
        compiler = PythonCompiler()
        build_system = compiler._detect_build_system(tmp_path)
        
        assert build_system == "poetry"

    def test_detect_build_system_setuptools(self, tmp_path: Path) -> None:
        """Test build system detection for setuptools."""
        setup_py = tmp_path / "setup.py"
        setup_py.write_text(
            "from setuptools import setup\n"
            "setup(name='test', version='0.1.0')\n"
        )
        
        compiler = PythonCompiler()
        build_system = compiler._detect_build_system(tmp_path)
        
        assert build_system == "setuptools"

    def test_detect_build_system_pdm(self, tmp_path: Path) -> None:
        """Test build system detection for PDM."""
        pyproject = tmp_path / "pyproject.toml"
        pyproject.write_text(
            "[project]\n"
            "name = 'test'\n"
            "version = '0.1.0'\n"
            "\n[tool.pdm]\n"
        )
        
        compiler = PythonCompiler()
        build_system = compiler._detect_build_system(tmp_path)
        
        assert build_system == "pdm"

    def test_detect_build_system_none(self, tmp_path: Path) -> None:
        """Test build system detection returns none when no build system found."""
        compiler = PythonCompiler()
        build_system = compiler._detect_build_system(tmp_path)
        
        assert build_system == "none"

    def test_clean(self, tmp_path: Path) -> None:
        """Test cleaning Python artifacts."""
        project_dir = tmp_path / "project"
        project_dir.mkdir()
        
        # Create __pycache__
        pycache = project_dir / "__pycache__"
        pycache.mkdir()
        (pycache / "module.pyc").write_text("")
        
        # Create .pyc file
        (project_dir / "test.pyc").write_text("")
        
        # Create pytest_cache
        pytest_cache = project_dir / ".pytest_cache"
        pytest_cache.mkdir()
        
        # Create dist
        dist = project_dir / "dist"
        dist.mkdir()
        
        # Create build
        build = project_dir / "build"
        build.mkdir()
        
        # Create egg-info
        egg_info = project_dir / "test-0.1.0.egg-info"
        egg_info.mkdir()
        
        compiler = PythonCompiler()
        result = compiler.clean(project_dir)
        
        assert result is True
        assert not pycache.exists()
        assert not (project_dir / "test.pyc").exists()
        assert not pytest_cache.exists()
        assert not dist.exists()
        assert not build.exists()
        assert not egg_info.exists()
