/**
 * Agent harness dispatcher.
 *
 * Picks the right harness implementation based on `cfg.ai.provider` and
 * forwards every call. The supervisor, channels, and scheduler still import
 * from `./bloby-agent.js` exactly as they did before — this file is a
 * compatibility shim so the harness split is invisible to callers.
 *
 * Provider routing:
 *   anthropic (or empty/default) → Claude Agent SDK harness — unchanged
 *   openai                       → Codex app-server harness  — Phase 2
 *
 * Cleanup operations (`endAllConversations`, `endConversation(id)`) are
 * fanned out to *both* harnesses so a stale conversation on a previously
 * active provider can't outlive a provider switch.
 */

import * as claude from './harnesses/claude.js';
import * as codex from './harnesses/codex.js';
import * as pi from './harnesses/pi/index.js';
import type { Harness, OnAgentMessage, RecentMessage, AgentAttachment, AgentQueryRequest, AgentQueryResult } from './harnesses/types.js';
import type { SavedFile } from './file-saver.js';
import { loadConfig } from '../shared/config.js';

export type { RecentMessage, AgentAttachment, AgentQueryRequest, AgentQueryResult };

const HARNESSES: Record<string, Harness> = {
  anthropic: claude,
  openai: codex,
  pi: pi as unknown as Harness,
};

/** Resolve the harness for the currently-configured provider. */
function activeHarness(): Harness {
  let provider = '';
  try {
    provider = loadConfig().ai?.provider || '';
  } catch {
    // Pre-onboard or transient config error: fall through to default.
  }
  return HARNESSES[provider] ?? claude;
}

/* ── Live conversation API ─────────────────────────────────────────────── */

export function startConversation(
  conversationId: string,
  model: string,
  onMessage: OnAgentMessage,
  names?: { botName: string; humanName: string },
  recentMessages?: RecentMessage[],
): Promise<boolean> {
  return activeHarness().startConversation(conversationId, model, onMessage, names, recentMessages);
}

export function pushMessage(
  conversationId: string,
  content: string,
  attachments?: AgentAttachment[],
  savedFiles?: SavedFile[],
): boolean {
  // Push to whichever harness owns this conversation; routing by current
  // config alone could miss a conversation that was started under a previous
  // provider (rare but possible during a switch).
  for (const h of Object.values(HARNESSES)) {
    if (h.hasConversation(conversationId)) {
      return h.pushMessage(conversationId, content, attachments, savedFiles);
    }
  }
  return activeHarness().pushMessage(conversationId, content, attachments, savedFiles);
}

export function hasConversation(conversationId: string): boolean {
  return Object.values(HARNESSES).some((h) => h.hasConversation(conversationId));
}

export function endConversation(conversationId: string): void {
  for (const h of Object.values(HARNESSES)) h.endConversation(conversationId);
}

export function endAllConversations(): void {
  for (const h of Object.values(HARNESSES)) h.endAllConversations();
}

export function isConversationBusy(conversationId: string): boolean {
  return Object.values(HARNESSES).some((h) => h.isConversationBusy(conversationId));
}

/** True if ANY conversation in ANY harness is mid-turn. Lets the supervisor defer backend
 *  restarts during channel/Alexa turns, which don't set the dashboard's agentQueryActive flag. */
export function anyOneShotActive(): boolean {
  return Object.values(HARNESSES).some((h) => h.anyOneShotActive());
}

export function anyConversationBusy(): boolean {
  return Object.values(HARNESSES).some((h) => h.anyConversationBusy());
}

export async function stopSubAgentTask(conversationId: string, taskId: string): Promise<void> {
  for (const h of Object.values(HARNESSES)) {
    if (h.hasConversation(conversationId)) {
      await h.stopSubAgentTask(conversationId, taskId);
      return;
    }
  }
}

export function warmUpForLiveConversation(
  model: string,
  names?: { botName: string; humanName: string },
): Promise<void> {
  return activeHarness().warmUpForLiveConversation(model, names);
}

/* ── One-shot API ──────────────────────────────────────────────────────── */

export function startBlobyAgentQuery(
  conversationId: string,
  prompt: string,
  model: string,
  onMessage: OnAgentMessage,
  attachments?: AgentAttachment[],
  savedFiles?: SavedFile[],
  names?: { botName: string; humanName: string },
  recentMessages?: RecentMessage[],
  supportPrompt?: string,
  maxTurns?: number,
): Promise<void> {
  return activeHarness().startBlobyAgentQuery(
    conversationId, prompt, model, onMessage, attachments, savedFiles,
    names, recentMessages, supportPrompt, maxTurns,
  );
}

export function stopBlobyAgentQuery(conversationId: string): void {
  for (const h of Object.values(HARNESSES)) h.stopBlobyAgentQuery(conversationId);
}

/* ── Workspace agent endpoint ──────────────────────────────────────────── */

export function runAgentQuery(req: AgentQueryRequest): Promise<AgentQueryResult> {
  return activeHarness().runAgentQuery(req);
}
