"""
venvy.core.venv
~~~~~~~~~~~~~~~
Handles creation, activation scripts, and detection of virtual environments.
Works cross-platform (Windows + macOS/Linux).
"""

from __future__ import annotations

import os
import platform
import subprocess
import sys
from pathlib import Path
from typing import Optional, Tuple


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


class VenvManager:
    """Manages virtual environment lifecycle."""

    def __init__(self, project_root: Path, venv_name: str = ".venv") -> None:
        self.project_root = project_root
        self.venv_name = venv_name
        self.venv_path = project_root / venv_name

    @property
    def exists(self) -> bool:
        return self.venv_path.exists() and self._has_python()

    def _has_python(self) -> bool:
        return self.python_executable.exists()

    @property
    def python_executable(self) -> Path:
        if IS_WINDOWS:
            return self.venv_path / "Scripts" / "python.exe"
        return self.venv_path / "bin" / "python"

    @property
    def pip_executable(self) -> Path:
        if IS_WINDOWS:
            return self.venv_path / "Scripts" / "pip.exe"
        return self.venv_path / "bin" / "pip"

    @property
    def activate_script(self) -> Path:
        if IS_WINDOWS:
            return self.venv_path / "Scripts" / "Activate.ps1"
        return self.venv_path / "bin" / "activate"

    def is_active(self) -> bool:
        """Check if THIS venv is the currently active venv."""
        active = os.environ.get("VIRTUAL_ENV")
        if not active:
            return False
        return Path(active).resolve() == self.venv_path.resolve()

    def create(self, python_executable: Optional[str] = None) -> Tuple[bool, str]:
        """Create the virtual environment. Returns (success, message)."""
        python = python_executable or sys.executable
        try:
            subprocess.run(
                [python, "-m", "venv", str(self.venv_path)],
                check=True,
                capture_output=True,
                text=True,
            )
            return True, f"Virtual environment created at '{self.venv_path}'"
        except subprocess.CalledProcessError as e:
            return False, f"Failed to create venv: {e.stderr}"
        except FileNotFoundError:
            return False, f"Python executable not found: {python}"

    def install_ipykernel(self, project_name: str = "venvy-env") -> Tuple[bool, str]:
        """Install ipykernel into the venv."""
        try:
            subprocess.run(
                [
                    str(self.pip_executable),
                    "install",
                    "ipykernel",
                ],
                check=True,
                capture_output=True,
                text=True,
            )
            # Register the kernel
            subprocess.run(
                [
                    str(self.python_executable),
                    "-m",
                    "ipykernel",
                    "install",
                    "--user",
                    "--name",
                    project_name,
                    "--display-name",
                    f"Python ({project_name})",
                ],
                check=True,
                capture_output=True,
                text=True,
            )
            return True, "ipykernel installed and registered."
        except subprocess.CalledProcessError as e:
            return False, f"Failed to install ipykernel: {e.stderr}"

    def get_activation_command(self) -> str:
        """Return the shell command to activate the venv (for display only)."""
        rel = self.venv_path.relative_to(self.project_root)
        if IS_WINDOWS:
            return f".\\{rel}\\Scripts\\Activate.ps1"
        return f"source {rel}/bin/activate"

    def get_deactivation_command(self) -> str:
        return "deactivate"

    def generate_activation_script(self, shell: str = "auto") -> str:
        """
        Generate a shell script snippet that can be eval'd to activate the venv.
        Returns the path to a temp script that the shell wrapper will source.
        """
        activate_path = str(self.activate_script)
        if IS_WINDOWS:
            return f"& '{activate_path}'"
        else:
            return f"source '{activate_path}'"

    def get_installed_packages(self) -> dict[str, str]:
        """Returns dict of {package_name: version} installed in the venv."""
        try:
            result = subprocess.run(
                [str(self.pip_executable), "list", "--format=json"],
                check=True,
                capture_output=True,
                text=True,
            )
            import json
            packages = json.loads(result.stdout)
            return {p["name"].lower(): p["version"] for p in packages}
        except (subprocess.CalledProcessError, Exception):
            return {}

    @staticmethod
    def detect_active_venv() -> Optional[Path]:
        """Return the path of the currently active virtual environment, if any."""
        active = os.environ.get("VIRTUAL_ENV")
        return Path(active) if active else None

    @staticmethod
    def find_venv_in_project(project_root: Path) -> Optional[Path]:
        """Scan for common venv directory names in project root."""
        candidates = [".venv", "venv", "env", ".env"]
        for name in candidates:
            candidate = project_root / name
            if candidate.is_dir():
                python = (
                    candidate / "Scripts" / "python.exe"
                    if IS_WINDOWS
                    else candidate / "bin" / "python"
                )
                if python.exists():
                    return candidate
        return None
