"""
check_ollama.py — Ensure Ollama is installed and default model is pulled.
=========================================================================
Usage:
    python scripts/check_ollama.py

Steps performed:
    1. Check if Ollama CLI is installed
    2. If not, download and install OllamaSetup.exe silently (Windows)
    3. Start Ollama service if not running
    4. Pull default model (qwen3.5:4b) if not already present
    5. Pull embedding model (nomic-embed-text) if not already present
"""

import os
import sys
import shutil
import subprocess
import time
import urllib.request

DEFAULT_MODEL    = "qwen3.5:4b"
EMBEDDING_MODEL  = "nomic-embed-text"
OLLAMA_API       = "http://localhost:11434"
OLLAMA_WIN_URL   = "https://ollama.com/download/OllamaSetup.exe"
SETUP_EXE        = os.path.join(os.environ.get("TEMP", "C:\\Temp"), "OllamaSetup.exe")


# ── Helpers ───────────────────────────────────────────────────────────────────

def _print(msg: str):
    print(msg, flush=True)


def _run(cmd: list, **kwargs) -> subprocess.CompletedProcess:
    return subprocess.run(cmd, capture_output=True, text=True, **kwargs)


def ollama_installed() -> bool:
    """Return True if the ollama CLI is on PATH."""
    return shutil.which("ollama") is not None


def ollama_running() -> bool:
    """Return True if Ollama HTTP API is reachable."""
    try:
        import urllib.request as req
        req.urlopen(f"{OLLAMA_API}/api/tags", timeout=3)
        return True
    except Exception:
        return False


def list_local_models() -> list[str]:
    """Return list of model names already pulled."""
    try:
        import json, urllib.request as req
        with req.urlopen(f"{OLLAMA_API}/api/tags", timeout=5) as r:
            data = json.loads(r.read())
        return [m["name"] for m in data.get("models", [])]
    except Exception:
        return []


def model_present(model: str) -> bool:
    """Check if a model (or its base name) is already pulled."""
    local = list_local_models()
    # match exact or base name without tag
    base = model.split(":")[0]
    return any(m == model or m.startswith(base + ":") for m in local)


# ── Step 1: Install Ollama ────────────────────────────────────────────────────

def install_ollama_windows():
    """Download and silently install Ollama on Windows."""
    _print(f"[+] Downloading Ollama installer from {OLLAMA_WIN_URL} ...")
    try:
        urllib.request.urlretrieve(OLLAMA_WIN_URL, SETUP_EXE,
            reporthook=lambda b, bs, ts: print(
                f"\r    {min(b*bs, ts)//1024//1024} / {ts//1024//1024} MB   ",
                end="", flush=True
            )
        )
        print()
    except Exception as e:
        _print(f"[ERROR] Download failed: {e}")
        _print("  Please install Ollama manually from https://ollama.com/download")
        sys.exit(1)

    _print("[+] Installing Ollama (silent) ...")
    result = subprocess.run([SETUP_EXE, "/S"], timeout=120)
    if result.returncode != 0:
        _print(f"[ERROR] Installer exited with code {result.returncode}")
        _print("  Try running OllamaSetup.exe manually.")
        sys.exit(1)

    # Clean up
    try:
        os.remove(SETUP_EXE)
    except OSError:
        pass

    _print("[OK] Ollama installed.")


# ── Step 2: Start Ollama service ──────────────────────────────────────────────

def start_ollama():
    """Start Ollama in the background if not already running."""
    if ollama_running():
        return
    _print("[+] Starting Ollama service ...")
    subprocess.Popen(
        ["ollama", "serve"],
        stdout=subprocess.DEVNULL,
        stderr=subprocess.DEVNULL,
        creationflags=subprocess.CREATE_NO_WINDOW if sys.platform == "win32" else 0,
    )
    # Wait up to 15 seconds for it to come up
    for i in range(15):
        time.sleep(1)
        if ollama_running():
            _print("[OK] Ollama service is running.")
            return
        print(f"\r    Waiting for Ollama... {i+1}s", end="", flush=True)
    print()
    _print("[WARNING] Ollama service did not respond in time. Proceeding anyway.")


# ── Step 3: Pull models ───────────────────────────────────────────────────────

def pull_model(model: str):
    """Pull a model using ollama CLI, streaming output."""
    _print(f"[+] Pulling {model} (this may take a while - model is ~2-5 GB) ...")
    result = subprocess.run(["ollama", "pull", model])
    if result.returncode == 0:
        _print(f"[OK] {model} ready.")
    else:
        _print(f"[WARNING] Pull may have failed for {model}. Check Ollama logs.")


# ── Main ──────────────────────────────────────────────────────────────────────

def main():
    _print("=" * 55)
    _print("  BizNode — Ollama Setup")
    _print(f"  Default model : {DEFAULT_MODEL}")
    _print(f"  Embed model   : {EMBEDDING_MODEL}")
    _print("=" * 55)
    _print("")

    # Step 1 — Install if missing
    if ollama_installed():
        ver = _run(["ollama", "--version"]).stdout.strip()
        _print(f"[OK] Ollama found: {ver}")
    else:
        _print("[!] Ollama not found on this machine.")
        if sys.platform != "win32":
            _print("[ERROR] Automatic install only supported on Windows.")
            _print("  Linux/Mac: curl -fsSL https://ollama.com/install.sh | sh")
            sys.exit(1)
        install_ollama_windows()

    _print("")

    # Step 2 — Start service
    start_ollama()
    _print("")

    # Step 3 — Pull default LLM model
    if model_present(DEFAULT_MODEL):
        _print(f"[OK] {DEFAULT_MODEL} already present — skipping pull.")
    else:
        pull_model(DEFAULT_MODEL)

    _print("")

    # Step 4 — Pull embedding model
    if model_present(EMBEDDING_MODEL):
        _print(f"[OK] {EMBEDDING_MODEL} already present — skipping pull.")
    else:
        pull_model(EMBEDDING_MODEL)

    _print("")
    _print("=" * 55)
    _print("  [OK] Ollama setup complete.")
    _print("=" * 55)


if __name__ == "__main__":
    main()
