#!/usr/bin/env python3
"""
atlas_tray.py - Atlas always-on system tray icon.

Right-click menu for quick access to Atlas Command / HUD / Chat / Portal +
util actions. Icon color reflects cluster health.

Run with pythonw.exe so no console flash:
    pythonw atlas_tray.py
"""
import os
import sys
import time
import json
import webbrowser
import threading
import urllib.request
import subprocess
from pathlib import Path

try:
    import pystray
    from pystray import MenuItem as Item, Menu
    from PIL import Image, ImageDraw, ImageFont
except ImportError as e:
    print(f"Missing dep: {e}\nInstall: pip install pystray pillow")
    sys.exit(1)

HOME = Path.home()
ATLAS = HOME / ".config" / "atlas"
LOG = ATLAS / "atlas_tray.log"

CFG = {
    "ollama_url":  "http://atlas-01/ollama/api/tags",
    "priorities":  "http://atlas-01/priorities.json",
    "poll_seconds": 30,
}

STATE = {
    "status": "unknown",
    "overdue_eur": 0,
    "disk_free_gb": 0,
    "containers_down": 0,
    "last_update": None,
    "models": 0,
}


def log(msg):
    try:
        LOG.parent.mkdir(parents=True, exist_ok=True)
        with open(LOG, "a", encoding="utf-8") as f:
            f.write(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] {msg}\n")
    except Exception:
        pass


def fetch_json(url, timeout=4):
    try:
        with urllib.request.urlopen(url, timeout=timeout) as r:
            return json.loads(r.read().decode("utf-8", errors="replace"))
    except Exception:
        return None


def make_icon(color_hex="#3B82F6", text="A"):
    """Generate 64x64 icon: filled circle + white letter."""
    size = 64
    img = Image.new("RGBA", (size, size), (0, 0, 0, 0))
    d = ImageDraw.Draw(img)
    d.ellipse((4, 4, size - 4, size - 4), fill=color_hex)
    try:
        font = ImageFont.truetype("arial.ttf", 38)
    except Exception:
        font = ImageFont.load_default()
    bbox = d.textbbox((0, 0), text, font=font)
    tw, th = bbox[2] - bbox[0], bbox[3] - bbox[1]
    d.text(((size - tw) / 2 - bbox[0], (size - th) / 2 - bbox[1]), text, fill="white", font=font)
    return img


def color_for(status):
    return {
        "ok":      "#10B981",
        "warn":    "#F59E0B",
        "err":     "#EF4444",
        "unknown": "#6B7280",
    }.get(status, "#3B82F6")


def poll_loop(icon):
    while True:
        try:
            pri = fetch_json(CFG["priorities"])
            tags = fetch_json(CFG["ollama_url"])

            if pri:
                STATE["overdue_eur"] = pri.get("odoo", {}).get("overdue_eur", 0)
                STATE["disk_free_gb"] = pri.get("disk_free_gb", 0)
                STATE["containers_down"] = pri.get("containers_down", 0)
                STATE["last_update"] = time.strftime("%H:%M:%S")

            STATE["models"] = len(tags.get("models", [])) if tags else 0

            if tags is None and pri is None:
                STATE["status"] = "err"
            elif STATE["containers_down"] > 0 or STATE["disk_free_gb"] < 5:
                STATE["status"] = "err"
            elif STATE["overdue_eur"] > 0 or STATE["disk_free_gb"] < 15:
                STATE["status"] = "warn"
            else:
                STATE["status"] = "ok"

            try:
                icon.icon = make_icon(color_for(STATE["status"]))
                icon.title = (
                    f"Atlas — {STATE['status'].upper()}\n"
                    f"Overdue: €{STATE['overdue_eur']}\n"
                    f"Disk: {STATE['disk_free_gb']}GB free\n"
                    f"Containers down: {STATE['containers_down']}\n"
                    f"Models on atlas-01: {STATE['models']}\n"
                    f"Updated: {STATE['last_update']}"
                )
            except Exception as e:
                log(f"icon update fail: {e}")
        except Exception as e:
            log(f"poll error: {e}")

        time.sleep(CFG["poll_seconds"])


def open_url(url):
    def _open(icon, item):
        webbrowser.open(url)
        log(f"opened url {url}")
    return _open


def open_app(url, profile):
    """Open in Edge --app mode for native-window feel."""
    edge = r"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe"
    profile_dir = HOME / "AppData" / "Local" / "Atlas" / profile

    def _open(icon, item):
        if not Path(edge).exists():
            webbrowser.open(url)
            return
        try:
            subprocess.Popen([
                edge,
                f"--app={url}",
                f"--user-data-dir={profile_dir}",
                "--no-first-run", "--no-default-browser-check",
            ])
            log(f"app opened {url}")
        except Exception as e:
            log(f"app open fail: {e}")
    return _open


def restart_ollama(icon, item):
    log("restart ollama requested")
    try:
        subprocess.run(["taskkill", "/F", "/IM", "ollama.exe"], capture_output=True, timeout=5)
        subprocess.run(["taskkill", "/F", "/IM", "ollama app.exe"], capture_output=True, timeout=5)
        time.sleep(1)
        ollama = HOME / "AppData" / "Local" / "Programs" / "Ollama" / "ollama.exe"
        if ollama.exists():
            env = os.environ.copy()
            env["OLLAMA_HOST"] = "0.0.0.0:11434"
            env["OLLAMA_KEEP_ALIVE"] = "30m"
            subprocess.Popen([str(ollama), "serve"], env=env, creationflags=0x08000000)
            log("ollama restarted")
    except Exception as e:
        log(f"restart ollama fail: {e}")


def open_log(icon, item):
    try:
        subprocess.Popen(["notepad.exe", str(LOG)])
    except Exception:
        webbrowser.open(f"file:///{LOG}")


def quit_tray(icon, item):
    log("tray quit")
    icon.stop()


def status_label(item):
    return f"Status: {STATE['status'].upper()}  ·  €{STATE['overdue_eur']} open  ·  {STATE['disk_free_gb']}GB"


def updated_label(item):
    return f"Updated: {STATE['last_update'] or 'never'}"


def main():
    log("=== atlas_tray starting ===")

    menu = Menu(
        Item(status_label, lambda i, x: None, enabled=False),
        Item(updated_label, lambda i, x: None, enabled=False),
        Menu.SEPARATOR,
        Item("Atlas Command · Ctrl+Shift+P", open_app("http://atlas-01/atlas-command.html", "Cockpit-Profile"), default=True),
        Item("Atlas HUD · Ctrl+Shift+H", open_app("http://atlas-01/atlas-hud.html", "HUD-Profile")),
        Item("Atlas Chat · Ctrl+Shift+C", open_app("http://atlas-01/atlas-chat.html", "Chat-Profile")),
        Item("Atlas Portal (legacy)", open_url("http://atlas-01/portal.html")),
        Menu.SEPARATOR,
        Item("Odoo", open_url("http://<YOUR_VPS_IP>:8069")),
        Item("Nextcloud", open_url("http://cloud.your-domain.tld")),
        Item("MeshCentral", open_url("http://mesh.your-domain.tld")),
        Item("Open WebUI", open_url("http://<YOUR_VPS_IP>:8082")),
        Menu.SEPARATOR,
        Item("Restart Ollama (PC)", restart_ollama),
        Item("Open tray log", open_log),
        Menu.SEPARATOR,
        Item("Quit", quit_tray),
    )

    img = make_icon(color_for("unknown"))
    icon = pystray.Icon("atlas", img, "Atlas (loading...)", menu)

    threading.Thread(target=poll_loop, args=(icon,), daemon=True).start()
    icon.run()


if __name__ == "__main__":
    main()
