/**
 * tools/matrix-presence-harness/agent-proc.ts
 *
 *   *** DEV harness — LOCAL SANDBOX ONLY. ***
 *
 * Runs ONE presence agent as its own OS process: joins the fleet presence
 * room and heartbeats `mosaic.presence` via @mosaicstack/comms (RFC-001 P1).
 * Kept as a standalone process so the validation harness can `kill -9` it to
 * prove A3 (hard-kill -> offline within dark_threshold) against a real crash,
 * not a graceful shutdown.
 *
 * Auth in DEV is Application-Service masquerade: the process presents the
 * as_token and acts as its own @agent-<slug> MXID. (Per-agent minted tokens
 * are P2, RFC-001 §2.2/§8.)
 *
 * ESM / NodeNext: .js import extensions.
 */
import { MinimalMatrixClient, PresenceAgent, type LivenessPolicy } from '@mosaicstack/comms';

const env = (k: string, fallback?: string): string => {
  const v = process.env[k] ?? fallback;
  if (v === undefined) throw new Error(`missing env ${k}`);
  return v;
};

async function main(): Promise<void> {
  const homeserverUrl = env('MATRIX_CS_URL');
  const asToken = env('MOSAIC_AS_TOKEN');
  const serverName = env('MATRIX_SERVER_NAME');
  const slug = env('AGENT_SLUG');
  const roomId = env('FLEET_ROOM_ID');
  const intervalMs = Number(env('HEARTBEAT_INTERVAL_MS', '1000'));

  const mxid = `@agent-${slug}:${serverName}`;
  const policy: LivenessPolicy = {
    heartbeatIntervalMs: intervalMs,
    missTolerance: Number(env('MISS_TOLERANCE', '2')),
    darkThresholdMs: Number(env('DARK_THRESHOLD_MS', '6000')),
  };

  const client = new MinimalMatrixClient({
    homeserverUrl,
    accessToken: asToken,
    actAsUserId: mxid,
  });
  const agent = new PresenceAgent({
    client,
    agent: { mxid, slug, harness: 'claude-code' },
    roomId,
    intervalMs,
    policy,
    onError: (err) => console.error(`[${slug}] heartbeat error:`, (err as Error).message),
  });

  await agent.connect();
  agent.start();
  console.log(`[${slug}] pid=${process.pid} mxid=${mxid} heartbeating every ${intervalMs}ms`);

  // Graceful stop only on SIGTERM; the A3 test uses SIGKILL (no cleanup runs).
  process.on('SIGTERM', () => {
    void agent.stop().finally(() => process.exit(0));
  });
  // Keep the event loop alive indefinitely.
  setInterval(() => {}, 1 << 30);
}

main().catch((err) => {
  console.error('agent-proc fatal:', err);
  process.exit(1);
});
