"""Core helpers for running pip against curated package mirrors."""

from __future__ import annotations

import os
import shlex
import subprocess
import sys
from typing import Mapping, Optional, Sequence, Tuple

from .project_config import ProjectConfig

DEFAULT_MIRROR = "ali"
DEFAULT_MIRROR_ENV = "QPIP_DEFAULT_MIRROR"

MIRRORS = {
    "ali": "https://mirrors.aliyun.com/pypi/simple/",
    "tsinghua": "https://pypi.tuna.tsinghua.edu.cn/simple/",
    "douban": "https://pypi.doubanio.com/simple/",
    "pypi": "https://pypi.org/simple/",
}

MIRROR_ALIASES = {
    "ali": "ali",
    "aliyun": "ali",
    "tsinghua": "tsinghua",
    "tuna": "tsinghua",
    "douban": "douban",
    "pypi": "pypi",
    "pip": "pypi",
    "official": "pypi",
}

NETWORK_SUBCOMMANDS = {"install", "download", "wheel", "index", "search"}
INSTALL_SUBCOMMAND_ALIASES = {
    "i": "install",
}
UPDATE_SUBCOMMAND_ALIASES = {"update", "up"}
UNINSTALL_SUBCOMMAND_ALIASES = {"un": "uninstall"}
SOURCE_OVERRIDE_FLAGS = {
    "-i",
    "--index-url",
    "--extra-index-url",
    "--find-links",
    "-f",
    "--no-index",
}


def normalize_mirror_name(mirror: str) -> str:
    """Resolve a user-provided mirror name into a canonical key."""
    candidate = mirror.strip().lower()
    try:
        return MIRROR_ALIASES[candidate]
    except KeyError as exc:
        choices = ", ".join(sorted(MIRRORS))
        raise ValueError(f"不支持的镜像 '{mirror}'，可用项: {choices}") from exc


def is_mirror_name(value: str) -> bool:
    """Return whether the value maps to a known mirror."""
    try:
        normalize_mirror_name(value)
    except ValueError:
        return False
    return True


def get_default_mirror(
    env: Optional[Mapping[str, str]] = None,
    project_config: Optional[ProjectConfig] = None,
) -> Tuple[str, Optional[str]]:
    """Read the default mirror from the environment with a safe fallback."""
    mirror, warning, _ = get_default_mirror_settings(env=env, project_config=project_config)
    return mirror, warning


def get_default_mirror_settings(
    env: Optional[Mapping[str, str]] = None,
    project_config: Optional[ProjectConfig] = None,
) -> Tuple[str, Optional[str], str]:
    """Resolve the default mirror and report its source."""
    env_map = os.environ if env is None else env
    if DEFAULT_MIRROR_ENV in env_map:
        configured = env_map[DEFAULT_MIRROR_ENV]
        source = "env"
    elif project_config and project_config.mirror:
        configured = project_config.mirror
        source = "project"
    else:
        configured = DEFAULT_MIRROR
        source = "default"
    try:
        return normalize_mirror_name(configured), None, source
    except ValueError:
        if source == "env":
            warning = (
                f"{DEFAULT_MIRROR_ENV}={configured!r} 无效；"
                f"已回退到 '{DEFAULT_MIRROR}'。"
            )
        elif source == "project":
            config_source = str(project_config.source_path) if project_config and project_config.source_path else "[tool.qpip]"
            warning = f"{config_source} 中的 mirror={configured!r} 无效；已回退到 '{DEFAULT_MIRROR}'。"
        else:
            warning = None
        return DEFAULT_MIRROR, warning, "default"


def canonicalize_subcommand(subcommand: str) -> str:
    """Normalize qpip convenience subcommands into canonical pip subcommands."""
    if subcommand in UPDATE_SUBCOMMAND_ALIASES:
        return "install"
    if subcommand in UNINSTALL_SUBCOMMAND_ALIASES:
        return UNINSTALL_SUBCOMMAND_ALIASES[subcommand]
    return INSTALL_SUBCOMMAND_ALIASES.get(subcommand, subcommand)


def detect_subcommand(args: Sequence[str]) -> Optional[str]:
    """Return the first pip subcommand token in the argument list."""
    for arg in args:
        if arg == "--":
            continue
        if not arg.startswith("-"):
            return canonicalize_subcommand(arg)
    return None


def normalize_pip_args(args: Sequence[str]) -> list[str]:
    """Expand supported qpip subcommand aliases into pip arguments."""
    normalized_args = list(args)

    for index, arg in enumerate(normalized_args):
        if arg == "--":
            continue
        if arg.startswith("-"):
            continue

        if arg in UPDATE_SUBCOMMAND_ALIASES:
            normalized_args[index] = "install"
            if "--upgrade" not in normalized_args[index + 1 :]:
                normalized_args.insert(index + 1, "--upgrade")

            remaining_tokens = [
                token
                for token in normalized_args[index + 1 :]
                if token not in {"--upgrade", "--"}
            ]
            if not remaining_tokens:
                raise ValueError("update 需要至少一个包名，或使用 -r/--requirement 指定依赖文件")
            return normalized_args

        if arg in UNINSTALL_SUBCOMMAND_ALIASES:
            normalized_args[index] = UNINSTALL_SUBCOMMAND_ALIASES[arg]
            return normalized_args

        normalized_args[index] = INSTALL_SUBCOMMAND_ALIASES.get(arg, arg)
        return normalized_args

    return normalized_args


def should_inject_mirror(args: Sequence[str]) -> bool:
    """Inject an index URL only for network-facing pip subcommands."""
    if not args:
        return False

    if any(arg in SOURCE_OVERRIDE_FLAGS for arg in args):
        return False

    subcommand = detect_subcommand(args)
    return subcommand in NETWORK_SUBCOMMANDS


def build_pip_command(
    args: Sequence[str],
    mirror: str = DEFAULT_MIRROR,
    python_executable: Optional[str] = None,
) -> list[str]:
    """Build the pip command line for the supplied arguments."""
    if not args:
        raise ValueError("pip 参数不能为空")

    resolved_mirror = normalize_mirror_name(mirror)
    normalized_args = normalize_pip_args(args)
    command = [python_executable or sys.executable, "-m", "pip", *normalized_args]

    if should_inject_mirror(normalized_args):
        command.extend(["-i", MIRRORS[resolved_mirror]])

    return command


def format_command(command: Sequence[str]) -> str:
    """Render a subprocess command in a shell-friendly format."""
    if os.name == "nt":
        return subprocess.list2cmdline(list(command))
    return shlex.join(command)


def format_mirrors() -> str:
    """Format the configured mirrors for human-readable CLI output."""
    rows = ["可用镜像:"]
    for name, url in MIRRORS.items():
        rows.append(f"  {name:<8} {url}")
    return "\n".join(rows)


def run_pip_with_mirror(
    args: Sequence[str],
    mirror: str = DEFAULT_MIRROR,
    dry_run: bool = False,
    python_executable: Optional[str] = None,
) -> int:
    """Execute pip using the current interpreter and an optional mirror."""
    command = build_pip_command(args, mirror=mirror, python_executable=python_executable)
    print(f"[qpip] {format_command(command)}")

    if dry_run:
        return 0

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