// Engine capability model — which engines can actually run tools, and whether a
// given agent's job depends on that.
//
// Two tiers:
//   - GOVERNED tool loop: dispatches madarX registry tools through
//     executeRegistryTool (gates, allowlists, audit). Today ONLY `anthropic`
//     (generateTickOutput in lib/agent-runner.js).
//   - REAL tool execution of SOME kind: the CLI agents (`claude-code`, `codex`)
//     run their own native tool runtimes (WebSearch/WebFetch/Write/etc.) in a
//     subprocess, so they do real work even though it isn't the governed madarX
//     toolset.
//
// Engines with NEITHER (`ollama`, `hermes`) run a single-shot text completion
// with NO tool dispatch at all. A Hermes/Nous-template instruct model "satisfies"
// a tool task by role-playing the whole loop — emitting `<tool_call>` and an
// invented `<tool_response>` in plain text. Saving that as a deliverable ships
// fiction as a real result. This module is the single source of truth for the
// capability check so the runner can fail loudly instead.

import { normalizeAgentEngine } from './agent-engines.js';
import { getEnabledToolSchemasForAgent } from './tools/registry.js';

// Engines that run the GOVERNED madarX tool loop end-to-end.
const GOVERNED_TOOL_LOOP_ENGINES = new Set(['anthropic']);

// Engines that execute tools for real in SOME form (governed loop OR a native
// CLI tool runtime). Engines NOT in this set produce single-shot text only and
// will fabricate a tool transcript when asked to use tools.
const TOOL_EXECUTING_ENGINES = new Set(['anthropic', 'claude-code', 'codex']);

/**
 * True when the engine runs the governed madarX tool loop (dispatches registry
 * tools through executeRegistryTool with gates/allowlists/audit).
 */
export function engineHasGovernedToolLoop(engine) {
  return GOVERNED_TOOL_LOOP_ENGINES.has(normalizeAgentEngine(engine));
}

/**
 * True when the engine can execute tools for real (governed loop or native CLI
 * runtime). `ollama` / `hermes` return false — they only complete text.
 */
export function engineCanExecuteTools(engine) {
  return TOOL_EXECUTING_ENGINES.has(normalizeAgentEngine(engine));
}

/**
 * True when the agent has at least one enabled madarX tool — i.e. its work
 * depends on tools actually running. An agent with zero enabled tools is a
 * pure-reasoning/summarize job and runs fine single-shot on any engine.
 */
export function agentNeedsTools(agent) {
  if (!agent?.id) return false;
  try {
    return getEnabledToolSchemasForAgent(agent.id).length > 0;
  } catch {
    // If we can't determine the toolset, do NOT over-block — treat as no tools.
    return false;
  }
}

/**
 * Assert that `agent` can actually run its job on its selected engine. Throws a
 * tagged error (`code = 'engine_cannot_run_tools'`) when the agent needs tools
 * but the engine cannot execute any (`ollama` / `hermes`). The caller records
 * this as a FAILED run with the remediation message rather than saving a
 * fabricated transcript as a "complete" deliverable.
 *
 * CLI engines (`claude-code` / `codex`) pass this check — they run real tool
 * runtimes — and rely on the fabricated-transcript detector (Layer B) as the
 * backstop on the rare turn they role-play instead of dispatching.
 *
 * @returns {{ needsTools: boolean, engineCanExecuteTools: boolean }}
 */
export function assertRunnable(agent) {
  const engine = normalizeAgentEngine(agent?.engine);
  const canExecute = engineCanExecuteTools(engine);
  const needsTools = agentNeedsTools(agent);

  if (!needsTools || canExecute) {
    return { needsTools, engineCanExecuteTools: canExecute };
  }

  const e = new Error(
    `This agent has live tools enabled but its engine (${engine}) cannot run tools — it only completes text, ` +
    `so it would fabricate a tool transcript instead of doing real work. The run was stopped. ` +
    `Fix: switch this agent's engine to "Anthropic API" (governed tools) or "Claude Code" in its Details view.`
  );
  e.code = 'engine_cannot_run_tools';
  e.engine = engine;
  throw e;
}
