"""
Auto-manages requirements.txt.
Parses, adds, removes, and writes package entries with pinned versions.
"""
from __future__ import annotations
import re
import subprocess
from pathlib import Path
from typing import Dict, List, Optional


def _normalize(name: str) -> str:
    """Normalize package name: lowercase, dashes→underscores"""
    return re.sub(r"[-_.]+", "_", name).lower()


def parse(req_file: Path) -> Dict[str, str]:
    """Returns {normalized_name: full_line} for all non-comment entries."""
    if not req_file.exists():
        return {}
    result: Dict[str, str] = {}
    for line in req_file.read_text("utf-8").splitlines():
        s = line.strip()
        if not s or s.startswith("#"):
            continue
        m = re.match(r"^([A-Za-z0-9_\-\.]+)", s)
        if m:
            result[_normalize(m.group(1))] = s
    return result


def write(req_file: Path, packages: Dict[str, str]) -> None:
    """Write sorted package list back to file."""
    req_file.write_text("\n".join(sorted(packages.values(), key=str.lower)) + "\n", "utf-8")


def get_version(python: str, pkg: str) -> Optional[str]:
    """Get installed version of pkg using pip show."""
    r = subprocess.run([python, "-m", "pip", "show", pkg], capture_output=True, text=True)
    for line in r.stdout.splitlines():
        if line.lower().startswith("version:"):
            return line.split(":", 1)[1].strip()
    return None


def add(req_file: Path, python: str, packages: List[str]) -> None:
    """Add successfully installed packages (pinned versions) to req file."""
    # Ensure file exists
    if not req_file.exists():
        req_file.touch()

    pkgs = parse(req_file)
    changed = False

    for pkg in packages:
        # Strip version specifiers/extras from the requested name
        clean = re.sub(r"(\[.*?\]|[><=!;].*)", "", pkg).strip()
        if not clean:
            continue
        version = get_version(python, clean)
        if version:
            key = _normalize(clean)
            entry = f"{clean}=={version}"
            if pkgs.get(key) != entry:
                pkgs[key] = entry
                changed = True
                print(f"[venvy] Added {entry} → {req_file.name}")

    if changed:
        write(req_file, pkgs)


def remove(req_file: Path, packages: List[str]) -> None:
    """Remove uninstalled packages from req file."""
    if not req_file.exists():
        return
    pkgs = parse(req_file)
    changed = False
    for pkg in packages:
        clean = re.sub(r"(\[.*?\]|[><=!;].*)", "", pkg).strip()
        key = _normalize(clean)
        if key in pkgs:
            del pkgs[key]
            changed = True
            print(f"[venvy] Removed {clean} from {req_file.name}")
    if changed:
        write(req_file, pkgs)
