import { execFile } from 'node:child_process';
import { promisify } from 'node:util';

const execFileAsync = promisify(execFile);

export type ProviderName = 'gemini-api' | 'gemini-cli' | 'claude-api' | 'claude-code' | 'openai-api' | 'codex';

export interface ResolvedConfig {
  provider: ProviderName | null;
  model: string | null;
  fallback_provider: ProviderName | null;
  fallback_model: string | null;
  resolved_from: string | null;
}

export async function resolveProvider(commandType: string): Promise<ResolvedConfig> {
  const endpoint = process.env.PROVIDER_CONFIG_ENDPOINT;
  const botId = process.env.BOT_ID;
  if (!endpoint || !botId) {
    return { provider: null, model: null, fallback_provider: null, fallback_model: null, resolved_from: null };
  }
  try {
    const url = `${endpoint}?botId=${encodeURIComponent(botId)}&command=${encodeURIComponent(commandType)}`;
    const res = await fetch(url, { signal: AbortSignal.timeout(3000) });
    if (!res.ok) throw new Error(`resolve ${res.status}`);
    return await res.json() as ResolvedConfig;
  } catch {
    return { provider: null, model: null, fallback_provider: null, fallback_model: null, resolved_from: null };
  }
}

export interface RouteResult {
  text: string;
  usedFallback: boolean;
  provider: ProviderName;
  model: string | null;
}

export async function routePrompt(
  commandType: string,
  prompt: string,
  legacyFn: (prompt: string) => Promise<string>,
): Promise<RouteResult> {
  const config = await resolveProvider(commandType);

  if (!config.provider) {
    const text = await legacyFn(prompt);
    return { text, usedFallback: false, provider: 'gemini-cli', model: null };
  }

  const tryProvider = async (provider: ProviderName, model: string | null): Promise<string> => {
    if (provider === 'gemini-cli') {
      const apiKey = process.env.GOOGLE_API_KEY;
      const { generateTextCLI } = await import('./gemini/cli.js');
      return generateTextCLI(prompt, apiKey ?? undefined, commandType);
    }
    if (provider === 'gemini-api') {
      const apiKey = process.env.GOOGLE_API_KEY;
      if (!apiKey) throw new Error('GOOGLE_API_KEY not set');
      const { generateText } = await import('./gemini/client.js');
      return generateText(prompt, apiKey);
    }
    if (provider === 'claude-api') {
      const apiKey = process.env.ANTHROPIC_API_KEY;
      if (!apiKey) throw new Error('ANTHROPIC_API_KEY not set');
      const { default: Anthropic } = await import('@anthropic-ai/sdk');
      const client = new Anthropic({ apiKey });
      const msg = await client.messages.create({
        model: model ?? 'claude-sonnet-4-6',
        max_tokens: 4096,
        messages: [{ role: 'user', content: prompt }],
      });
      const block = msg.content[0];
      if (block.type !== 'text') throw new Error('unexpected response type');
      return block.text;
    }
    if (provider === 'claude-code') {
      const { stdout } = await execFileAsync('claude', ['-p', prompt], { timeout: 300000 });
      return stdout.trim();
    }
    if (provider === 'openai-api' || provider === 'codex') {
      const apiKey = process.env.OPENAI_API_KEY;
      if (!apiKey) throw new Error('OPENAI_API_KEY not set');
      const { default: OpenAI } = await import('openai');
      const client = new OpenAI({ apiKey });
      const completion = await client.chat.completions.create({
        model: model ?? 'gpt-4o',
        messages: [{ role: 'user', content: prompt }],
      });
      return completion.choices[0]?.message?.content ?? '';
    }
    throw new Error(`Unknown provider: ${provider}`);
  };

  const { reportMetric } = await import('./metrics-reporter.js');

  try {
    const text = await tryProvider(config.provider, config.model);
    void reportMetric({ model: config.model ?? config.provider, call_type: commandType });
    return { text, usedFallback: false, provider: config.provider, model: config.model };
  } catch (primaryErr) {
    void reportMetric({ model: config.model ?? config.provider, call_type: commandType, error: String(primaryErr) });
    if (config.fallback_provider) {
      try {
        const text = await tryProvider(config.fallback_provider, config.fallback_model);
        void reportMetric({ model: config.fallback_model ?? config.fallback_provider, call_type: `${commandType}:fallback` });
        return { text, usedFallback: true, provider: config.fallback_provider, model: config.fallback_model };
      } catch (fallbackErr) {
        void reportMetric({ model: config.fallback_model ?? config.fallback_provider, call_type: `${commandType}:fallback`, error: String(fallbackErr) });
        throw primaryErr;
      }
    }
    throw primaryErr;
  }
}
