#!/usr/bin/env -S npx tsx
/**
 * Standalone Memory worker.
 *
 * Generic process container for background memory operations. Hosts a
 * registry of MemorySubTask implementations and ticks each one on a
 * fixed interval. v1 only ships the Temper Summary sub-task (chat raw
 * → memory_store ingest); decay / profile compaction / cross-session
 * linking land in v1.x as additional sub-tasks under the same loop.
 *
 * Lifecycle:
 *  - Spawned by bin/forge-server.mjs alongside terminal/workspace/telegram
 *  - --forge-port=<webPort> tag for cleanupOrphans (multi-instance safe)
 *  - SIGTERM is honored; the current tick finishes before exit
 *  - No HTTP port; pure poller
 *
 * Failure isolation: a sub-task's tick() rejecting is logged and the
 * next sub-task still runs in the same cycle.
 */

const MEMORY_TICK_SEC = 120;

export interface MemorySubTask {
  /** Stable identifier for logs + future health introspection. */
  readonly name: string;
  /** One pass of work. Errors are caught at the supervisor level. */
  tick(): Promise<void>;
}

const subTasks: MemorySubTask[] = [];

function registerSubTask(task: MemorySubTask): void {
  subTasks.push(task);
  console.log(`[memory] registered sub-task: ${task.name}`);
}

let running = true;
let tickInProgress = false;

async function runTick(): Promise<void> {
  if (!running) return;
  tickInProgress = true;
  const cycleStart = Date.now();
  for (const t of subTasks) {
    const start = Date.now();
    try {
      await t.tick();
      console.log(`[memory] ${t.name} tick ok (${Date.now() - start}ms)`);
    } catch (err) {
      const msg = err instanceof Error ? err.message : String(err);
      console.warn(`[memory] ${t.name} tick failed: ${msg}`);
    }
  }
  console.log(`[memory] cycle done (${Date.now() - cycleStart}ms, ${subTasks.length} sub-task${subTasks.length === 1 ? '' : 's'})`);
  tickInProgress = false;
}

async function loop(): Promise<void> {
  await runTick();
  if (!running) return;
  setTimeout(loop, MEMORY_TICK_SEC * 1000);
}

async function main(): Promise<void> {
  console.log(`[memory] starting standalone (tick every ${MEMORY_TICK_SEC}s)`);

  // Register v1 sub-tasks. Lazy import keeps this entry declarative
  // and means a sub-task module failing to load doesn't kill the
  // process — the supervisor loop still runs (with one fewer task).
  await safeRegister('temper-summary', async () => {
    const { temperSummaryTask } = await import('./memory/temper-summary');
    return temperSummaryTask();
  });

  if (subTasks.length === 0) {
    console.log('[memory] no sub-tasks registered, exiting');
    return;
  }

  await loop();
}

async function safeRegister(name: string, factory: () => Promise<MemorySubTask>): Promise<void> {
  try {
    registerSubTask(await factory());
  } catch (err) {
    const msg = err instanceof Error ? err.message : String(err);
    console.warn(`[memory] failed to register ${name}: ${msg}`);
  }
}

process.on('SIGTERM', () => {
  console.log('[memory] SIGTERM received');
  running = false;
  // Let the in-flight tick finish; exit after a generous grace window.
  setTimeout(() => process.exit(0), tickInProgress ? 30000 : 0);
});

process.on('SIGINT', () => {
  console.log('[memory] SIGINT received');
  running = false;
  setTimeout(() => process.exit(0), tickInProgress ? 30000 : 0);
});

main().catch((err) => {
  console.error('[memory] fatal:', err);
  process.exit(1);
});
