#!/usr/bin/env -S npx --yes tsx
// =============================================================================
// session_health_data.ts — Collect raw data for session health classification
//
// Part of: .opencode/skills/auto-agents-system/scripts/
//
// Fetches all sessions, their current busy/idle status, and their recent
// messages, then returns this data as a structured JSON payload.  This script
// does NOT classify health — it only collects the raw signals.  The caller
// (async-agent-util's "health" operation) is responsible for reading and
// interpreting the message content to produce a health classification.
//
// Each message in recent_messages is a FormattedMessage with a parts[] array
// preserving every part type in original order — nothing is dropped or truncated:
//   text        — visible model output
//   reasoning   — full internal chain-of-thought (clearly distinct from text)
//   tool        — full tool call: name, status, complete input + output, error flag
//   patch       — file-change snapshot: hash + list of modified files
//   step_start  — inference step boundary (snapshot ref)
//   step_finish — inference step boundary: reason, tokens, cost
//
// USAGE:
//   session_health_data.ts [--message-limit N] [--server URL]
//
//   --message-limit N   Max recent messages to fetch per session (default: 15)
//   --server URL        OpenCode Server base URL (default: http://localhost:4096)
//
// OUTPUT (stdout): JSON object:
//   {
//     "collected_at_ms": <epoch ms when this snapshot was taken>,
//     "sessions": [ ...HealthDataEntry objects... ]
//   }
//
// collected_at_ms is used by timing evaluators to calculate how long a session
// has been inactive without needing the caller to know the current time.
//
// DEPENDENCIES: Node.js >= 18 (native fetch), npx tsx (TypeScript execution)
// =============================================================================
import { fileURLToPath } from 'node:url';
import {
  listSessions,
  getAllStatuses,
  getMessages,
  formatMessages,
  withStatuses,
  parseServerFlag,
  getFlag,
  type HealthDataEntry,
} from './session_api.ts';

// Fetch messages for multiple sessions with bounded concurrency
const MESSAGE_CONCURRENCY = 10;

async function pLimit<T>(
  tasks: ReadonlyArray<() => Promise<T>>,
  limit: number,
): Promise<T[]> {
  const results: T[] = new Array(tasks.length);
  let next = 0;
  async function worker(): Promise<void> {
    while (next < tasks.length) {
      const i = next++;
      results[i] = await tasks[i]();
    }
  }
  await Promise.all(
    Array.from({ length: Math.min(limit, tasks.length) }, worker),
  );
  return results;
}

// ─────────────────────────────────────────────────────────────────────────────
// MAIN
// ─────────────────────────────────────────────────────────────────────────────

async function main(): Promise<void> {
  const { server, rest } = parseServerFlag(process.argv);

  if (rest.includes('-h') || rest.includes('--help')) {
    process.stderr.write(
      'Usage: session_health_data.ts [--message-limit N] [--server URL]\n',
    );
    process.exit(0);
  }

  const limitStr     = getFlag(rest, '--message-limit');
  const messageLimit = limitStr !== undefined ? parseInt(limitStr, 10) : 15;
  if (isNaN(messageLimit) || messageLimit < 1) {
    process.stderr.write('ERROR: --message-limit must be a positive integer\n');
    process.exit(1);
  }

  process.stderr.write(`Fetching health data (last ${messageLimit} msgs/session) from ${server}…\n`);

  // Fetch sessions and statuses in parallel
  const [sessions, statuses] = await Promise.all([
    listSessions(server),
    getAllStatuses(server),
  ]);

  const enriched = withStatuses(sessions, statuses);
  process.stderr.write(`Found ${enriched.length} session(s). Fetching recent messages…\n`);

  // Fetch messages for all sessions concurrently (bounded concurrency)
  const allMessages = await pLimit(
    enriched.map(s => async () => {
      try {
        const msgs = await getMessages(server, s.id, messageLimit);
        return formatMessages(msgs);
      } catch (err) {
        process.stderr.write(
          `WARNING: Could not fetch messages for session ${s.id}: ${err}\n`,
        );
        return [];
      }
    }),
    MESSAGE_CONCURRENCY,
  );

  const healthData: HealthDataEntry[] = enriched.map((s, i) => ({
    id: s.id,
    title: s.title,
    tag: s.tag,
    status: s.status,
    last_active: s.last_active,
    recent_messages: allMessages[i],
  }));

  const output = {
    collected_at_ms: Date.now(),
    sessions: healthData,
  };

  process.stderr.write(`Health data collected for ${healthData.length} session(s).\n`);
  process.stdout.write(JSON.stringify(output, null, 2) + '\n');
}

// ─────────────────────────────────────────────────────────────────────────────
// CLI ENTRY POINT
// ─────────────────────────────────────────────────────────────────────────────

if (process.argv[1] === fileURLToPath(import.meta.url)) {
  main().catch((err: unknown) => {
    process.stderr.write(`ERROR: ${err instanceof Error ? err.message : String(err)}\n`);
    process.exit(1);
  });
}
