"""appmac.py — o Primo como app de verdade no macOS (Dock e Cmd+Tab dizem "Primo", com o ícone).

Gera sob demanda ~/.primocode/Primo.app (bundle leve: Info.plist, ícone .icns e um script que
roda o python do venv com `-m primo_voz.hud`). Quem abre o painel executa o binário do bundle
direto (spawn), então o canal stdin/stdout continua o mesmo.

    python -m primo_voz.appmac      # gera (se precisar) e imprime o caminho do executável
"""
from __future__ import annotations

import os
import plistlib
import shutil
import subprocess
import sys
import tempfile
from pathlib import Path

AQUI = Path(__file__).resolve().parent
ICONE = AQUI / "vendor" / "primo-icone.png"
VERSAO = "7"          # sobe quando o bundle muda: força regerar


def pasta_app() -> Path:
    return Path(os.environ.get("PRIMOCODE_APP", Path.home() / "Applications" / "Primo.app"))


def _icns(destino: Path) -> bool:
    with tempfile.TemporaryDirectory() as t:
        iset = Path(t) / "Primo.iconset"
        iset.mkdir()
        for base in (16, 32, 128, 256, 512):
            for k in (1, 2):
                px = base * k
                nome = f"icon_{base}x{base}{'@2x' if k == 2 else ''}.png"
                subprocess.run(["sips", "-z", str(px), str(px), str(ICONE), "--out", str(iset / nome)],
                               stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True)
        r = subprocess.run(["iconutil", "-c", "icns", str(iset), "-o", str(destino)],
                           stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
        return r.returncode == 0


def _no_dock(app: Path) -> None:
    """Fixa o Primo no Dock (só com --dock)."""
    item = ("<dict><key>tile-data</key><dict><key>file-data</key><dict><key>_CFURLString</key><string>%s</string>"
            "<key>_CFURLStringType</key><integer>0</integer></dict></dict></dict>" % str(app))
    subprocess.run(["defaults", "write", "com.apple.dock", "persistent-apps", "-array-add", item], check=False)
    subprocess.run(["killall", "Dock"], check=False)


def pythonpath() -> str:
    """O PYTHONPATH que o binário do bundle precisa (o primo_voz e os pacotes do venv, já que ele não é o python do venv)."""
    import sysconfig
    dirs = [str(AQUI.parent), sysconfig.get_path("purelib"), sysconfig.get_path("platlib")]
    return os.pathsep.join(dict.fromkeys(d for d in dirs if d))


def preparar() -> str | None:
    """Devolve o caminho do executável do Primo.app (criando o bundle se faltar); None se não deu."""
    if sys.platform != "darwin" or not ICONE.exists():
        return None
    try:
        app = pasta_app()
        exe = app / "Contents" / "MacOS" / "Primo"      # o binário de verdade (o processo se chama Primo); o lançador só dá exec nele
        lanc = app / "Contents" / "MacOS" / "Primo-iniciar"
        marca = app / "Contents" / ".versao"
        py = sys.executable
        chave = f"{VERSAO}|{py}|{AQUI.parent}"
        if exe.exists() and marca.exists() and marca.read_text() == chave and (app / "Contents/Resources/Primo.icns").exists():
            return str(exe)
        if app.exists():
            shutil.rmtree(app)
        antigo = Path.home() / ".primocode" / "Primo.app"        # onde ficava até a 10.15.1
        if antigo.exists():
            shutil.rmtree(antigo, ignore_errors=True)
        app.parent.mkdir(parents=True, exist_ok=True)
        (app / "Contents" / "MacOS").mkdir(parents=True)
        (app / "Contents" / "Resources").mkdir()
        if not _icns(app / "Contents" / "Resources" / "Primo.icns"):
            return None
        info = {
            "CFBundleName": "Primo", "CFBundleDisplayName": "Primo", "CFBundleIdentifier": "ai.primocode.primo",
            "CFBundleExecutable": "Primo-iniciar", "CFBundleIconFile": "Primo", "CFBundlePackageType": "APPL",
            "CFBundleShortVersionString": "1.0", "CFBundleVersion": "1", "LSMinimumSystemVersion": "11.0",
            "NSHighResolutionCapable": True, "NSPrincipalClass": "NSApplication",
            "NSSpeechRecognitionUsageDescription": "O Primo entende o que você fala usando o reconhecimento de voz do Mac, no próprio computador.",
            "NSMicrophoneUsageDescription": "O Primo ouve você pelo microfone para atender os seus pedidos por voz.",
            "NSCalendarsFullAccessUsageDescription": "O Primo mostra os compromissos de hoje no painel.",
            "NSCalendarsUsageDescription": "O Primo mostra os compromissos de hoje no painel.",
            "NSAppleEventsUsageDescription": "O Primo controla a música (Spotify/Music) pelo painel.",
        }
        with open(app / "Contents" / "Info.plist", "wb") as f:
            plistlib.dump(info, f)
        # binário de verdade (cópia do Python.app do framework): o macOS passa a chamar o processo de "Primo"
        real = Path(os.path.realpath(py))
        mach = next((c for c in (real.parents[1] / "Resources/Python.app/Contents/MacOS/Python",
                                 real.parent / "../Resources/Python.app/Contents/MacOS/Python") if c.exists()), None)
        if mach:
            shutil.copy2(mach, exe)
            # duplo clique no Finder/Launchpad: sem argumentos = sobe o app completo (voz + painel + cérebro)
            lanc.write_text(f'#!/bin/bash\nexport PYTHONPATH="{pythonpath()}"\nD="$(cd "$(dirname "$0")" && pwd)"\n'
                            f'[ $# -eq 0 ] && set -- -m primo_voz.app\nexec "$D/Primo" "$@"\n')
            lanc.chmod(0o755)
            subprocess.run(["codesign", "--force", "--sign", "-", str(app)], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
        else:
            exe.write_text(f'#!/bin/bash\nexport PYTHONPATH="{pythonpath()}"\n[ $# -eq 0 ] && set -- -m primo_voz.app\nexec "{py}" "$@"\n')
            exe.chmod(0o755)
            shutil.copy2(exe, lanc)
        marca.write_text(chave)
        if os.environ.get("PRIMOCODE_APP_DOCK") == "1":
            _no_dock(app)
        subprocess.run(["/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister",
                        "-f", str(app)], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, timeout=10)
        return str(exe)
    except Exception as e:
        sys.stderr.write(f"appmac: {e}\n")
        return None


if __name__ == "__main__":
    r = preparar()
    if r:
        print(r)
        print(pythonpath())
    sys.exit(0 if r else 1)
