import type { AiProvider } from '@wa-ticketing/shared';
import {
  chatCompletionTextWithCredentials,
  isOpenAiConfigured,
  listOpenAiModelsWithCredentials,
  resolveOpenAiCredentials,
  type OpenAiChatMessage,
} from './openaiService.js';
import { buildAnthropicRequestBody } from './anthropicMessageHelpers.js';
import {
  isOpenRouterConfigured,
  listOpenRouterModels,
  OPENROUTER_DEFAULT_BASE,
  openRouterRequestHeaders,
  resolveOpenRouterCredentials,
} from './openrouterService.js';

const OPENAI_DEFAULT_BASE = 'https://api.openai.com/v1';
const ANTHROPIC_DEFAULT_BASE = 'https://api.anthropic.com';
const ANTHROPIC_VERSION = '2023-06-01';

/** True when platform OpenAI or OpenRouter credentials are available. */
export async function isAnyPlatformLlmConfigured(): Promise<boolean> {
  return (await isOpenAiConfigured()) || (await isOpenRouterConfigured());
}

/** Platform-level chat completion: OpenAI first, then OpenRouter. */
export async function platformChatCompletionText(opts: {
  model: string;
  system: string;
  messages: OpenAiChatMessage[];
  maxTokens?: number;
  responseFormat?: 'json_object';
}): Promise<string> {
  const openai = await resolveOpenAiCredentials();
  if (openai?.key) {
    return chatCompletionTextWithCredentials({
      base: openai.base,
      apiKey: openai.key,
      model: opts.model,
      system: opts.system,
      messages: opts.messages,
      maxTokens: opts.maxTokens,
      responseFormat: opts.responseFormat,
    });
  }

  const or = await resolveOpenRouterCredentials();
  if (!or?.key) throw new Error('No platform LLM provider configured');
  return chatCompletionTextWithCredentials({
    base: OPENROUTER_DEFAULT_BASE,
    apiKey: or.key,
    model: opts.model || or.model,
    system: opts.system,
    messages: opts.messages,
    maxTokens: opts.maxTokens,
    responseFormat: opts.responseFormat,
    extraHeaders: openRouterRequestHeaders(),
    providerLabel: 'OpenRouter',
  });
}

/** Curated Claude chat models (Anthropic has no public list endpoint like OpenAI /models). */
export const ANTHROPIC_CHAT_MODELS = [
  'claude-sonnet-4-20250514',
  'claude-3-5-sonnet-20241022',
  'claude-3-5-haiku-20241022',
  'claude-3-opus-20240229',
  'claude-3-sonnet-20240229',
  'claude-3-haiku-20240307',
] as const;

async function anthropicCompletionText(opts: {
  apiKey: string;
  baseUrl?: string | null;
  model: string;
  system: string;
  messages: Array<{ role: 'user' | 'assistant'; content: string }>;
  maxTokens?: number;
}): Promise<string> {
  const base = (opts.baseUrl?.trim() || ANTHROPIC_DEFAULT_BASE).replace(/\/$/, '');
  const url = `${base}/v1/messages`;
  const body = buildAnthropicRequestBody({
    model: opts.model,
    system: opts.system,
    messages: opts.messages,
    maxTokens: opts.maxTokens ?? 1024,
  });

  const res = await fetch(url, {
    method: 'POST',
    headers: {
      'x-api-key': opts.apiKey,
      'anthropic-version': ANTHROPIC_VERSION,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(body),
  });
  const raw = await res.text();
  if (!res.ok) throw new Error(`Anthropic error ${res.status}: ${raw.slice(0, 500)}`);

  const data = JSON.parse(raw) as {
    content?: Array<{ type?: string; text?: string }>;
  };
  const text = data.content?.find(c => c.type === 'text')?.text?.trim();
  if (!text) throw new Error('Anthropic returned empty content');
  return text;
}

export async function listModelsForPreview(opts: {
  provider: AiProvider;
  apiKey: string;
  baseUrl?: string | null;
}): Promise<string[]> {
  const key = opts.apiKey.trim();
  if (!key) throw Object.assign(new Error('api_key is required'), { statusCode: 400 });

  if (opts.provider === 'anthropic') {
    return [...ANTHROPIC_CHAT_MODELS];
  }

  if (opts.provider === 'openrouter') {
    return listOpenRouterModels(key);
  }

  const base =
    opts.provider === 'openai'
      ? OPENAI_DEFAULT_BASE
      : (opts.baseUrl?.trim() || '').replace(/\/$/, '');
  if (opts.provider === 'openai_compatible' && !base) {
    throw Object.assign(new Error('base_url is required for openai_compatible'), { statusCode: 400 });
  }
  return listOpenAiModelsWithCredentials(base, key);
}

function toAnthropicTextMessages(
  messages: OpenAiChatMessage[],
): Array<{ role: 'user' | 'assistant'; content: string }> {
  return messages.map(m => ({
    role: m.role,
    content: typeof m.content === 'string'
      ? m.content
      : m.content
          .map(p => (p.type === 'text' ? p.text : '[image]'))
          .filter(Boolean)
          .join('\n') || '[image]',
  }));
}

export async function completeChat(opts: {
  provider: AiProvider;
  apiKey: string;
  baseUrl?: string | null;
  model: string;
  system: string;
  messages: OpenAiChatMessage[];
  maxTokens?: number;
}): Promise<string> {
  const key = opts.apiKey.trim();
  if (!key) throw new Error('Missing API key');

  if (opts.provider === 'anthropic') {
    // Anthropic vision parts not wired yet — flatten to text placeholders
    return anthropicCompletionText({
      apiKey: key,
      baseUrl: opts.baseUrl,
      model: opts.model,
      system: opts.system,
      messages: toAnthropicTextMessages(opts.messages),
      maxTokens: opts.maxTokens,
    });
  }

  const base =
    opts.provider === 'openai'
      ? OPENAI_DEFAULT_BASE
      : opts.provider === 'openrouter'
        ? OPENROUTER_DEFAULT_BASE
        : (opts.baseUrl?.trim() || '').replace(/\/$/, '');
  if (opts.provider === 'openai_compatible' && !base) {
    throw new Error('base_url is required for openai_compatible');
  }

  const isOpenRouter = opts.provider === 'openrouter';
  return chatCompletionTextWithCredentials({
    base,
    apiKey: key,
    model: opts.model,
    system: opts.system,
    messages: opts.messages,
    maxTokens: opts.maxTokens,
    extraHeaders: isOpenRouter ? openRouterRequestHeaders() : undefined,
    providerLabel: isOpenRouter ? 'OpenRouter' : undefined,
  });
}
