/**
 * OpenCode process supervisor.
 *
 * Monitors the OpenCode child process for unexpected exits and
 * auto-restarts with exponential backoff. Integrates with proxy
 * and SSE modules to update auth and reconnect after restart.
 */
import { logger } from "../utils/logger.js";
import { spawnOpenCode } from "./spawn.js";
import { setOpenCodeAuth, clearOpenCodeAuth } from "./proxy.js";
const BACKOFF_BASE_MS = 2_000;
const BACKOFF_MAX_MS = 30_000;
let currentProcess = null;
let restartAttempts = 0;
let restartTimer = null;
let shuttingDown = false;
let exitHandler = null;
let supervisorOptions = {};
/**
 * Start supervising an already-spawned OpenCode process.
 *
 * Attaches an exit listener that triggers auto-restart with
 * exponential backoff on unexpected exits.
 */
export function supervise(proc, options = {}) {
    currentProcess = proc;
    supervisorOptions = options;
    shuttingDown = false;
    restartAttempts = 0;
    attachExitHandler(proc.process);
}
/**
 * Stop the supervisor and the managed OpenCode process.
 * Prevents any further restart attempts.
 */
export async function stopSupervisor() {
    shuttingDown = true;
    if (restartTimer) {
        clearTimeout(restartTimer);
        restartTimer = null;
    }
    if (currentProcess) {
        detachExitHandler(currentProcess.process);
        await currentProcess.stop();
        currentProcess = null;
    }
    clearOpenCodeAuth();
}
/**
 * Get the current supervised OpenCode process, if any.
 */
export function getSupervisedProcess() {
    return currentProcess;
}
/**
 * Check whether the supervisor is in shutdown mode.
 */
export function isShuttingDown() {
    return shuttingDown;
}
/**
 * Get the current restart attempt count (for testing/monitoring).
 */
export function getRestartAttempts() {
    return restartAttempts;
}
/**
 * Reset supervisor state (for testing).
 */
export function resetSupervisor() {
    shuttingDown = false;
    restartAttempts = 0;
    currentProcess = null;
    exitHandler = null;
    supervisorOptions = {};
    if (restartTimer) {
        clearTimeout(restartTimer);
        restartTimer = null;
    }
}
/**
 * Attach an exit handler to the child process for monitoring.
 */
function attachExitHandler(child) {
    exitHandler = (code, signal) => {
        handleUnexpectedExit(code, signal);
    };
    child.on("exit", exitHandler);
}
/**
 * Detach the exit handler from the child process.
 */
function detachExitHandler(child) {
    if (exitHandler) {
        child.removeListener("exit", exitHandler);
        exitHandler = null;
    }
}
/**
 * Handle an unexpected process exit by scheduling a restart.
 */
function handleUnexpectedExit(code, signal) {
    if (shuttingDown) {
        return;
    }
    logger.error(`OpenCode process exited unexpectedly: code=${code}, signal=${signal}`);
    currentProcess = null;
    clearOpenCodeAuth();
    scheduleRestart();
}
/**
 * Schedule a restart with exponential backoff.
 */
function scheduleRestart() {
    if (shuttingDown) {
        return;
    }
    restartAttempts++;
    const delay = Math.min(BACKOFF_BASE_MS * Math.pow(2, restartAttempts - 1), BACKOFF_MAX_MS);
    logger.info(`Scheduling OpenCode restart in ${delay}ms (attempt ${restartAttempts})`);
    restartTimer = setTimeout(() => {
        restartTimer = null;
        void attemptRestart();
    }, delay);
}
/**
 * Attempt to restart the OpenCode process.
 */
async function attemptRestart() {
    if (shuttingDown) {
        return;
    }
    try {
        logger.info(`Restarting OpenCode (attempt ${restartAttempts})...`);
        const proc = await spawnOpenCode(supervisorOptions.port);
        currentProcess = proc;
        setOpenCodeAuth(proc.auth);
        // Reset backoff on successful restart
        restartAttempts = 0;
        logger.info("OpenCode restarted successfully");
        // Notify caller about restart (for SSE re-subscription, etc.)
        supervisorOptions.onRestarted?.(proc.auth);
        // Supervise the new process
        attachExitHandler(proc.process);
    }
    catch (err) {
        const message = err instanceof Error ? err.message : String(err);
        logger.error(`OpenCode restart failed: ${message}`);
        // Schedule another restart attempt
        scheduleRestart();
    }
}
//# sourceMappingURL=supervisor.js.map