import type { AIProvider, AIProviderRegistry } from '@genesislcap/foundation-ai';
import type { AgentConfig } from './config';

/**
 * Resolve a provider name against the registry and assert it implements
 * `chat()` — the capability the chat tool-loop depends on. Throws with a
 * message naming the agent so misconfigured agents are easy to track down.
 *
 * Used by both the up-front static walk in `OrchestratingDriver` and by
 * `ChatDriver`'s per-turn resolver for function-form `provider` values.
 *
 * @internal
 */
export function resolveChatProvider(
  registry: AIProviderRegistry,
  providerName: string,
  agentName: string,
): AIProvider {
  const provider = registry.get(providerName);
  if (!provider) {
    const available = registry.names().join(', ') || '(none)';
    throw new Error(
      `Agent "${agentName}" references unknown provider "${providerName}". Registered: ${available}`,
    );
  }
  if (!provider.chat) {
    throw new Error(
      `Agent "${agentName}" resolved to provider "${providerName}" which does not implement chat().`,
    );
  }
  return provider;
}

/**
 * Walk an agent tree (including nested `subAgents`) and validate every static
 * `provider: string` against the registry. Function-form `provider` values are
 * deferred — they validate on first resolution inside the driver.
 *
 * Throws on the first failure, with the same message shape produced by
 * {@link resolveChatProvider}.
 *
 * @internal
 */
export function validateStaticAgentProviders(
  agents: AgentConfig[],
  registry: AIProviderRegistry,
): void {
  const visited = new Set<AgentConfig>();
  const walk = (agent: AgentConfig): void => {
    if (visited.has(agent)) return;
    visited.add(agent);
    if (typeof agent.provider === 'string') {
      resolveChatProvider(registry, agent.provider, agent.name);
    }
    for (const sub of agent.subAgents ?? []) walk(sub);
  };
  for (const root of agents) walk(root);
}
