/**
 * Daemon management: start, stop, status commands.
 *
 * Uses launchd on macOS to run `avis serve` as a persistent background service.
 */
import { execSync } from "node:child_process";
import * as fs from "node:fs";
import * as path from "node:path";
import chalk from "chalk";
import { isDaemonRunning } from "../modes/serve/serve-mode.js";
const PLIST_LABEL = "email.kaio.avis";
const PLIST_DIR = path.join(process.env.HOME ?? "/tmp", "Library", "LaunchAgents");
const PLIST_PATH = path.join(PLIST_DIR, `${PLIST_LABEL}.plist`);
const LOG_DIR = path.join(process.env.HOME ?? "/tmp", ".avis", "logs");
const AVIS_BIN = path.join(process.env.HOME ?? "/tmp", ".avis", "bin", "Avis");
const DEFAULT_PORT = 3141;
function getNodePath() {
    try {
        return execSync("which node", { encoding: "utf-8" }).trim();
    }
    catch {
        return "/usr/local/bin/node";
    }
}
function getCliPath() {
    // Resolve from this file's location
    return path.resolve(import.meta.dirname, "..", "..", "dist", "cli.js");
}
function ensureAvisBin() {
    if (fs.existsSync(AVIS_BIN))
        return;
    // Compile the native wrapper so macOS Login Items shows "Avis"
    const srcDir = path.dirname(AVIS_BIN);
    fs.mkdirSync(srcDir, { recursive: true });
    const src = path.join(srcDir, "_avis.c");
    fs.writeFileSync(src, `#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
    if (argc < 3) { fprintf(stderr, "Usage: Avis <node> <cli> [args...]\\n"); return 1; }
    char *node = argv[1];
    char **a = malloc(sizeof(char *) * argc);
    a[0] = "Avis";
    for (int i = 2; i < argc; i++) a[i-1] = argv[i];
    a[argc-1] = NULL;
    execv(node, a);
    perror("execv"); return 1;
}\n`);
    try {
        execSync(`clang -O2 -o "${AVIS_BIN}" "${src}"`, { stdio: "pipe" });
    }
    finally {
        try {
            fs.unlinkSync(src);
        }
        catch { }
    }
}
function generatePlist(port) {
    const nodePath = getNodePath();
    const cliPath = getCliPath();
    // Use native wrapper binary so macOS shows "Avis" in Login Items
    // instead of "Node.js Foundation" / "bash"
    const useBin = fs.existsSync(AVIS_BIN);
    return `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>Label</key>
	<string>${PLIST_LABEL}</string>
	<key>ProgramArguments</key>
	<array>${useBin
        ? `
		<string>${AVIS_BIN}</string>
		<string>${nodePath}</string>`
        : `
		<string>${nodePath}</string>`}
		<string>${cliPath}</string>
		<string>serve</string>
		<string>--port</string>
		<string>${port}</string>
	</array>
	<key>RunAtLoad</key>
	<true/>
	<key>KeepAlive</key>
	<dict>
		<key>SuccessfulExit</key>
		<false/>
	</dict>
	<key>StandardOutPath</key>
	<string>${LOG_DIR}/daemon.log</string>
	<key>StandardErrorPath</key>
	<string>${LOG_DIR}/daemon.log</string>
	<key>EnvironmentVariables</key>
	<dict>
		<key>PATH</key>
		<string>${path.dirname(nodePath)}:/usr/local/bin:/usr/bin:/bin</string>
		<key>HOME</key>
		<string>${process.env.HOME}</string>
		<key>AVIS_MEMORY_MODEL</key>
		<string>gemma4</string>
	</dict>
	<key>ProcessType</key>
	<string>Background</string>
</dict>
</plist>`;
}
function copyEnvKeysToDict() {
    // Copy API key env vars into the plist so the daemon has access
    const envKeys = [
        "ANTHROPIC_API_KEY",
        "ANTHROPIC_OAUTH_TOKEN",
        "OPENAI_API_KEY",
        "GEMINI_API_KEY",
        "GROQ_API_KEY",
        "XAI_API_KEY",
        "OPENROUTER_API_KEY",
        "AI_GATEWAY_API_KEY",
        "MISTRAL_API_KEY",
        "AWS_PROFILE",
        "AWS_ACCESS_KEY_ID",
        "AWS_SECRET_ACCESS_KEY",
        "AWS_REGION",
        "HUME_API_KEY",
    ];
    let dict = "";
    for (const key of envKeys) {
        const val = process.env[key];
        if (val) {
            dict += `\t\t<key>${key}</key>\n\t\t<string>${val}</string>\n`;
        }
    }
    return dict;
}
export function parseDaemonCommand(args) {
    const cmd = args[0];
    if (cmd !== "start" && cmd !== "stop" && cmd !== "status" && cmd !== "serve") {
        return undefined;
    }
    let port = DEFAULT_PORT;
    for (let i = 1; i < args.length; i++) {
        if (args[i] === "--port" && i + 1 < args.length) {
            port = Number.parseInt(args[++i], 10);
        }
    }
    return { command: cmd, port };
}
export async function handleDaemonCommand(cmd) {
    switch (cmd.command) {
        case "status":
            return handleStatus();
        case "start":
            return handleStart(cmd.port ?? DEFAULT_PORT);
        case "stop":
            return handleStop();
        case "serve":
            return false; // Handled by main.ts directly
    }
}
function handleStatus() {
    const info = isDaemonRunning();
    if (info) {
        console.log(chalk.green(`Avis daemon is running (pid ${info.pid}, port ${info.port})`));
        console.log(chalk.dim(`  ws://127.0.0.1:${info.port}`));
    }
    else {
        console.log(chalk.dim("Avis daemon is not running."));
        console.log(chalk.dim(`  Start with: avis start`));
    }
    return true;
}
function cleanupLegacyDaemon() {
    // Remove legacy Python memory daemon plist if it exists
    const oldPlist = path.join(PLIST_DIR, "com.avis.memory-daemon.plist");
    if (fs.existsSync(oldPlist)) {
        try {
            execSync(`launchctl unload "${oldPlist}" 2>/dev/null`);
            fs.unlinkSync(oldPlist);
        }
        catch {
            // ignore
        }
    }
    // Remove legacy daemon directory
    const legacyDaemonDir = path.join(process.env.HOME ?? "/tmp", ".avis", "agent", "daemon");
    if (fs.existsSync(legacyDaemonDir)) {
        fs.rmSync(legacyDaemonDir, { recursive: true, force: true });
    }
    // Remove legacy pid file location
    const legacyPid = path.join(process.env.HOME ?? "/tmp", ".avis", "agent", "daemon.pid");
    if (fs.existsSync(legacyPid)) {
        fs.unlinkSync(legacyPid);
    }
}
function handleStart(port) {
    const existing = isDaemonRunning();
    if (existing) {
        console.log(chalk.yellow(`Daemon already running (pid ${existing.pid}, port ${existing.port})`));
        return true;
    }
    // Clean up legacy daemon artifacts
    cleanupLegacyDaemon();
    // Compile native wrapper binary (shows "Avis" in macOS Login Items)
    try {
        ensureAvisBin();
    }
    catch {
        // Non-fatal: falls back to raw node in plist
    }
    // Ensure log dir exists
    fs.mkdirSync(LOG_DIR, { recursive: true });
    // Generate plist with env vars baked in
    let plist = generatePlist(port);
    // Inject API keys into EnvironmentVariables dict
    const envEntries = copyEnvKeysToDict();
    if (envEntries) {
        plist = plist.replace("</dict>\n\t<key>ProcessType</key>", `${envEntries}\t</dict>\n\t<key>ProcessType</key>`);
    }
    // Write plist
    fs.mkdirSync(PLIST_DIR, { recursive: true });
    fs.writeFileSync(PLIST_PATH, plist);
    // Load via launchctl
    try {
        // Unload first in case of stale registration
        try {
            execSync(`launchctl unload "${PLIST_PATH}" 2>/dev/null`);
        }
        catch {
            // ignore
        }
        execSync(`launchctl load "${PLIST_PATH}"`);
        // Wait a moment and check if it started
        setTimeout(() => {
            const info = isDaemonRunning();
            if (info) {
                console.log(chalk.green(`Avis daemon started (pid ${info.pid}, port ${info.port})`));
                console.log(chalk.dim(`  ws://127.0.0.1:${info.port}`));
                console.log(chalk.dim(`  Logs: ${LOG_DIR}/daemon.log`));
            }
            else {
                console.log(chalk.yellow("Daemon loaded but not yet running. Check logs:"));
                console.log(chalk.dim(`  tail -f ${LOG_DIR}/daemon.log`));
            }
        }, 1500);
    }
    catch (err) {
        console.error(chalk.red(`Failed to start daemon: ${err.message}`));
        process.exitCode = 1;
    }
    return true;
}
function handleStop() {
    const info = isDaemonRunning();
    // Unload plist
    if (fs.existsSync(PLIST_PATH)) {
        try {
            execSync(`launchctl unload "${PLIST_PATH}"`);
        }
        catch {
            // ignore
        }
        fs.unlinkSync(PLIST_PATH);
    }
    // Kill process if still running
    if (info) {
        try {
            process.kill(info.pid, "SIGTERM");
        }
        catch {
            // already dead
        }
        console.log(chalk.green(`Avis daemon stopped (was pid ${info.pid})`));
    }
    else {
        console.log(chalk.dim("Daemon was not running (cleaned up plist)."));
    }
    return true;
}
//# sourceMappingURL=daemon.js.map