"""Tests for project overlay infrastructure."""

from __future__ import annotations

from pathlib import Path

import pytest

from codeflash_python.pipeline._eval_worktree import (
    cleanup_overlay,
    create_project_overlay,
)


def _make_project(tmp_path: Path) -> tuple[Path, Path]:
    """Build a minimal src-layout project.

    Returns (project_root, module_path).
    """
    root = tmp_path / "project"
    root.mkdir()
    (root / "pyproject.toml").write_text("[project]\nname='demo'\n")
    src = root / "src"
    pkg = src / "mypkg"
    pkg.mkdir(parents=True)
    (pkg / "__init__.py").write_text("# init\n")
    (pkg / "util.py").write_text("def helper(): ...\n")
    mod = pkg / "core.py"
    mod.write_text("def original(): ...\n")
    return root, mod


class TestCreateProjectOverlay:
    """create_project_overlay directory structure."""

    def test_overlay_contains_candidate_code(self, tmp_path: Path) -> None:
        """The target module file has the candidate code."""
        root, mod = _make_project(tmp_path)
        overlay = create_project_overlay(mod, root, "def fast(): ...")
        try:
            target = overlay / "src" / "mypkg" / "core.py"
            assert target.exists()
            assert "def fast(): ..." == target.read_text("utf-8")
        finally:
            cleanup_overlay(overlay)

    def test_siblings_are_symlinked(self, tmp_path: Path) -> None:
        """Other files in the same package are symlinks."""
        root, mod = _make_project(tmp_path)
        overlay = create_project_overlay(mod, root, "def fast(): ...")
        try:
            init_link = overlay / "src" / "mypkg" / "__init__.py"
            util_link = overlay / "src" / "mypkg" / "util.py"
            assert init_link.is_symlink()
            assert util_link.is_symlink()
            assert init_link.resolve() == (
                root / "src" / "mypkg" / "__init__.py"
            )
        finally:
            cleanup_overlay(overlay)

    def test_project_root_siblings_are_symlinked(self, tmp_path: Path) -> None:
        """Files at project root level are symlinked."""
        root, mod = _make_project(tmp_path)
        overlay = create_project_overlay(mod, root, "code")
        try:
            pyproject = overlay / "pyproject.toml"
            assert pyproject.is_symlink()
            assert pyproject.resolve() == root / "pyproject.toml"
        finally:
            cleanup_overlay(overlay)

    def test_intermediate_dir_siblings_are_symlinked(
        self, tmp_path: Path
    ) -> None:
        """Directories on the path to the module are real, not symlinks."""
        root, mod = _make_project(tmp_path)
        overlay = create_project_overlay(mod, root, "code")
        try:
            src_over = overlay / "src"
            assert src_over.is_dir()
            assert not src_over.is_symlink()
            mypkg_over = src_over / "mypkg"
            assert mypkg_over.is_dir()
            assert not mypkg_over.is_symlink()
        finally:
            cleanup_overlay(overlay)

    def test_target_is_not_symlink(self, tmp_path: Path) -> None:
        """The candidate file is a real file, not a symlink."""
        root, mod = _make_project(tmp_path)
        overlay = create_project_overlay(mod, root, "def fast(): ...")
        try:
            assert not (overlay / "src" / "mypkg" / "core.py").is_symlink()
        finally:
            cleanup_overlay(overlay)

    def test_cleanup_removes_overlay(self, tmp_path: Path) -> None:
        """cleanup_overlay removes the entire directory."""
        root, mod = _make_project(tmp_path)
        overlay = create_project_overlay(mod, root, "code")
        cleanup_overlay(overlay)
        assert not overlay.exists()

    def test_module_not_under_project_root_raises(
        self, tmp_path: Path
    ) -> None:
        """Raises ValueError when module_path is outside project_root."""
        other = tmp_path / "other" / "mod.py"
        other.parent.mkdir(parents=True)
        other.write_text("x = 1\n")
        project_root = tmp_path / "project"
        project_root.mkdir()
        with pytest.raises(ValueError, match="is not in the subpath of"):
            create_project_overlay(other, project_root, "x = 2")

    def test_flat_layout_module(self, tmp_path: Path) -> None:
        """Works for flat-layout projects where module is at root."""
        root = tmp_path / "project"
        root.mkdir()
        mod = root / "mymodule.py"
        mod.write_text("def old(): ...\n")
        (root / "setup.py").write_text("# setup\n")

        overlay = create_project_overlay(mod, root, "def new(): ...")
        try:
            target = overlay / "mymodule.py"
            assert target.exists()
            assert not target.is_symlink()
            assert "def new(): ..." == target.read_text("utf-8")
            setup_link = overlay / "setup.py"
            assert setup_link.is_symlink()
        finally:
            cleanup_overlay(overlay)

    def test_overlay_usable_as_cwd(self, tmp_path: Path) -> None:
        """A subprocess with cwd=overlay can import the candidate."""
        import subprocess
        import sys

        root, mod = _make_project(tmp_path)
        overlay = create_project_overlay(mod, root, "VALUE = 42\n")
        try:
            result = subprocess.run(
                [
                    sys.executable,
                    "-c",
                    "import sys; sys.path.insert(0, 'src'); "
                    "from mypkg.core import VALUE; "
                    "print(VALUE)",
                ],
                cwd=overlay,
                capture_output=True,
                text=True,
                check=True,
            )
            assert "42" == result.stdout.strip()
        finally:
            cleanup_overlay(overlay)
