"""
Injects the venvy pip wrapper into a virtual environment.

On Windows, pip lives at Scripts/pip.exe — we can't easily replace it.
Instead we write a pip.bat that intercepts calls, plus a pip script.

On Unix, pip is at bin/pip — a simple script replacement works.

Strategy:
  1. Backup the original pip entry point
  2. Write our wrapper as the new pip entry point
  3. The wrapper calls `python -m pip` (real pip) then updates requirements
"""
from __future__ import annotations

import platform
import sys
from pathlib import Path

IS_WINDOWS = platform.system() == "Windows"


def get_pip_paths(venv: Path):
    """Return all pip entry points we need to wrap in this venv."""
    if IS_WINDOWS:
        scripts = venv / "Scripts"
        # pip, pip3, pip3.x — cover all of them
        return [scripts / "pip.exe", scripts / "pip3.exe"], scripts / "venvy_pip_real.py"
    else:
        bin_dir = venv / "bin"
        return [bin_dir / "pip", bin_dir / "pip3"], bin_dir / "venvy_pip_real.py"


def inject(venv: Path) -> bool:
    """
    Inject the venvy pip wrapper into the venv.
    Returns True on success.
    """
    from venvy.core.pip_wrapper import WRAPPER_SCRIPT

    if IS_WINDOWS:
        _inject_windows(venv, WRAPPER_SCRIPT)
    else:
        _inject_unix(venv, WRAPPER_SCRIPT)

    return True


def _inject_unix(venv: Path, wrapper: str) -> None:
    """Replace bin/pip and bin/pip3 with our wrapper script."""
    bin_dir = venv / "bin"
    python = venv / "bin" / "python"

    shebang = f"#!{python}\n"
    script_content = shebang + wrapper

    for name in ("pip", "pip3"):
        pip_path = bin_dir / name
        if pip_path.exists():
            # Backup original (keep it for direct access if needed)
            backup = bin_dir / f"_venvy_orig_{name}"
            if not backup.exists():
                backup.write_bytes(pip_path.read_bytes())
            # Write wrapper
            pip_path.write_text(script_content, "utf-8")
            pip_path.chmod(0o755)


def _inject_windows(venv: Path, wrapper: str) -> None:
    """
    On Windows, pip.exe is a binary launcher we can't replace easily.
    Instead we write a pip.bat that takes precedence over pip.exe
    when called from CMD or PowerShell, because .bat is checked before .exe
    when using the Scripts directory that's on PATH.
    
    We also write a pip3.bat for completeness.
    """
    scripts = venv / "Scripts"
    python = scripts / "python.exe"
    # If python.exe isn't in Scripts, use the venv's python
    if not python.exists():
        python = venv / "python.exe"

    # Write the wrapper as a .py file
    wrapper_py = scripts / "_venvy_pip_wrapper.py"
    wrapper_py.write_text(wrapper, "utf-8")

    # Write .bat files that call our wrapper py
    bat_content = f'@echo off\n"{python}" "{wrapper_py}" %*\n'

    for name in ("pip.bat", "pip3.bat"):
        bat_path = scripts / name
        bat_path.write_text(bat_content, "utf-8")

    # Also write pip (no extension) for PowerShell users who call `pip`
    # PowerShell prefers .bat over .exe when both exist in same dir
    # This handles: pip install flask  in both CMD and PS


def is_injected(venv: Path) -> bool:
    """Check if venvy wrapper is already injected in this venv."""
    if IS_WINDOWS:
        return (venv / "Scripts" / "_venvy_pip_wrapper.py").exists()
    else:
        backup = venv / "bin" / "_venvy_orig_pip"
        return backup.exists()


def remove_injection(venv: Path) -> bool:
    """Remove the venvy wrapper and restore original pip."""
    if IS_WINDOWS:
        scripts = venv / "Scripts"
        wrapper = scripts / "_venvy_pip_wrapper.py"
        for bat in [scripts / "pip.bat", scripts / "pip3.bat"]:
            if bat.exists():
                bat.unlink()
        if wrapper.exists():
            wrapper.unlink()
    else:
        bin_dir = venv / "bin"
        for name in ("pip", "pip3"):
            backup = bin_dir / f"_venvy_orig_{name}"
            if backup.exists():
                (bin_dir / name).write_bytes(backup.read_bytes())
                (bin_dir / name).chmod(0o755)
                backup.unlink()
    return True
