import { Agent, fetch as undiciFetch } from 'undici';
import { jsonrepair } from 'jsonrepair';
import {
  decryptSecret,
  normalizeApiUrl,
  resolveChatCompletionsUrl,
} from './llm-providers.mjs';
import { normalizeCoverMetaSuggestion, parseMindspaceCoverMeta } from './mindspace-cover-meta.mjs';

const insecureDispatcher = new Agent({
  connect: { rejectUnauthorized: false },
});

function resolveEncryptionKey(explicitKey) {
  const raw =
    explicitKey ??
    process.env.H5_SETTINGS_ENCRYPTION_KEY ??
    process.env.TKMIND_SERVER__SECRET_KEY ??
    'local-dev-secret';
  return raw;
}

function parseJsonObject(text) {
  const source = String(text ?? '').trim();
  if (!source) return null;
  try {
    return JSON.parse(source);
  } catch {
    try {
      return JSON.parse(jsonrepair(source));
    } catch {
      return null;
    }
  }
}

function extractJsonObject(text) {
  const direct = parseJsonObject(text);
  if (direct && typeof direct === 'object') return direct;
  const source = String(text ?? '').trim();
  const start = source.indexOf('{');
  const end = source.lastIndexOf('}');
  if (start < 0 || end <= start) {
    throw Object.assign(new Error('AI 未返回有效 JSON'), { code: 'cover_ai_invalid_output' });
  }
  const raw = source.slice(start, end + 1);
  try {
    return JSON.parse(jsonrepair(raw));
  } catch (error) {
    throw Object.assign(new Error('AI 封面 JSON 解析失败'), {
      code: 'cover_ai_invalid_output',
      cause: error instanceof Error ? error.message : String(error),
    });
  }
}

function buildCoverPrompt({ title, summary, html, instruction, currentCover }) {
  const excerpt = String(html ?? '')
    .replace(/<script[\s\S]*?<\/script>/gi, ' ')
    .replace(/<style[\s\S]*?<\/style>/gi, ' ')
    .replace(/<[^>]+>/g, ' ')
    .replace(/\s+/g, ' ')
    .trim()
    .slice(0, 1200);

  return [
    '你是 MindSpace 信息流封面设计助手。请根据页面内容生成 mindspace-cover 元数据。',
    '只返回一个 JSON 对象，不要 Markdown，不要解释。',
    '字段要求：',
    '- tag: 2-4 个汉字分类，如 旅行/美食/活动/报告',
    '- emoji: 1 个与主题高度相关的 emoji',
    '- accent: 主色 hex，如 #eeb04e',
    '- accent2: 辅色 hex',
    '- subtitle: 12-28 字副标题，用于封面底部',
    '- cover: 可选，若页面已有 hero 图路径可保留，否则留空字符串',
    '',
    `页面标题：${title || '未命名页面'}`,
    `页面摘要：${summary || '无'}`,
    `当前 cover 元数据：${JSON.stringify(currentCover ?? {})}`,
    `页面正文摘录：${excerpt || '无'}`,
    instruction ? `用户补充要求：${instruction}` : '',
  ]
    .filter(Boolean)
    .join('\n');
}

export async function suggestCoverMetaWithAi(
  pool,
  { title, summary, html, instruction, encryptionKey },
) {
  const [rows] = await pool.query(
    `SELECT * FROM h5_llm_provider_keys WHERE is_selected = 1 AND status = 'active' LIMIT 1`,
  );
  const row = rows[0];
  if (!row) {
    throw Object.assign(new Error('请先在管理后台配置并启用 LLM'), { code: 'llm_not_configured' });
  }

  let apiKey;
  try {
    apiKey = decryptSecret(
      {
        ciphertext: row.api_key_ciphertext,
        iv: row.api_key_iv,
        tag: row.api_key_tag,
      },
      resolveEncryptionKey(encryptionKey),
    );
  } catch {
    throw Object.assign(new Error('LLM 密钥解密失败，请重新配置'), { code: 'llm_not_configured' });
  }
  const apiUrl = normalizeApiUrl(row.api_url);
  const url = resolveChatCompletionsUrl(apiUrl);
  if (!url) {
    throw Object.assign(new Error('LLM API 地址无效'), { code: 'llm_not_configured' });
  }

  const prompt = buildCoverPrompt({
    title,
    summary,
    html,
    instruction,
    currentCover: parseMindspaceCoverMeta(html),
  });

  let upstream;
  try {
    upstream = await undiciFetch(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${apiKey}`,
      },
      body: JSON.stringify({
        model: row.default_model,
        messages: [{ role: 'user', content: prompt }],
        stream: false,
        ...(row.relay_provider ? { provider: row.relay_provider } : {}),
      }),
      dispatcher: url.startsWith('https://') ? insecureDispatcher : undefined,
    });
  } catch (error) {
    throw Object.assign(
      new Error(`AI 封面生成失败：${error instanceof Error ? error.message : '网络错误'}`),
      { code: 'cover_ai_failed' },
    );
  }

  const text = await upstream.text().catch(() => '');
  if (!upstream.ok) {
    throw Object.assign(new Error(`AI 封面生成失败：${text.slice(0, 240) || upstream.status}`), {
      code: 'cover_ai_failed',
    });
  }

  let data;
  try {
    data = JSON.parse(text);
  } catch {
    throw Object.assign(new Error('AI 响应不是 JSON'), { code: 'cover_ai_failed' });
  }

  const reply =
    data?.choices?.[0]?.message?.content ??
    data?.message?.content ??
    data?.output ??
    text;
  const parsed = normalizeCoverMetaSuggestion(extractJsonObject(reply));
  if (!parsed.tag && !parsed.emoji && !parsed.accent) {
    throw Object.assign(new Error('AI 未生成有效封面参数'), { code: 'cover_ai_invalid_output' });
  }
  return parsed;
}
