/**
 * OpenAI — API key from platform_settings first, then OPENAI_API_KEY env.
 */
import { getPlatformSettings } from './platformSettingsService.js';

const DEFAULT_BASE = 'https://api.openai.com/v1';

let credsCache: { key: string; base: string; at: number } | null = null;
const CACHE_TTL_MS = 60_000;

/** Call after updating platform_settings so the next request picks up new credentials. */
export function invalidateOpenAiCredentialsCache(): void {
  credsCache = null;
}

/** Returns bearer key + base URL, or null if no key is available. */
export async function resolveOpenAiCredentials(): Promise<{ key: string; base: string } | null> {
  const now = Date.now();
  if (credsCache && now - credsCache.at < CACHE_TTL_MS) {
    return credsCache.key ? { key: credsCache.key, base: credsCache.base } : null;
  }

  const row = await getPlatformSettings();
  const dbKey = row?.openai_api_key?.trim() ?? '';
  const envKey = process.env.OPENAI_API_KEY?.trim() ?? '';
  const key = dbKey || envKey;
  const baseRaw = row?.openai_base_url?.trim() || process.env.OPENAI_BASE_URL?.trim() || DEFAULT_BASE;
  const base = baseRaw.replace(/\/$/, '');
  credsCache = { key, base, at: now };
  return key ? { key, base } : null;
}

export async function isOpenAiConfigured(): Promise<boolean> {
  const c = await resolveOpenAiCredentials();
  return Boolean(c?.key);
}

/** Model ids suitable for chat completions (filters obvious non-chat endpoints). */
export async function listOpenAiModels(): Promise<string[]> {
  const c = await resolveOpenAiCredentials();
  if (!c?.key) throw new Error('No OpenAI API key configured (Settings or OPENAI_API_KEY)');

  const res = await fetch(`${c.base}/models`, {
    headers: { Authorization: `Bearer ${c.key}` },
  });
  const raw = await res.text();
  if (!res.ok) throw new Error(`OpenAI models error ${res.status}: ${raw.slice(0, 400)}`);

  const data = JSON.parse(raw) as { data?: Array<{ id: string }> };
  const ids = (data.data ?? []).map(m => m.id).filter(isLikelyChatModel);
  return [...new Set(ids)].sort();
}

function isLikelyChatModel(id: string): boolean {
  const lower = id.toLowerCase();
  if (
    lower.includes('embedding') ||
    lower.includes('moderation') ||
    lower.includes('whisper') ||
    lower.includes('tts') ||
    lower.includes('dall-e') ||
    lower.includes('realtime') ||
    lower.includes('audio') ||
    lower.includes('transcribe') ||
    lower.includes('search')
  ) {
    return false;
  }
  return (
    id.startsWith('gpt-') ||
    /^o[0-9]/.test(id) ||
    id.startsWith('chatgpt-') ||
    id.startsWith('ft:') // fine-tuned chat models
  );
}

export async function chatCompletionText(opts: {
  model: string;
  system: string;
  messages: Array<{ role: 'user' | 'assistant'; content: string }>;
  maxTokens?: number;
}): Promise<string> {
  const c = await resolveOpenAiCredentials();
  if (!c?.key) throw new Error('OPENAI_API_KEY is not set');

  const res = await fetch(`${c.base}/chat/completions`, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${c.key}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      model: opts.model,
      messages: [{ role: 'system', content: opts.system }, ...opts.messages],
      max_tokens: opts.maxTokens ?? 1024,
    }),
  });

  const raw = await res.text();
  if (!res.ok) throw new Error(`OpenAI error ${res.status}: ${raw.slice(0, 500)}`);

  const data = JSON.parse(raw) as {
    choices?: Array<{ message?: { content?: string | null } }>;
  };
  const text = data.choices?.[0]?.message?.content?.trim();
  if (!text) throw new Error('OpenAI returned empty content');
  return text;
}
