/**
 * Low-level model calls (Anthropic or OpenAI). Server-only.
 * Callers should check canUseAi() first and provide their own fallback for
 * DEMO_MODE / missing key.
 */
import { env } from '../env';

export interface CallOptions {
  system?: string;
  maxTokens?: number;
  temperature?: number;
}

export async function callModel(userPrompt: string, opts: CallOptions = {}): Promise<string> {
  const { system, maxTokens = 1200, temperature = 0.7 } = opts;
  if (env.aiProvider === 'openai') return callOpenai(userPrompt, system, maxTokens, temperature);
  return callAnthropic(userPrompt, system, maxTokens, temperature);
}

async function callAnthropic(
  prompt: string,
  system: string | undefined,
  maxTokens: number,
  temperature: number,
): Promise<string> {
  const res = await fetch('https://api.anthropic.com/v1/messages', {
    method: 'POST',
    headers: {
      'content-type': 'application/json',
      'x-api-key': env.aiApiKey,
      'anthropic-version': '2023-06-01',
    },
    body: JSON.stringify({
      model: env.aiModel,
      max_tokens: maxTokens,
      temperature,
      ...(system ? { system } : {}),
      messages: [{ role: 'user', content: prompt }],
    }),
  });
  const json = await res.json();
  return json?.content?.[0]?.text ?? '';
}

async function callOpenai(
  prompt: string,
  system: string | undefined,
  maxTokens: number,
  temperature: number,
): Promise<string> {
  const messages: { role: string; content: string }[] = [];
  if (system) messages.push({ role: 'system', content: system });
  messages.push({ role: 'user', content: prompt });
  const res = await fetch('https://api.openai.com/v1/chat/completions', {
    method: 'POST',
    headers: { 'content-type': 'application/json', authorization: `Bearer ${env.aiApiKey}` },
    body: JSON.stringify({ model: env.aiModel, max_tokens: maxTokens, temperature, messages }),
  });
  const json = await res.json();
  return json?.choices?.[0]?.message?.content ?? '';
}

/**
 * Vision call: describe an image (data URL, e.g. a child's canvas drawing).
 * Supports OpenAI and Anthropic image formats.
 */
export async function callVision(
  prompt: string,
  dataUrl: string,
  opts: { system?: string; maxTokens?: number } = {},
): Promise<string> {
  const maxTokens = opts.maxTokens ?? 300;
  if (env.aiProvider === 'openai') {
    const messages: any[] = [];
    if (opts.system) messages.push({ role: 'system', content: opts.system });
    messages.push({
      role: 'user',
      content: [
        { type: 'text', text: prompt },
        { type: 'image_url', image_url: { url: dataUrl } },
      ],
    });
    const res = await fetch('https://api.openai.com/v1/chat/completions', {
      method: 'POST',
      headers: { 'content-type': 'application/json', authorization: `Bearer ${env.aiApiKey}` },
      body: JSON.stringify({ model: env.aiModel, max_tokens: maxTokens, messages }),
    });
    const json = await res.json();
    return json?.choices?.[0]?.message?.content ?? '';
  }

  // Anthropic: base64 image block
  const m = dataUrl.match(/^data:(image\/\w+);base64,(.+)$/);
  const mediaType = m?.[1] ?? 'image/png';
  const data = m?.[2] ?? '';
  const res = await fetch('https://api.anthropic.com/v1/messages', {
    method: 'POST',
    headers: {
      'content-type': 'application/json',
      'x-api-key': env.aiApiKey,
      'anthropic-version': '2023-06-01',
    },
    body: JSON.stringify({
      model: env.aiModel,
      max_tokens: maxTokens,
      ...(opts.system ? { system: opts.system } : {}),
      messages: [
        {
          role: 'user',
          content: [
            { type: 'image', source: { type: 'base64', media_type: mediaType, data } },
            { type: 'text', text: prompt },
          ],
        },
      ],
    }),
  });
  const json = await res.json();
  return json?.content?.[0]?.text ?? '';
}

/** Extract the first JSON array from a possibly-noisy model response. */
export function extractJsonArray(text: string): any {
  const match = text.match(/\[[\s\S]*\]/);
  if (!match) return null;
  try {
    return JSON.parse(match[0]);
  } catch {
    return null;
  }
}
