"""Helpers for qpip virtual environment commands."""

from __future__ import annotations

import os
import platform
import shlex
import subprocess
import sys
from pathlib import Path
from typing import Mapping, Optional, Sequence, TextIO

from .core import format_command

DEFAULT_VENV_DIRNAME = ".venv"

SHELL_LINUX = "linux"
SHELL_MACOS = "macos"
SHELL_WINDOWS_POWERSHELL = "windows-powershell"
SHELL_WINDOWS_BASH = "windows-bash"
SHELL_WINDOWS_CMD = "windows-cmd"

SHELL_OVERRIDE_ALIASES = {
    "linux": SHELL_LINUX,
    "mac": SHELL_MACOS,
    "macos": SHELL_MACOS,
    "darwin": SHELL_MACOS,
    "powershell": SHELL_WINDOWS_POWERSHELL,
    "pwsh": SHELL_WINDOWS_POWERSHELL,
    "windows-powershell": SHELL_WINDOWS_POWERSHELL,
    "bash": SHELL_WINDOWS_BASH,
    "git-bash": SHELL_WINDOWS_BASH,
    "windows-bash": SHELL_WINDOWS_BASH,
    "cmd": SHELL_WINDOWS_CMD,
    "windows-cmd": SHELL_WINDOWS_CMD,
}


class VenvUsageError(ValueError):
    """Raised when qpip virtual-environment usage is invalid."""


def build_venv_command(
    python_executable: Optional[str] = None,
    venv_dirname: str = DEFAULT_VENV_DIRNAME,
) -> list[str]:
    """Build the command used to create the default virtual environment."""
    return [python_executable or sys.executable, "-m", "venv", venv_dirname]


def detect_parent_process_name(os_name: Optional[str] = None) -> str:
    """Best-effort parent process detection for shell selection."""
    current_os = os.name if os_name is None else os_name
    parent_pid = os.getppid()

    if current_os == "nt":
        result = subprocess.run(
            ["tasklist", "/fi", f"PID eq {parent_pid}", "/fo", "csv", "/nh"],
            capture_output=True,
            check=False,
            text=True,
        )
        output = result.stdout.strip().strip('"')
        if not output or output.startswith("INFO:"):
            return ""
        return output.split('","', 1)[0]

    result = subprocess.run(
        ["ps", "-p", str(parent_pid), "-o", "comm="],
        capture_output=True,
        check=False,
        text=True,
    )
    return result.stdout.strip()


def detect_shell(
    env: Optional[Mapping[str, str]] = None,
    platform_system: Optional[str] = None,
    os_name: Optional[str] = None,
    parent_process_name: Optional[str] = None,
) -> str:
    """Detect the current session shell for activation snippets."""
    env_map = os.environ if env is None else env
    platform_name = platform.system() if platform_system is None else platform_system
    current_os = os.name if os_name is None else os_name

    override = env_map.get("QPIP_SHELL", "").strip().lower()
    if override:
        try:
            return SHELL_OVERRIDE_ALIASES[override]
        except KeyError as exc:
            choices = ", ".join(sorted(SHELL_OVERRIDE_ALIASES))
            raise VenvUsageError(
                f"QPIP_SHELL={override!r} 无效，可用值: {choices}"
            ) from exc

    if current_os != "nt":
        if platform_name == "Darwin":
            return SHELL_MACOS
        return SHELL_LINUX

    resolved_parent_name = parent_process_name if parent_process_name is not None else detect_parent_process_name(os_name=current_os)
    parent_name = resolved_parent_name.lower()
    if parent_name.endswith(".exe"):
        parent_name = parent_name[:-4]

    if parent_name in {"bash", "sh", "zsh"}:
        return SHELL_WINDOWS_BASH
    if parent_name in {"powershell", "pwsh"}:
        return SHELL_WINDOWS_POWERSHELL
    if parent_name == "cmd":
        return SHELL_WINDOWS_CMD

    shell_value = env_map.get("SHELL", "").lower()
    if env_map.get("MSYSTEM") or "bash" in shell_value or "zsh" in shell_value:
        return SHELL_WINDOWS_BASH
    if env_map.get("PSModulePath") or env_map.get("POWERSHELL_DISTRIBUTION_CHANNEL"):
        return SHELL_WINDOWS_POWERSHELL
    return SHELL_WINDOWS_CMD


def build_activation_command(
    shell_kind: str,
    venv_dirname: str = DEFAULT_VENV_DIRNAME,
) -> str:
    """Build the shell snippet used to activate the virtual environment."""
    if shell_kind in {SHELL_LINUX, SHELL_MACOS}:
        return f"source {shlex.quote(f'{venv_dirname}/bin/activate')}"
    if shell_kind == SHELL_WINDOWS_BASH:
        return f"source {shlex.quote(f'{venv_dirname}/Scripts/activate')}"
    if shell_kind == SHELL_WINDOWS_POWERSHELL:
        return f"& .\\{venv_dirname}\\Scripts\\Activate.ps1"
    if shell_kind == SHELL_WINDOWS_CMD:
        return f"call .\\{venv_dirname}\\Scripts\\activate.bat"
    raise VenvUsageError(f"不支持的 shell 类型: {shell_kind}")


def build_deactivate_command(shell_kind: str) -> str:
    """Build the shell snippet used to deactivate the current virtual environment."""
    if shell_kind in {SHELL_LINUX, SHELL_MACOS, SHELL_WINDOWS_BASH}:
        return "if command -v deactivate >/dev/null 2>&1; then deactivate; fi"
    if shell_kind == SHELL_WINDOWS_POWERSHELL:
        return "if (Get-Command deactivate -ErrorAction SilentlyContinue) { deactivate }"
    if shell_kind == SHELL_WINDOWS_CMD:
        return "if defined VIRTUAL_ENV call deactivate"
    raise VenvUsageError(f"不支持的 shell 类型: {shell_kind}")


def get_activation_script_path(
    cwd: Path,
    shell_kind: str,
    venv_dirname: str = DEFAULT_VENV_DIRNAME,
) -> Path:
    """Resolve the activation script path for the selected shell."""
    if shell_kind in {SHELL_LINUX, SHELL_MACOS}:
        return cwd / venv_dirname / "bin" / "activate"
    if shell_kind == SHELL_WINDOWS_BASH:
        return cwd / venv_dirname / "Scripts" / "activate"
    if shell_kind == SHELL_WINDOWS_POWERSHELL:
        return cwd / venv_dirname / "Scripts" / "Activate.ps1"
    if shell_kind == SHELL_WINDOWS_CMD:
        return cwd / venv_dirname / "Scripts" / "activate.bat"
    raise VenvUsageError(f"不支持的 shell 类型: {shell_kind}")


def run_venv_command(
    args: Sequence[str],
    cwd: Path,
    output: TextIO = sys.stdout,
    dry_run: bool = False,
    python_executable: Optional[str] = None,
) -> int:
    """Create the default .venv in the current project directory."""
    if tuple(args) != ("venv",):
        raise VenvUsageError("venv 暂不支持额外参数")

    command = build_venv_command(python_executable=python_executable)
    print(f"[qpip] {format_command(command)}", file=output)

    if dry_run:
        return 0

    result = subprocess.run(command, cwd=str(cwd), check=False)
    return result.returncode


def run_activate_command(
    args: Sequence[str],
    cwd: Path,
    output: TextIO = sys.stdout,
    env: Optional[Mapping[str, str]] = None,
    platform_system: Optional[str] = None,
    os_name: Optional[str] = None,
    parent_process_name: Optional[str] = None,
) -> int:
    """Print an activation snippet for the current shell session."""
    if tuple(args) != ("active",):
        raise VenvUsageError("active 暂不支持额外参数")

    shell_kind = detect_shell(
        env=env,
        platform_system=platform_system,
        os_name=os_name,
        parent_process_name=parent_process_name,
    )
    activation_script = get_activation_script_path(cwd, shell_kind)
    if not activation_script.exists():
        raise VenvUsageError(
            f"未找到虚拟环境激活脚本: {activation_script}; 请先运行 qpip venv"
        )

    print(build_activation_command(shell_kind), file=output)
    return 0


def run_deactivate_command(
    args: Sequence[str],
    cwd: Path,
    output: TextIO = sys.stdout,
    env: Optional[Mapping[str, str]] = None,
    platform_system: Optional[str] = None,
    os_name: Optional[str] = None,
    parent_process_name: Optional[str] = None,
) -> int:
    """Print a deactivation snippet for the current shell session."""
    del cwd

    if tuple(args) != ("deactivate",):
        raise VenvUsageError("deactivate 暂不支持额外参数")

    shell_kind = detect_shell(
        env=env,
        platform_system=platform_system,
        os_name=os_name,
        parent_process_name=parent_process_name,
    )
    print(build_deactivate_command(shell_kind), file=output)
    return 0

