/**
 * Race an agent turn against a wall clock; on timeout, abort the in-flight turn
 * and await {@link Agent.waitForIdle} so a subsequent {@link Agent.prompt} is allowed.
 */

import type { Agent } from '@earendil-works/pi-agent-core';

import type { Config } from '../../config/schema.js';

/** Minimum per-turn timeout (1 minute). */
export const MIN_AGENT_TURN_TIMEOUT_MS = 60_000;

/** Maximum per-turn timeout (4 hours), aligned with `maxTaskDurationMs` schema cap. */
export const MAX_AGENT_TURN_TIMEOUT_MS = 4 * 60 * 60 * 1000;

/** Default when `agents.defaults.maxTaskDurationMs` is unset (30 minutes). */
export const DEFAULT_AGENT_TURN_TIMEOUT_MS = 30 * 60 * 1000;

/**
 * Effective turn timeout from config or default.
 * Uses `agents.defaults.maxTaskDurationMs` when set (clamped to schema bounds).
 */
export function resolveAgentTurnTimeoutMs(config?: Config): number {
  const raw = config?.agents?.defaults?.maxTaskDurationMs;
  if (typeof raw === 'number' && Number.isFinite(raw)) {
    const n = Math.floor(raw);
    return Math.min(MAX_AGENT_TURN_TIMEOUT_MS, Math.max(MIN_AGENT_TURN_TIMEOUT_MS, n));
  }
  return DEFAULT_AGENT_TURN_TIMEOUT_MS;
}

export function isAgentTurnTimeoutError(err: unknown): boolean {
  return err instanceof Error && err.message.startsWith('Agent turn timed out after');
}

export async function runAgentTurnWithTimeout(
  agent: Agent,
  runTurn: () => Promise<void>,
  timeoutMs: number,
): Promise<void> {
  let timeoutId: ReturnType<typeof setTimeout> | undefined;
  const timeoutPromise = new Promise<never>((_, reject) => {
    timeoutId = setTimeout(
      () => reject(new Error(`Agent turn timed out after ${timeoutMs / 1000}s`)),
      timeoutMs,
    );
  });

  try {
    await Promise.race([
      runTurn().finally(() => {
        if (timeoutId !== undefined) {
          clearTimeout(timeoutId);
        }
      }),
      timeoutPromise,
    ]);
  } catch (err) {
    if (isAgentTurnTimeoutError(err)) {
      agent.abort();
      await agent.waitForIdle();
    }
    throw err;
  }
}
