"""
venvy.core.pip_hook
~~~~~~~~~~~~~~~~~~~
Wrapper/hook logic to intercept `pip install` / `pip uninstall`
and automatically sync requirements.txt.

venvy installs a pip wrapper script into the venv's Scripts/bin directory
that calls the real pip and then notifies venvy to update requirements.
"""

from __future__ import annotations

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

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


def _get_package_version(pip_executable: Path, package: str) -> Optional[str]:
    """Query pip for the installed version of a single package."""
    try:
        result = subprocess.run(
            [str(pip_executable), "show", package],
            capture_output=True,
            text=True,
            check=False,
        )
        for line in result.stdout.splitlines():
            if line.startswith("Version:"):
                return line.split(":", 1)[1].strip()
    except Exception:
        pass
    return None


def run_pip_and_sync(
    pip_executable: Path,
    args: List[str],
    requirements_path: Path,
) -> int:
    """
    Run pip with the given args, then sync requirements.txt.
    Returns pip's exit code.
    """
    from venvy.core.requirements import RequirementsManager

    # Run the actual pip command
    result = subprocess.run([str(pip_executable)] + args, check=False)
    exit_code = result.returncode

    if exit_code != 0:
        return exit_code

    mgr = RequirementsManager(requirements_path)

    # Determine action
    if not args:
        return exit_code

    action = args[0].lower()

    if action == "install":
        # Collect package names (skip flags like -r, --upgrade, etc.)
        packages = _extract_package_names(args[1:])
        for pkg in packages:
            version = _get_package_version(pip_executable, pkg)
            if version:
                if not mgr.exists:
                    _prompt_create_requirements(requirements_path)
                if mgr.exists:
                    changed = mgr.add_or_update(pkg, version)
                    if changed:
                        _print_sync(f"Added {pkg}=={version} to {requirements_path.name}")

    elif action in ("uninstall",):
        # Collect package names
        packages = _extract_package_names(args[1:])
        for pkg in packages:
            if mgr.exists:
                removed = mgr.remove(pkg)
                if removed:
                    _print_sync(f"Removed {pkg} from {requirements_path.name}")

    return exit_code


def _extract_package_names(args: List[str]) -> List[str]:
    """
    Extract bare package names from pip args, skipping flags and their values.
    e.g. ["requests", "--upgrade", "flask", "-q"] → ["requests", "flask"]
    """
    skip_next = False
    packages = []
    flag_with_value = {"-r", "--requirement", "-c", "--constraint", "-t", "--target",
                       "--index-url", "-i", "--extra-index-url", "--find-links", "-f",
                       "--python", "--prefix", "--root"}
    for arg in args:
        if skip_next:
            skip_next = False
            continue
        if arg in flag_with_value:
            skip_next = True
            continue
        if arg.startswith("-"):
            continue
        # Could be package==version, package>=version, etc.
        pkg = _strip_version_spec(arg)
        if pkg:
            packages.append(pkg)
    return packages


def _strip_version_spec(arg: str) -> str:
    """'requests>=2.0' → 'requests'"""
    import re
    m = re.match(r"^([A-Za-z0-9_.\-]+)", arg)
    return m.group(1) if m else ""


def _prompt_create_requirements(requirements_path: Path) -> None:
    """Ask the user if they want to create the requirements file."""
    try:
        answer = input(
            f"\n[venvy] '{requirements_path.name}' not found. Create it? [Y/n]: "
        ).strip().lower()
        if answer in ("", "y", "yes"):
            requirements_path.write_text("", encoding="utf-8")
            print(f"[venvy] Created {requirements_path.name}")
    except (EOFError, KeyboardInterrupt):
        pass


def _print_sync(msg: str) -> None:
    print(f"\n\033[32m[venvy]\033[0m {msg}")
