"""
Virtual environment creation, activation, and deactivation logic.
Cross-platform: Windows + macOS/Linux.
"""

from __future__ import annotations

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

from venvy.core.config import VenvyConfig, save_config
from venvy.utils.console import console


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


def get_venv_python(venv_path: Path) -> Path:
    if IS_WINDOWS:
        return venv_path / "Scripts" / "python.exe"
    return venv_path / "bin" / "python"


def get_venv_pip(venv_path: Path) -> Path:
    if IS_WINDOWS:
        return venv_path / "Scripts" / "pip.exe"
    return venv_path / "bin" / "pip"


def get_activate_script(venv_path: Path) -> Tuple[str, str]:
    """
    Returns (shell_command, note) for activating the venv.
    Because child processes cannot modify the parent shell's environment,
    we print the command the user must run (or source via eval).
    """
    if IS_WINDOWS:
        script = venv_path / "Scripts" / "activate.bat"
        return str(script), "cmd /k"
    else:
        script = venv_path / "bin" / "activate"
        return f"source {script}", "bash/zsh"


def create_venv(
    cwd: Path,
    config: VenvyConfig,
) -> bool:
    """
    Create a virtual environment and optionally install ipykernel.
    Returns True on success.
    """
    venv_path = cwd / config.venv_name

    if venv_path.exists():
        console.print(
            f"[yellow]⚠[/yellow]  Virtual environment already exists at [bold]{venv_path}[/bold]"
        )
        return False

    console.print(f"[cyan]→[/cyan] Creating virtual environment at [bold]{config.venv_name}[/bold]...")
    result = subprocess.run(
        [sys.executable, "-m", "venv", str(venv_path)],
        capture_output=True,
        text=True,
    )

    if result.returncode != 0:
        console.print(f"[red]✗[/red] Failed to create venv:\n{result.stderr}")
        return False

    console.print("[green]✓[/green] Virtual environment created.")

    # Upgrade pip silently
    _run_in_venv(venv_path, ["-m", "pip", "install", "--upgrade", "pip", "-q"])

    if config.install_ipykernel:
        console.print("[cyan]→[/cyan] Installing ipykernel...")
        ok = _run_in_venv(venv_path, ["-m", "pip", "install", "ipykernel", "-q"])
        if ok:
            # Register kernel
            _run_in_venv(
                venv_path,
                ["-m", "ipykernel", "install", "--user", f"--name={cwd.name}"],
            )
            console.print(f"[green]✓[/green] ipykernel installed and registered as [bold]{cwd.name}[/bold].")
        else:
            console.print("[yellow]⚠[/yellow]  ipykernel installation failed — skipping.")

    # Install from requirements file if it exists
    req_file = cwd / config.requirements_file
    if req_file.exists():
        console.print(f"[cyan]→[/cyan] Installing packages from [bold]{config.requirements_file}[/bold]...")
        _run_in_venv(venv_path, ["-m", "pip", "install", "-r", str(req_file), "-q"])
        console.print("[green]✓[/green] Packages installed.")

    # Save config
    save_config(config, cwd)

    # Print activation instructions
    _print_activation_hint(venv_path, cwd)

    return True


def _run_in_venv(venv_path: Path, args: list) -> bool:
    python = get_venv_python(venv_path)
    result = subprocess.run(
        [str(python)] + args,
        capture_output=True,
        text=True,
    )
    return result.returncode == 0


def _print_activation_hint(venv_path: Path, cwd: Path) -> None:
    if IS_WINDOWS:
        activate = venv_path / "Scripts" / "activate.bat"
        console.print(
            f"\n[bold green]✓ Virtual environment ready![/bold green]\n"
            f"[dim]Activate with:[/dim]\n"
            f"  [bold cyan]{activate}[/bold cyan]\n"
            f"[dim]Or use:[/dim]\n"
            f"  [bold cyan]venvy +[/bold cyan]  (activate)\n"
            f"  [bold cyan]venvy -[/bold cyan]  (deactivate)\n"
        )
    else:
        activate = venv_path / "bin" / "activate"
        console.print(
            f"\n[bold green]✓ Virtual environment ready![/bold green]\n"
            f"[dim]Activate with:[/dim]\n"
            f"  [bold cyan]source {activate}[/bold cyan]\n"
            f"[dim]Or use the venvy shell helper:[/dim]\n"
            f"  [bold cyan]venvy +[/bold cyan]  (activate)\n"
            f"  [bold cyan]venvy -[/bold cyan]  (deactivate)\n"
        )


def print_activation_command(venv_path: Path) -> None:
    """Print the command to activate the venv (user must eval/run it)."""
    if IS_WINDOWS:
        script = venv_path / "Scripts" / "activate.bat"
        console.print(f"[bold cyan]{script}[/bold cyan]")
    else:
        script = venv_path / "bin" / "activate"
        console.print(f"[bold cyan]source {script}[/bold cyan]")


def print_deactivation_command() -> None:
    """Print the command to deactivate the venv."""
    console.print("[bold cyan]deactivate[/bold cyan]")


def find_venv(cwd: Optional[Path] = None) -> Optional[Path]:
    """
    Find a venv in the current or parent directories.
    Checks .venvy config first, then common names.
    """
    from venvy.core.config import load_config, find_project_root

    base = cwd or Path.cwd()

    # Check venvy config
    root = find_project_root(base)
    if root:
        cfg = load_config(root)
        if cfg:
            venv_path = root / cfg.venv_name
            if venv_path.exists():
                return venv_path

    # Fall back to common venv directory names
    for name in (".venv", "venv", "env", ".env"):
        candidate = base / name
        if candidate.is_dir() and (get_venv_python(candidate)).exists():
            return candidate

    return None
