/**
 * persona-studio · API Key 模型检测路由
 * POST /api/ps/apikey/detect-models   检测可用模型
 * POST /api/ps/apikey/chat            通过用户 API Key 对话
 */
const express = require('express');
const router = express.Router();
const crypto = require('crypto');
const https = require('https');
const http = require('http');

/* ---- 模型列表缓存（1 小时有效期） ---- */
const modelCache = new Map();
const CACHE_TTL_MS = 60 * 60 * 1000; // 1 小时

function getCacheKey(apiBase, apiKey) {
  const hash = crypto.createHash('sha256').update(apiKey).digest('hex').slice(0, 16);
  return apiBase + '::' + hash;
}

function getCachedModels(apiBase, apiKey) {
  const key = getCacheKey(apiBase, apiKey);
  const entry = modelCache.get(key);
  if (entry && Date.now() - entry.timestamp < CACHE_TTL_MS) {
    return entry.models;
  }
  return null;
}

function setCachedModels(apiBase, apiKey, models) {
  const key = getCacheKey(apiBase, apiKey);
  modelCache.set(key, { models, timestamp: Date.now() });
}

/**
 * 请求第三方 API 的 /v1/models 接口
 */
function fetchModels(apiBase, apiKey, timeoutMs) {
  return new Promise((resolve, reject) => {
    // 规范化 apiBase：去除末尾斜杠
    const base = apiBase.replace(/\/+$/, '');
    // 直接拼接 /models，因为 apiBase 已经由前端规范化
    const modelsPath = base + '/models';
    const url = new URL(modelsPath);
    const isHttps = url.protocol === 'https:';
    const mod = isHttps ? https : http;

    const options = {
      hostname: url.hostname,
      port: url.port || (isHttps ? 443 : 80),
      path: url.pathname + (url.search || ''),
      method: 'GET',
      headers: {
        'Authorization': 'Bearer ' + apiKey,
        'Accept': 'application/json'
      },
      timeout: timeoutMs || 15000
    };

    const req = mod.request(options, (res) => {
      let data = '';
      res.on('data', (chunk) => { data += chunk; });
      res.on('end', () => {
        if (res.statusCode === 401 || res.statusCode === 403) {
          return reject(new Error('API Key 无效'));
        }
        if (res.statusCode >= 400) {
          return reject(new Error('API Base 不可访问 (HTTP ' + res.statusCode + ')'));
        }
        try {
          const json = JSON.parse(data);
          const models = json.data || json.models || [];
          const modelIds = models
            .map(function (m) { return m.id || m.name || null; })
            .filter(Boolean);
          resolve(modelIds);
        } catch (_e) {
          reject(new Error('未检测到可用模型'));
        }
      });
    });

    req.on('error', (err) => {
      const wrapped = new Error('API Base 不可访问: ' + err.message);
      wrapped.code = err.code;
      reject(wrapped);
    });

    req.on('timeout', () => {
      req.destroy();
      const err = new Error('API Base 不可访问（请求超时）');
      err.code = 'ETIMEDOUT';
      reject(err);
    });

    req.end();
  });
}

/**
 * 调用用户 API 的 chat/completions 接口
 */
function callUserApi({ apiBase, apiKey, model, messages, maxTokens, temperature, timeoutMs }) {
  return new Promise((resolve, reject) => {
    const base = apiBase.replace(/\/+$/, '');
    // 直接拼接 /chat/completions，因为 apiBase 已经由前端规范化
    // 包含完整版本路径（如 /v1, /v1beta/openai, /v4 等）
    const chatPath = base + '/chat/completions';
    const url = new URL(chatPath);
    const isHttps = url.protocol === 'https:';
    const mod = isHttps ? https : http;

    const body = JSON.stringify({
      model: model,
      messages: messages,
      max_tokens: maxTokens || 2000,
      temperature: temperature != null ? temperature : 0.8
    });

    const options = {
      hostname: url.hostname,
      port: url.port || (isHttps ? 443 : 80),
      path: url.pathname,
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer ' + apiKey,
        'Content-Length': Buffer.byteLength(body)
      },
      timeout: timeoutMs || 60000
    };

    const req = mod.request(options, (res) => {
      let data = '';
      res.on('data', (chunk) => { data += chunk; });
      res.on('end', () => {
        try {
          const json = JSON.parse(data);
          if (json.choices && json.choices[0] && json.choices[0].message) {
            resolve(json.choices[0].message.content);
          } else if (json.error) {
            reject(new Error(json.error.message || 'API 调用失败'));
          } else {
            reject(new Error('API 返回格式异常'));
          }
        } catch (_e) {
          reject(new Error('API 返回解析失败'));
        }
      });
    });

    req.on('error', (err) => {
      reject(new Error('API 请求失败: ' + err.message));
    });

    req.on('timeout', () => {
      req.destroy();
      reject(new Error('API 请求超时'));
    });

    req.write(body);
    req.end();
  });
}

// POST /api/ps/apikey/detect-models
router.post('/detect-models', async (req, res) => {
  const { api_base, api_key } = req.body || {};

  if (!api_base || typeof api_base !== 'string') {
    return res.status(400).json({
      error: true,
      code: 'MISSING_API_BASE',
      message: '请输入 API Base URL'
    });
  }

  // 校验 URL 格式
  try {
    const testBase = api_base.replace(/\/+$/, '');
    new URL(testBase + '/models');
  } catch (_e) {
    return res.status(400).json({
      error: true,
      code: 'INVALID_API_BASE',
      message: 'API Base URL 格式无效'
    });
  }

  if (!api_key || typeof api_key !== 'string') {
    return res.status(400).json({
      error: true,
      code: 'MISSING_API_KEY',
      message: '请输入 API Key'
    });
  }

  // 防止 header injection：API Key 不得包含换行符
  if (/[\r\n]/.test(api_key)) {
    return res.status(400).json({
      error: true,
      code: 'INVALID_API_KEY',
      message: 'API Key 格式无效'
    });
  }
  const cached = getCachedModels(api_base, api_key);
  if (cached) {
    return res.json({
      error: false,
      models: cached,
      count: cached.length,
      cached: true
    });
  }

  try {
    const models = await fetchModels(api_base, api_key, 15000);

    if (!models || models.length === 0) {
      return res.status(404).json({
        error: true,
        code: 'NO_MODELS',
        message: '未检测到可用模型'
      });
    }

    // 写入缓存
    setCachedModels(api_base, api_key, models);

    res.json({
      error: false,
      models: models,
      count: models.length,
      cached: false
    });
  } catch (err) {
    const errMsg = err.message || '模型检测失败';
    const errCode = err.code || '';
    let code = 'DETECT_FAILED';

    // 区分 DNS / 网络 / 超时错误（优先使用 Node.js 错误码）
    if (errCode === 'ENOTFOUND' || errCode === 'EAI_AGAIN' || /ENOTFOUND|getaddrinfo/.test(errMsg)) {
      code = 'DNS_ERROR';
    } else if (errCode === 'ECONNREFUSED' || errCode === 'ECONNRESET' || errCode === 'EHOSTUNREACH' || errCode === 'ENETUNREACH' || /ECONNREFUSED|ECONNRESET|EHOSTUNREACH|ENETUNREACH|socket hang up/.test(errMsg)) {
      code = 'NETWORK_ERROR';
    } else if (errCode === 'ETIMEDOUT' || errCode === 'ESOCKETTIMEDOUT' || /timeout|ETIMEDOUT/.test(errMsg)) {
      code = 'TIMEOUT';
    }

    res.status(502).json({
      error: true,
      code: code,
      message: errMsg
    });
  }
});

// POST /api/ps/apikey/chat
router.post('/chat', async (req, res) => {
  const { api_base, api_key, model, messages } = req.body || {};

  if (!api_base || !api_key || !model) {
    return res.status(400).json({
      error: true,
      code: 'MISSING_PARAMS',
      message: '缺少必要参数 (api_base, api_key, model)'
    });
  }

  // 防止 header injection
  if (/[\r\n]/.test(api_key)) {
    return res.status(400).json({
      error: true,
      code: 'INVALID_API_KEY',
      message: 'API Key 格式无效'
    });
  }

  if (!messages || !Array.isArray(messages) || messages.length === 0) {
    return res.status(400).json({
      error: true,
      code: 'MISSING_MESSAGES',
      message: '缺少消息内容'
    });
  }

  try {
    const reply = await callUserApi({
      apiBase: api_base,
      apiKey: api_key,
      model: model,
      messages: messages,
      maxTokens: 2000,
      temperature: 0.8,
      timeoutMs: 60000
    });

    res.json({
      error: false,
      reply: reply,
      model: model
    });
  } catch (err) {
    res.status(502).json({
      error: true,
      code: 'CHAT_FAILED',
      message: err.message || '对话请求失败'
    });
  }
});

module.exports = router;
