/**
 * Emacs daemon integration for OmegaLLM
 *
 * API:
 *   evalExpr(expr)   - evaluate elisp, auto-starts daemon
 *   loadProject()    - load all project files (headless, ~60s)
 *   checkHealth()    - get status
 *   restart()        - kill and restart daemon
 */

import { execSync, spawn } from "child_process";
import { join } from "path";
import type { Val } from "../../../src/core/eval/values";

const EMACS_PATH = process.env.EMACS_PATH || "C:/Program Files/Emacs/emacs-30.2/bin";
const EMACSCLIENT = join(EMACS_PATH, "emacsclient.exe");
const RUNEMACS = join(EMACS_PATH, "runemacs.exe");
const PROJECT_ROOT = process.env.OMEGA_PROJECT_ROOT || process.cwd();
const CLAUDE_HELPERS = join(PROJECT_ROOT, "elisp-prototype/claude-helpers.el").replace(/\\/g, "/");

const TIMEOUT = 120000;
const HEALTH_TIMEOUT = 5000;
const STARTUP_WAIT = 5000;

let ready = false;

function exec(expr: string, timeout = TIMEOUT): string {
  return execSync(
    `"${EMACSCLIENT}" --eval "${expr.replace(/"/g, '\\"')}"`,
    { encoding: "utf-8", timeout, windowsHide: true, stdio: ["pipe", "pipe", "pipe"] }
  ).trim();
}

function killAll(): void {
  try { execSync('taskkill //F //IM emacs.exe 2>nul', { stdio: "pipe", windowsHide: true }); } catch {}
  try { execSync('taskkill //F //IM emacsclient.exe 2>nul', { stdio: "pipe", windowsHide: true }); } catch {}
}

async function startDaemon(): Promise<void> {
  spawn(`"${RUNEMACS}"`, ["--daemon"], { shell: true, detached: true, stdio: "ignore", windowsHide: true }).unref();
  await new Promise(r => setTimeout(r, STARTUP_WAIT));
}

async function ensure(): Promise<void> {
  if (ready) {
    try { if (exec("(+ 1 1)", HEALTH_TIMEOUT) === "2") return; } catch {}
    ready = false;
  }

  try {
    if (exec("(+ 1 1)", HEALTH_TIMEOUT) === "2") {
      try { exec("(symbol-function 'claude-ping)", HEALTH_TIMEOUT); }
      catch { exec(`(load "${CLAUDE_HELPERS}")`); }
      ready = true;
      return;
    }
  } catch {}

  killAll();
  await startDaemon();
  if (exec("(+ 1 1)", HEALTH_TIMEOUT) !== "2") throw new Error("Failed to start Emacs");
  exec(`(load "${CLAUDE_HELPERS}")`);
  ready = true;
}

// === Exports ===

export interface EmacsHealth {
  alive: boolean;
  bufferCount?: { ts: number; lisp: number; other: number };
  error?: string;
}

export async function checkHealth(): Promise<EmacsHealth> {
  try {
    if (exec("(+ 1 1)", HEALTH_TIMEOUT) !== "2") return { alive: false, error: "Bad response" };
    try {
      const raw = exec("(claude-buffer-count)", HEALTH_TIMEOUT);
      const m = raw.match(/TS:\s*(\d+),\s*Lisp:\s*(\d+),\s*Other:\s*(\d+)/i);
      if (m) return { alive: true, bufferCount: { ts: +m[1], lisp: +m[2], other: +m[3] } };
    } catch {}
    return { alive: true };
  } catch (e: any) {
    return { alive: false, error: e.message };
  }
}

export async function evalExpr(expr: string, timeout = TIMEOUT): Promise<string> {
  await ensure();
  return exec(expr, timeout);
}

export async function loadProject(): Promise<{ ts: number; lisp: number; other: number }> {
  await ensure();
  const result = exec("(claude-init)", 120000);
  const m = result.match(/Loaded\s+(\d+)\s+TS,\s+(\d+)\s+Lisp,\s+(\d+)\s+JS/i);
  return m ? { ts: +m[1], lisp: +m[2], other: +m[3] } : { ts: 0, lisp: 0, other: 0 };
}

export async function ensureReady(): Promise<EmacsHealth> {
  await ensure();
  return checkHealth();
}

export async function restart(): Promise<EmacsHealth> {
  ready = false;
  killAll();
  await startDaemon();
  exec(`(load "${CLAUDE_HELPERS}")`);
  ready = true;
  return checkHealth();
}

// Val converters for OmegaLLM effects
export function healthToVal(h: EmacsHealth): Val {
  const msg = h.alive ? (h.bufferCount ? `Emacs ready: ${h.bufferCount.ts} TS, ${h.bufferCount.lisp} Lisp` : "running") : (h.error || "down");
  return { tag: "Vector", items: [{ tag: "Bool", b: h.alive } as Val, { tag: "Str", s: msg } as Val] };
}

export function bufferCountToVal(c: { ts: number; lisp: number; other: number }): Val {
  return {
    tag: "Map",
    entries: [
      [{ tag: "Str", s: "ts" } as Val, { tag: "Num", n: c.ts } as Val],
      [{ tag: "Str", s: "lisp" } as Val, { tag: "Num", n: c.lisp } as Val],
      [{ tag: "Str", s: "other" } as Val, { tag: "Num", n: c.other } as Val],
    ],
  };
}
