"""Integration tests for Python virtual environment workflow.

This module tests the end-to-end virtual environment detection and activation
workflow, including creating temporary projects, running venv-detect.sh, and
verifying that virtual environments are created and activated correctly.
"""

import os
import subprocess
import tempfile
from pathlib import Path

PROJECT_ROOT: Path = Path(__file__).parent.parent.parent
VENV_DETECT_SCRIPT: Path = PROJECT_ROOT / ".claude" / "scripts" / "venv-detect.sh"

# Virtual environment creation can take 10-20 seconds on slow systems,
# allow generous timeout to prevent flaky tests
VENV_CREATION_TIMEOUT_SECONDS: int = 30


def test_venv_detect_creates_named_venv() -> None:
    """Test that venv-detect.sh creates a named venv when none exists.

    This integration test:
    1. Creates a temporary project directory
    2. Creates a temporary env file
    3. Runs venv-detect.sh with proper environment variables
    4. Verifies that a named venv was created in ~/.virtualenvs/<project-name>
    5. Verifies that activation was written to the env file
    6. Cleans up the created venv
    """
    with tempfile.TemporaryDirectory() as tmpdir:
        project_dir: Path = Path(tmpdir) / "test-project-integration"
        project_dir.mkdir()

        env_file: Path = Path(tmpdir) / "env"
        env_file.touch()

        # Run venv-detect.sh
        env: dict[str, str] = os.environ.copy()
        env["CLAUDE_PROJECT_DIR"] = str(project_dir)
        env["CLAUDE_ENV_FILE"] = str(env_file)
        env["HOME"] = tmpdir  # Isolate ~/.virtualenvs to $HOME/.virtualenvs
        env["CLAUDE_VENV_QUIET"] = "true"  # Reduce noise in test output
        # Clear VIRTUAL_ENV to ensure the script doesn't think a venv is active
        env.pop("VIRTUAL_ENV", None)

        result: subprocess.CompletedProcess[str] = subprocess.run(
            ["bash", str(VENV_DETECT_SCRIPT)],
            env=env,
            capture_output=True,
            text=True,
            timeout=VENV_CREATION_TIMEOUT_SECONDS,
        )

        # Verify script succeeded
        assert result.returncode == 0, (
            f"venv-detect.sh failed:\nSTDOUT:\n{result.stdout}\nSTDERR:\n{result.stderr}"
        )

        # Verify named venv was created (script uses $HOME/.virtualenvs/)
        named_venv: Path = Path(tmpdir) / ".virtualenvs" / "test-project-integration"
        assert named_venv.exists(), f"Named venv not created at {named_venv}"
        assert (named_venv / "bin" / "activate").exists(), "activate script not found"
        assert (named_venv / "bin" / "python").exists(), "python executable not found"

        # Verify activation was written to env file
        env_content: str = env_file.read_text()
        assert "source" in env_content, "Activation command not written to env file"
        assert str(named_venv) in env_content, f"Named venv path not in env file: {env_content}"

        # Verify metadata file was created
        metadata_file: Path = named_venv / ".ccc-project-path"
        assert metadata_file.exists(), "Metadata file not created"
        stored_path: str = metadata_file.read_text().strip()
        assert stored_path == str(project_dir), (
            f"Incorrect project path in metadata: {stored_path} != {project_dir}"
        )


def test_venv_detect_uses_existing_local_venv() -> None:
    """Test that venv-detect.sh uses existing .venv when present.

    This integration test:
    1. Creates a temporary project directory with a .venv
    2. Runs venv-detect.sh
    3. Verifies that the existing .venv is used (not a named venv)
    4. Verifies that activation was written to the env file
    """
    with tempfile.TemporaryDirectory() as tmpdir:
        project_dir: Path = Path(tmpdir) / "test-project-local"
        project_dir.mkdir()

        # Create a local .venv
        local_venv: Path = project_dir / ".venv"
        subprocess.run(
            ["python3", "-m", "venv", str(local_venv)],
            check=True,
            capture_output=True,
            timeout=VENV_CREATION_TIMEOUT_SECONDS,
        )

        env_file: Path = Path(tmpdir) / "env"
        env_file.touch()

        # Run venv-detect.sh
        env: dict[str, str] = os.environ.copy()
        env["CLAUDE_PROJECT_DIR"] = str(project_dir)
        env["CLAUDE_ENV_FILE"] = str(env_file)
        env["HOME"] = tmpdir  # Isolate ~/.virtualenvs to $HOME/.virtualenvs
        env["CLAUDE_VENV_QUIET"] = "true"
        # Clear VIRTUAL_ENV to ensure the script doesn't think a venv is active
        env.pop("VIRTUAL_ENV", None)

        result: subprocess.CompletedProcess[str] = subprocess.run(
            ["bash", str(VENV_DETECT_SCRIPT)],
            env=env,
            capture_output=True,
            text=True,
            timeout=VENV_CREATION_TIMEOUT_SECONDS,
        )

        # Verify script succeeded
        assert result.returncode == 0, (
            f"venv-detect.sh failed:\nSTDOUT:\n{result.stdout}\nSTDERR:\n{result.stderr}"
        )

        # Verify named venv was NOT created (local venv should be used instead)
        named_venv: Path = Path(tmpdir) / ".virtualenvs" / "test-project-local"
        assert not named_venv.exists(), "Named venv should not be created when .venv exists"

        # Verify activation uses local .venv
        env_content: str = env_file.read_text()
        assert "source" in env_content, "Activation command not written to env file"
        assert ".venv" in env_content, f"Local .venv path not in env file: {env_content}"


def test_venv_detect_respects_active_venv() -> None:
    """Test that venv-detect.sh respects an already-active virtual environment.

    This integration test:
    1. Creates a temporary project directory
    2. Sets VIRTUAL_ENV to simulate an active venv
    3. Runs venv-detect.sh
    4. Verifies that no new venv is created
    5. Verifies that the activation written to env file references the existing venv
    """
    with tempfile.TemporaryDirectory() as tmpdir:
        project_dir: Path = Path(tmpdir) / "test-project-active"
        project_dir.mkdir()

        # Create a venv to simulate an active one
        active_venv: Path = Path(tmpdir) / "active-venv"
        subprocess.run(
            ["python3", "-m", "venv", str(active_venv)],
            check=True,
            capture_output=True,
            timeout=VENV_CREATION_TIMEOUT_SECONDS,
        )

        env_file: Path = Path(tmpdir) / "env"
        env_file.touch()

        # Run venv-detect.sh with VIRTUAL_ENV set
        env: dict[str, str] = os.environ.copy()
        env["CLAUDE_PROJECT_DIR"] = str(project_dir)
        env["CLAUDE_ENV_FILE"] = str(env_file)
        env["HOME"] = tmpdir  # Isolate ~/.virtualenvs to $HOME/.virtualenvs
        env["VIRTUAL_ENV"] = str(active_venv)
        env["CLAUDE_VENV_QUIET"] = "true"

        result: subprocess.CompletedProcess[str] = subprocess.run(
            ["bash", str(VENV_DETECT_SCRIPT)],
            env=env,
            capture_output=True,
            text=True,
            timeout=VENV_CREATION_TIMEOUT_SECONDS,
        )

        # Verify script succeeded
        assert result.returncode == 0, (
            f"venv-detect.sh failed:\nSTDOUT:\n{result.stdout}\nSTDERR:\n{result.stderr}"
        )

        # Verify no new venv was created
        named_venv: Path = Path(tmpdir) / ".virtualenvs" / "test-project-active"
        assert not named_venv.exists(), "Named venv should not be created when VIRTUAL_ENV is set"

        # Verify activation references the active venv
        env_content: str = env_file.read_text()
        assert str(active_venv) in env_content, f"Active venv path not in env file: {env_content}"


def test_venv_detect_recreates_corrupted_venv() -> None:
    """Test that venv-detect.sh recreates a corrupted venv.

    This integration test:
    1. Creates a corrupted named venv (directory exists but no activate script)
    2. Runs venv-detect.sh
    3. Verifies that the venv was recreated with valid structure
    """
    with tempfile.TemporaryDirectory() as tmpdir:
        project_dir: Path = Path(tmpdir) / "test-project-corrupted"
        project_dir.mkdir()

        # Create a corrupted named venv (directory exists but no activate)
        virtualenvs_dir: Path = Path(tmpdir) / ".virtualenvs"
        virtualenvs_dir.mkdir()
        corrupted_venv: Path = virtualenvs_dir / "test-project-corrupted"
        corrupted_venv.mkdir()
        # Create bin dir but NOT activate script
        (corrupted_venv / "bin").mkdir()

        env_file: Path = Path(tmpdir) / "env"
        env_file.touch()

        env: dict[str, str] = os.environ.copy()
        env["CLAUDE_PROJECT_DIR"] = str(project_dir)
        env["CLAUDE_ENV_FILE"] = str(env_file)
        env["HOME"] = tmpdir  # Isolate ~/.virtualenvs to $HOME/.virtualenvs
        env["CLAUDE_VENV_QUIET"] = "true"
        env.pop("VIRTUAL_ENV", None)

        result: subprocess.CompletedProcess[str] = subprocess.run(
            ["bash", str(VENV_DETECT_SCRIPT)],
            env=env,
            capture_output=True,
            text=True,
            timeout=VENV_CREATION_TIMEOUT_SECONDS,
        )

        # Verify script succeeded
        assert result.returncode == 0, (
            f"venv-detect.sh failed:\nSTDOUT:\n{result.stdout}\nSTDERR:\n{result.stderr}"
        )

        # Verify venv was recreated with valid structure
        assert (corrupted_venv / "bin" / "activate").exists(), (
            "activate script not found after recreation"
        )
        assert (corrupted_venv / "bin" / "python").exists(), (
            "python executable not found after recreation"
        )

        # Verify activation was written to env file
        env_content: str = env_file.read_text()
        assert "source" in env_content, "Activation command not written to env file"


def test_venv_detect_detects_orphans() -> None:
    """Test that venv-detect.sh detects orphaned venvs from renamed directories.

    This integration test:
    1. Creates a current project directory
    2. Creates an "orphan" venv with metadata pointing to current project
       (simulating a directory rename)
    3. Runs venv-detect.sh
    4. Verifies that orphan detection message appears in output
    """
    with tempfile.TemporaryDirectory() as tmpdir:
        # Current project
        project_dir: Path = Path(tmpdir) / "new-project-name"
        project_dir.mkdir()

        # Create an "orphan" venv with metadata pointing to current project
        virtualenvs_dir: Path = Path(tmpdir) / ".virtualenvs"
        virtualenvs_dir.mkdir()
        orphan_venv: Path = virtualenvs_dir / "old-project-name"
        subprocess.run(
            ["python3", "-m", "venv", str(orphan_venv)],
            check=True,
            capture_output=True,
            timeout=VENV_CREATION_TIMEOUT_SECONDS,
        )
        # Write metadata that points to current project (simulating rename)
        (orphan_venv / ".ccc-project-path").write_text(str(project_dir))

        env_file: Path = Path(tmpdir) / "env"
        env_file.touch()

        env: dict[str, str] = os.environ.copy()
        env["CLAUDE_PROJECT_DIR"] = str(project_dir)
        env["CLAUDE_ENV_FILE"] = str(env_file)
        env["HOME"] = tmpdir  # Isolate ~/.virtualenvs to $HOME/.virtualenvs
        env["CLAUDE_VENV_QUIET"] = "false"  # Want to see orphan detection output
        env.pop("VIRTUAL_ENV", None)

        result: subprocess.CompletedProcess[str] = subprocess.run(
            ["bash", str(VENV_DETECT_SCRIPT)],
            env=env,
            capture_output=True,
            text=True,
            timeout=VENV_CREATION_TIMEOUT_SECONDS,
        )

        # Verify script succeeded
        assert result.returncode == 0, (
            f"venv-detect.sh failed:\nSTDOUT:\n{result.stdout}\nSTDERR:\n{result.stderr}"
        )

        # Verify orphan detection message in stderr
        assert "orphan" in result.stderr.lower() or "old-project-name" in result.stderr, (
            f"Orphan detection message not found in stderr:\n{result.stderr}"
        )
