import { AppError } from './errors.js';

/**
 * Shared helpers for AI content generation (OpenAI-compatible chat API)
 * and HTML formatting of the generated text.
 */

export async function generateWithModel(
  baseUrl: string,
  model: string,
  apiKey: string,
  prompt: string,
): Promise<string> {
  const response = await fetch(`${baseUrl.replace(/\/$/, '')}/chat/completions`, {
    method: 'POST',
    headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json' },
    body: JSON.stringify({ model, messages: [{ role: 'user', content: prompt }], temperature: 0.4 }),
    signal: AbortSignal.timeout(30_000),
  });
  const payload = (await response.json().catch(() => null)) as {
    choices?: Array<{ message?: { content?: unknown } }>;
    error?: { message?: string };
  } | null;
  if (!response.ok) {
    throw new AppError(502, 'AI_PROVIDER_ERROR', payload?.error?.message ?? `El proveedor IA respondió ${response.status}`);
  }
  const content = payload?.choices?.[0]?.message?.content;
  if (typeof content !== 'string' || !content.trim()) {
    throw new AppError(502, 'AI_EMPTY_RESPONSE', 'El modelo IA no devolvió contenido');
  }
  return content.trim();
}

function escapeHtml(text: string): string {
  return text
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;');
}

/**
 * Converts plain text (as returned by most AI models) into clean HTML so the
 * storefront renders paragraphs and lists nicely instead of a wall of text.
 * If the input already contains HTML block tags it is returned sanitized.
 */
export function aiTextToHtml(raw: string): string {
  const text = raw.trim();
  if (!text) return '';

  // Already HTML: strip dangerous parts and return as-is.
  if (/<\s*(p|ul|ol|li|h[1-6]|div|br|strong|em|b|i|a|table|blockquote)\b/i.test(text)) {
    return text
      .replace(/<\s*(script|style|iframe|object|embed|form)\b[\s\S]*?<\s*\/\s*\1\s*>/gi, '')
      .replace(/<\s*(script|style|iframe|object|embed|form|link|meta)\b[^>]*\/?>/gi, '')
      .replace(/\son\w+\s*=\s*("[^"]*"|'[^']*'|[^\s>]+)/gi, '')
      .replace(/javascript\s*:/gi, '');
  }

  // Strip markdown fences the model sometimes wraps output in.
  const cleaned = text.replace(/^```[a-z]*\s*/i, '').replace(/\s*```$/, '').trim();

  const blocks: string[] = [];
  let listItems: string[] = [];

  const flushList = () => {
    if (listItems.length) {
      blocks.push(`<ul>${listItems.map((li) => `<li>${li}</li>`).join('')}</ul>`);
      listItems = [];
    }
  };

  for (const paragraph of cleaned.split(/\n{2,}/)) {
    const lines = paragraph.split('\n').map((l) => l.trim()).filter(Boolean);
    if (!lines.length) continue;
    if (lines.every((l) => /^[-*•·]\s+/.test(l))) {
      for (const line of lines) listItems.push(escapeHtml(line.replace(/^[-*•·]\s+/, '')));
      continue;
    }
    flushList();
    const inline = lines.map((line) => escapeHtml(line.replace(/^[-*•·]\s+/, ''))).join('<br/>');
    // Render **bold** markdown the model often emits in plain text.
    blocks.push(`<p>${inline.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')}</p>`);
  }
  flushList();
  return blocks.join('');
}

/** Loads the shared AI settings (base url, model, api key) from store_settings. */
export async function loadAiSettings(
  pool: { query: <T>(sql: string, values?: unknown[]) => Promise<{ rows: T[] }> },
  extraKeys: string[] = [],
): Promise<Record<string, string>> {
  const keys = ['ai_base_url', 'ai_model', 'ai_api_key', ...extraKeys];
  const result = await pool.query<{ key: string; value: string }>(
    `SELECT key, value FROM store_settings WHERE key = ANY($1::text[])`,
    [keys],
  );
  return Object.fromEntries(result.rows.map((row) => [row.key, row.value]));
}

export function requireAiConfigured(settings: Record<string, string>): {
  baseUrl: string;
  model: string;
  apiKey: string;
} {
  const baseUrl = settings.ai_base_url?.trim();
  const model = settings.ai_model?.trim();
  const apiKey = settings.ai_api_key?.trim();
  if (!baseUrl || !model || !apiKey) {
    throw new AppError(422, 'AI_NOT_CONFIGURED', 'Configura proveedor, URL base, modelo y API key en Ajustes → IA para SEO');
  }
  return { baseUrl, model, apiKey };
}
