# mustel/main.py
"""
mustel - Simple Python environment inspector
A tool to see what's installed across all your Python versions.

Named after the Mustelidae family (otters, weasels) - small but effective hunters!
"""
import sys
import os
import subprocess
import json
import platform
import importlib.util

# Fix Windows console encoding for emojis
if platform.system() == "Windows":
    try:
        sys.stdout.reconfigure(encoding='utf-8')
    except:
        pass


# ============================================================
# HELPER FUNCTIONS
# ============================================================

def run_cmd(cmd):
    """Run a command and return output, or None if it fails."""
    try:
        return subprocess.check_output(cmd, stderr=subprocess.DEVNULL, text=True)
    except:
        return None


def get_packages(python_path):
    """Get dict of {package_name: version} for a Python installation."""
    out = run_cmd([python_path, "-m", "pip", "list", "--format=json"])
    if not out:
        return {}
    try:
        data = json.loads(out)
        return {pkg["name"].lower(): pkg["version"] for pkg in data}
    except:
        return {}


def get_outdated(python_path):
    """Get dict of {package_name: (current, latest)} for outdated packages."""
    out = run_cmd([python_path, "-m", "pip", "list", "--outdated", "--format=json"])
    if not out:
        return {}
    try:
        data = json.loads(out)
        return {pkg["name"].lower(): (pkg["version"], pkg["latest_version"]) for pkg in data}
    except:
        return {}


def find_all_pythons():
    """Find all Python installations on the system."""
    found = set()
    current = sys.executable
    
    if platform.system() == "Windows":
        # Check common Windows install location
        local_programs = os.path.expandvars(r"%LOCALAPPDATA%\Programs\Python")
        if os.path.exists(local_programs):
            for folder in os.listdir(local_programs):
                exe = os.path.join(local_programs, folder, "python.exe")
                if os.path.exists(exe):
                    found.add(exe)
        
        # Check where python finds
        out = run_cmd(["where", "python"])
        if out:
            for line in out.splitlines():
                line = line.strip()
                if line.lower().endswith(".exe"):
                    found.add(line)
    else:
        # Unix: check python3.x versions
        for minor in range(7, 15):
            out = run_cmd(["which", f"python3.{minor}"])
            if out:
                found.add(out.strip())
    
    # Remove current python and normalize paths
    result = []
    current_real = os.path.normcase(os.path.realpath(current))
    seen = set()
    
    for path in found:
        try:
            path_real = os.path.normcase(os.path.realpath(path))
            if path_real == current_real:
                continue
            if path_real not in seen:
                seen.add(path_real)
                result.append(path)
        except:
            pass
    
    return result


def is_importable(name):
    """Check if a module can be imported."""
    return importlib.util.find_spec(name) is not None


# ============================================================
# COMMANDS
# ============================================================

def cmd_list():
    """Show packages in current Python."""
    exe = sys.executable
    version = sys.version.split()[0]
    
    print(f"\n🦦 mustel — Python {version}\n")
    print(f"Path: {exe}\n")
    
    packages = get_packages(exe)
    if packages:
        print(f"📦 {len(packages)} packages installed:\n")
        for name in sorted(packages):
            print(f"  {name} == {packages[name]}")
    else:
        print("  (no packages found)")


def cmd_all():
    """Show all Python installations."""
    print("\n🦦 mustel — all pythons\n")
    
    current = sys.executable
    others = find_all_pythons()
    all_pythons = [current] + others
    
    print(f"Found {len(all_pythons)} Python(s):\n")
    print("-" * 60)
    
    for exe in all_pythons:
        # Get version
        out = run_cmd([exe, "--version"])
        version = out.strip() if out else "unknown"
        
        # Get package count
        packages = get_packages(exe)
        count = len(packages)
        
        # Mark current
        marker = "★" if exe == current else " "
        label = "(current)" if exe == current else ""
        
        print(f"{marker} {version} {label}")
        print(f"  Path: {exe}")
        print(f"  Packages: {count}\n")
    
    print("-" * 60)


def cmd_diff():
    """Show packages in other Pythons that aren't in current."""
    print("\n🦦 mustel — diff\n")
    
    current = sys.executable
    current_packages = get_packages(current)
    others = find_all_pythons()
    
    if not others:
        print("No other Python installations found.")
        return
    
    print(f"Current Python: {current}")
    print(f"Packages: {len(current_packages)}\n")
    
    for other in others:
        other_packages = get_packages(other)
        if not other_packages:
            continue
        
        # Find packages in other that aren't in current
        missing = {k: v for k, v in other_packages.items() if k not in current_packages}
        
        out = run_cmd([other, "--version"])
        version = out.strip() if out else other
        
        print(f"\n{version}")
        print(f"  Path: {other}")
        
        if missing:
            print(f"  📦 {len(missing)} unique packages:")
            for name in sorted(missing):
                print(f"    {name} == {missing[name]}")
        else:
            print("  ✅ No unique packages")


def cmd_check(package_name):
    """Check if a package exists and where."""
    print(f"\n🦦 mustel — check '{package_name}'\n")
    
    current = sys.executable
    
    # Check if importable in current
    if is_importable(package_name):
        print(f"✅ '{package_name}' is available in current Python")
        return
    
    print(f"❌ '{package_name}' is NOT in current Python ({current})\n")
    
    # Search other Pythons
    others = find_all_pythons()
    found_in = []
    
    for other in others:
        packages = get_packages(other)
        if package_name.lower() in packages:
            found_in.append((other, packages[package_name.lower()]))
    
    if found_in:
        print("Found in:")
        for path, version in found_in:
            print(f"  {path} -> {version}")
        print(f"\nTo install: pip install {package_name}")
    else:
        print("Not found in any Python installation.")


def cmd_updates():
    """Show outdated packages."""
    print("\n🦦 mustel — updates\n")
    
    current = sys.executable
    print(f"Python: {current}\n")
    print("⏳ Checking... (this takes 1-2 minutes)\n")
    
    outdated = get_outdated(current)
    
    if not outdated:
        print("✅ All packages are up to date!")
        return
    
    print(f"📦 {len(outdated)} updates available:\n")
    for name in sorted(outdated):
        old, new = outdated[name]
        print(f"  {name}: {old} → {new}")


def cmd_install(package_name):
    """Install a package using pip."""
    print(f"\n🦦 mustel — install '{package_name}'\n")
    
    current = sys.executable
    print(f"Installing to: {current}\n")
    
    # Check if already installed
    packages = get_packages(current)
    if package_name.lower() in packages:
        version = packages[package_name.lower()]
        print(f"⚠️  '{package_name}' is already installed (version {version})")
        print(f"\nTo upgrade, run: pip install --upgrade {package_name}")
        return
    
    print(f"⏳ Installing {package_name}...\n")
    
    # Run pip install (using subprocess.run so output shows in real-time)
    result = subprocess.run(
        [current, "-m", "pip", "install", package_name],
        text=True
    )
    
    # Check if it worked
    if result.returncode == 0:
        print(f"\n✅ Successfully installed {package_name}!")
    else:
        print(f"\n❌ Failed to install {package_name}")
        print("   Check the error message above.")

def cmd_help():
    """Show help."""
    print("""
mustel — Python environment inspector

Commands:
  mustel             Show packages in current Python
  mustel all         Show all Python installations  
  mustel diff        Compare packages across Pythons
  mustel check X     Check if package X exists
  mustel install X   Install package X
  mustel updates     Show outdated packages
  mustel help        Show this help

Tip: Run with different Python:
  C:\\path\\python.exe -m mustel
""")


# ============================================================
# MAIN
# ============================================================

def main():
    args = sys.argv[1:]
    
    if not args:
        cmd_list()
    elif args[0] in ("help", "-h", "--help"):
        cmd_help()
    elif args[0] == "all":
        cmd_all()
    elif args[0] == "diff":
        cmd_diff()
    elif args[0] == "check" and len(args) > 1:
        cmd_check(args[1])
    elif args[0] == "updates":
        cmd_updates()
    elif args[0] == "install" and len(args) > 1:
        cmd_install(args[1])
    else:
        print(f"Unknown command: {args[0]}")
        cmd_help()


if __name__ == "__main__":
    main()
