'use strict';

/**
 * Lifecycle manager for the localhost OAuth-proxy child process.
 *
 * The proxy code lives in claude-task-manager/oauth-proxy.js. This module:
 *   - Spawns it as a child node process (`node oauth-proxy.js`) on demand.
 *   - Polls the child's /_health endpoint to surface live status.
 *   - Stops the child cleanly on stop() or CTM shutdown.
 *
 * Why a child process: keeps the proxy isolated from CTM's main loop, so a
 * crash or hang in upstream Anthropic doesn't take down the workbench. Also
 * lets us bind/unbind cleanly without juggling http server lifetimes inside
 * the request handler.
 */

const path = require('node:path');
const { spawn } = require('node:child_process');

const PROXY_SCRIPT = path.resolve(__dirname, '..', 'oauth-proxy.js');
const DEFAULT_PORT = parseInt(process.env.OAUTH_PROXY_PORT || '3458', 10);

let _child = null;
let _startedAt = null;
let _lastError = null;
let _shutdownRegistered = false;

function _ensureShutdownHook() {
  if (_shutdownRegistered) return;
  _shutdownRegistered = true;
  const stopOnExit = () => {
    if (_child && !_child.killed) {
      try { _child.kill('SIGTERM'); } catch {}
    }
  };
  process.on('exit', stopOnExit);
  process.on('SIGINT', stopOnExit);
  process.on('SIGTERM', stopOnExit);
}

function isRunning() {
  return !!(_child && !_child.killed && _child.exitCode === null);
}

function start({ port = DEFAULT_PORT } = {}) {
  if (isRunning()) {
    return { ok: true, running: true, port, pid: _child.pid, alreadyRunning: true };
  }
  _ensureShutdownHook();
  _lastError = null;
  // env: scrub ANTHROPIC_* vars so the proxy uses the keychain (the very
  // thing the user is opting into by enabling this proxy). The whole point
  // of Strategy 3 is to use claude.ai login — not whatever env vars happen
  // to be set by Wall-E or other tooling.
  const childEnv = { ...process.env };
  delete childEnv.ANTHROPIC_API_KEY;
  delete childEnv.ANTHROPIC_AUTH_TOKEN;
  childEnv.OAUTH_PROXY_PORT = String(port);
  try {
    _child = spawn(process.execPath, [PROXY_SCRIPT], {
      stdio: ['ignore', 'pipe', 'pipe'],
      env: childEnv,
      detached: false,
    });
  } catch (err) {
    _lastError = err.message;
    return { ok: false, running: false, error: err.message };
  }
  _startedAt = Date.now();
  _child.stdout.on('data', (d) => {
    // Forward to CTM stderr so it lands in the same log files. Prefix to
    // disambiguate.
    process.stderr.write('[oauth-proxy] ' + d.toString());
  });
  _child.stderr.on('data', (d) => {
    process.stderr.write('[oauth-proxy] ' + d.toString());
  });
  _child.on('error', (err) => {
    _lastError = err.message;
  });
  _child.on('exit', (code, signal) => {
    if (code !== 0 && !signal) {
      _lastError = `exited code=${code}`;
    }
    _child = null;
    _startedAt = null;
  });
  return { ok: true, running: true, port, pid: _child.pid };
}

function stop() {
  if (!isRunning()) {
    return { ok: true, running: false, alreadyStopped: true };
  }
  try {
    _child.kill('SIGTERM');
  } catch (err) {
    return { ok: false, error: err.message };
  }
  return { ok: true, running: false };
}

async function status({ port = DEFAULT_PORT } = {}) {
  if (!isRunning()) {
    return {
      running: false,
      port,
      pid: null,
      uptime_ms: null,
      health: null,
      last_error: _lastError,
    };
  }
  let health = null;
  try {
    const resp = await fetch(`http://127.0.0.1:${port}/_health`, {
      signal: AbortSignal.timeout(2000),
    });
    if (resp.ok) health = await resp.json();
  } catch (err) {
    _lastError = `health probe failed: ${err.message}`;
  }
  return {
    running: true,
    port,
    pid: _child.pid,
    uptime_ms: _startedAt ? Date.now() - _startedAt : null,
    health,
    last_error: _lastError,
  };
}

module.exports = { start, stop, status, isRunning };
