# tools/mugi_brew_bootstrap.py - Minimal Homebrew bootstrap for backend venv (no UI, no tools.system).
# Used by mugi_backend.py to ensure Pillow can build on macOS by installing libjpeg, zlib, etc.
# via Mugi's isolated Homebrew before pip install -r requirements. No user confirmation.
# Uses only stdlib so it runs under system Python before the venv exists.
from __future__ import annotations

import os
import subprocess
import sys

MUGI_DIR = os.path.join(os.path.expanduser("~"), ".mugi")
MUGI_HOMEBREW_PREFIX = os.path.join(MUGI_DIR, "homebrew")


def get_mugi_brew_bin() -> str | None:
    """Path to Mugi's Homebrew brew binary, or None if not installed."""
    if not MUGI_HOMEBREW_PREFIX:
        return None
    path = os.path.join(MUGI_HOMEBREW_PREFIX, "bin", "brew")
    return path if os.path.isfile(path) else None


def get_mugi_homebrew_bin_dir() -> str:
    """Directory containing binaries from Mugi's Homebrew (e.g. for PATH)."""
    return os.path.join(MUGI_HOMEBREW_PREFIX, "bin")


def _run_brew(args: list[str], timeout: int = 300) -> tuple[bool, str]:
    """Run Mugi's brew with args. Returns (success, output_or_error)."""
    brew_bin = get_mugi_brew_bin()
    if not brew_bin:
        return False, "Mugi's Homebrew is not installed."
    env = os.environ.copy()
    env["PATH"] = get_mugi_homebrew_bin_dir() + os.pathsep + env.get("PATH", "")
    env["HOMEBREW_PREFIX"] = MUGI_HOMEBREW_PREFIX
    env["HOMEBREW_REPOSITORY"] = MUGI_HOMEBREW_PREFIX
    # Avoid slow/blocking auto-update during bootstrap (causes app to hang at "Updating Homebrew...")
    env["HOMEBREW_NO_AUTO_UPDATE"] = "1"
    cmd = [brew_bin] + args
    try:
        proc = subprocess.Popen(
            cmd,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True,
            encoding="utf-8",
            errors="replace",
            env=env,
            cwd=MUGI_DIR,
        )
        try:
            out, err = proc.communicate(timeout=timeout)
        except subprocess.TimeoutExpired:
            proc.kill()
            proc.communicate()
            return False, f"brew timed out after {timeout} seconds."
        out = (out or "").strip()
        err = (err or "").strip()
        if proc.returncode != 0:
            msg = err or out or f"Exit code {proc.returncode}"
            combined_lower = (out + "\n" + err).lower()
            if any(s in combined_lower for s in ("command line tools", "xcode-select", "no developer tools", "xcrun")):
                msg += "\n\nHint: Apple Command Line Tools are required for this Homebrew operation. Install them with: xcode-select --install"
            return False, msg
        return True, out or "Done."
    except Exception as e:
        return False, str(e)


def _dulwich_clone_homebrew(python_exe: str) -> tuple[bool, str]:
    """Clone Homebrew via dulwich (pure-Python Git). No system git required. Returns (success, message)."""
    target = os.path.join(MUGI_DIR, "homebrew")
    if os.path.exists(target):
        return False, f"Path exists: {target}"
    print("[mugi] Cloning Homebrew (this can take several minutes)...", file=sys.stderr, flush=True)
    os.makedirs(MUGI_DIR, exist_ok=True)
    code = """
import sys
path = sys.argv[1]
try:
    from dulwich import porcelain
    porcelain.clone('https://github.com/Homebrew/brew.git', path)
except Exception as e:
    print(str(e), file=sys.stderr)
    sys.exit(1)
"""
    try:
        r = subprocess.run(
            [python_exe, "-c", code, target],
            capture_output=True,
            text=True,
            timeout=300,
        )
        if r.returncode != 0:
            return False, (r.stderr or r.stdout or "dulwich clone failed").strip()
        return True, "OK"
    except subprocess.TimeoutExpired:
        return False, "Clone timed out (network may be slow)."
    except Exception as e:
        return False, str(e)


def ensure_mugi_homebrew_core(python_for_dulwich: str | None = None) -> tuple[bool, str]:
    """Ensure Mugi's Homebrew exists at ~/.mugi/homebrew (clone via dulwich or update + tap). No UI. Returns (success, message)."""
    brew_bin = get_mugi_brew_bin()
    if brew_bin:
        # Skip brew update when brew already exists - it can hang for minutes and blocks app launch.
        # HOMEBREW_NO_AUTO_UPDATE in _run_brew prevents install from triggering update.
        _run_brew(["tap", "homebrew/core"], timeout=60)
        return True, "OK"
    os.makedirs(MUGI_DIR, exist_ok=True)
    if os.path.exists(MUGI_HOMEBREW_PREFIX):
        return False, f"Path exists but is not a valid Homebrew install: {MUGI_HOMEBREW_PREFIX}"
    if python_for_dulwich and os.path.isfile(python_for_dulwich):
        ok, msg = _dulwich_clone_homebrew(python_for_dulwich)
        if not ok:
            return False, f"dulwich clone failed: {msg}"
    else:
        # Fallback: git clone (for dev setups; app uses dulwich via venv)
        print("[mugi] Cloning Homebrew via git (this can take several minutes)...", file=sys.stderr, flush=True)
        try:
            subprocess.run(
                ["git", "clone", "https://github.com/Homebrew/brew.git", "homebrew"],
                cwd=MUGI_DIR,
                check=True,
                capture_output=True,
                text=True,
                timeout=300,
            )
        except subprocess.CalledProcessError as e:
            err = (e.stderr or e.stdout or str(e)).strip()
            return False, f"git clone failed: {err}"
        except FileNotFoundError:
            return False, "git not found. Install Xcode Command Line Tools or run the setup wizard."
        except subprocess.TimeoutExpired:
            return False, "git clone timed out (network may be slow)."
    brew_bin = get_mugi_brew_bin()
    if not brew_bin:
        return False, "Clone completed but bin/brew not found."
    # Fresh clone: run update once to bootstrap core tap (unavoidable on first install)
    print("[mugi] Setting up Homebrew (first time only)...", file=sys.stderr, flush=True)
    ok, msg = _run_brew(["update", "--quiet"], timeout=180)
    if not ok:
        return False, f"brew update failed: {msg}"
    _run_brew(["tap", "homebrew/core"], timeout=60)
    return True, msg or "OK"


# Formulae required for Pillow to build on macOS (headers/libs).
PILLOW_BREW_FORMULAE = ["libjpeg", "zlib", "libtiff", "freetype", "little-cms2", "webp"]


def get_mugi_python() -> str | None:
    """Ensure Mugi Homebrew exists, install python@3.12 if needed, return path to that Python (has venv). For use when system Python has no venv (e.g. macOS /usr/bin/python3 stub)."""
    ok, _ = ensure_mugi_homebrew_core()
    if not ok:
        return None
    bin_dir = get_mugi_homebrew_bin_dir()
    # Prefer already-installed Python so we don't re-run brew install every time
    for name in ("python3.12", "python3.11", "python3.10", "python3"):
        candidate = os.path.join(bin_dir, name)
        if os.path.isfile(candidate):
            return candidate
    # Install Python via Homebrew; prefer python@3.12 for consistency
    for formula in ("python@3.12", "python@3.11", "python"):
        ok, _ = _run_brew(["install", formula], timeout=300)
        if not ok:
            continue
        if formula.startswith("python@"):
            ver = formula.replace("python@", "")
            candidate = os.path.join(bin_dir, f"python{ver}")
        else:
            candidate = os.path.join(bin_dir, "python3")
        if os.path.isfile(candidate):
            return candidate
    return None


def install_pillow_build_deps() -> tuple[bool, str]:
    """Ensure Mugi Homebrew exists and install libjpeg, zlib, etc. so Pillow can build. No user confirmation. Returns (success, message)."""
    print("[mugi] Installing Pillow build dependencies...", file=sys.stderr, flush=True)
    ok, msg = ensure_mugi_homebrew_core()
    if not ok:
        return False, msg
    # Install all in one go so brew can resolve deps
    ok, msg = _run_brew(["install"] + PILLOW_BREW_FORMULAE, timeout=600)
    if not ok:
        return False, msg
    return True, msg or "Pillow build dependencies installed."


def env_for_pillow_build() -> dict[str, str]:
    """Return env dict with CFLAGS, LDFLAGS, CPATH, LIBRARY_PATH, PKG_CONFIG_PATH set so Pillow finds Mugi Homebrew libs."""
    if not os.path.isdir(MUGI_HOMEBREW_PREFIX):
        return {}
    include = os.path.join(MUGI_HOMEBREW_PREFIX, "include")
    lib = os.path.join(MUGI_HOMEBREW_PREFIX, "lib")
    pkgconfig = os.path.join(MUGI_HOMEBREW_PREFIX, "lib", "pkgconfig")
    env = os.environ.copy()
    sep = os.pathsep
    # Explicit flags so Pillow's build (and compiler) see Homebrew; platform-guessing is disabled on macOS.
    env["CFLAGS"] = f"-I{include}" + (f" {env['CFLAGS']}" if env.get("CFLAGS") else "")
    env["LDFLAGS"] = f"-L{lib}" + (f" {env['LDFLAGS']}" if env.get("LDFLAGS") else "")
    env["CPATH"] = include + (sep + env["CPATH"]) if env.get("CPATH") else include
    env["LIBRARY_PATH"] = lib + (sep + env["LIBRARY_PATH"]) if env.get("LIBRARY_PATH") else lib
    env["PKG_CONFIG_PATH"] = pkgconfig + (sep + env["PKG_CONFIG_PATH"]) if env.get("PKG_CONFIG_PATH") else pkgconfig
    env["PATH"] = get_mugi_homebrew_bin_dir() + sep + env.get("PATH", "")
    return env
