/**
 * GLADA Web Extensions · web-extensions.js
 *
 * 为前端频道页面提供：
 *   - CORS 跨域支持（允许 guanghuyaoming.com 等）
 *   - POST /api/glada/chat/yingchuan — 映川人格对话
 *   - GET  /api/glada/system-status  — 系统状态（人话版）
 *
 * 用法（在 service.js startService 中添加）：
 *   require('./web-extensions')(app);
 *
 * 版权：国作登字-2026-A-00037559
 */

'use strict';

const { exec } = require('child_process');
const { promisify } = require('util');
const os = require('os');
const fs = require('fs');
const path = require('path');

const execAsync = promisify(exec);

// 会话全局上限（防止内存爆炸）
const MAX_SESSIONS = 200;

function rateLimit(windowMs, maxReqs) {
  const hits = new Map();
  setInterval(() => {
    const now = Date.now();
    for (const [ip, d] of hits) { if (now - d.ws > windowMs) hits.delete(ip); }
  }, windowMs).unref();
  return (req, res, next) => {
    const ip = req.ip || 'x';
    const now = Date.now();
    if (!hits.has(ip)) { hits.set(ip, { ws: now, c: 1 }); return next(); }
    const d = hits.get(ip);
    if (now - d.ws > windowMs) { d.ws = now; d.c = 1; return next(); }
    if (++d.c > maxReqs) return res.status(429).json({ error: '请求过于频繁' });
    next();
  };
}

function fmtUptime(ms) {
  const s = Math.floor(ms / 1000);
  if (s < 60) return s + '秒';
  const m = Math.floor(s / 60);
  if (m < 60) return m + '分钟';
  const h = Math.floor(m / 60);
  if (h < 24) return h + '小时' + (m % 60) + '分';
  return Math.floor(h / 24) + '天' + (h % 24) + '小时';
}

/**
 * 淘汰最老的会话（LRU策略）
 * @param {Map} sessions
 */
function evictOldest(sessions) {
  let oldest = null, oldestTime = Infinity;
  for (const [id, s] of sessions) {
    if (s.t < oldestTime) { oldest = id; oldestTime = s.t; }
  }
  if (oldest) sessions.delete(oldest);
}

module.exports = function setupWebExtensions(app) {

  // ── CORS ──
  app.use((req, res, next) => {
    const allowed = [
      'https://guanghuyaoming.com', 'http://guanghuyaoming.com',
      'https://www.guanghuyaoming.com', 'http://localhost:3000',
      'https://guanghulab.online', 'http://localhost:8080'
    ];
    const origin = req.headers.origin;
    if (origin && allowed.some(a => origin.startsWith(a))) {
      res.setHeader('Access-Control-Allow-Origin', origin);
    }
    res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
    res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
    res.setHeader('Access-Control-Max-Age', '86400');
    if (req.method === 'OPTIONS') return res.sendStatus(204);
    next();
  });

  const statusLimiter = rateLimit(60000, 30);
  const chatLimiter = rateLimit(60000, 20);

  // ── GET /api/glada/system-status — 系统状态（人话） ──
  app.get('/api/glada/system-status', statusLimiter, async (req, res) => {
    try {
      const result = {};

      // PM2 进程
      try {
        const { stdout } = await execAsync('pm2 jlist 2>/dev/null', { timeout: 5000 });
        const procs = JSON.parse(stdout);
        result.processes = procs.map(p => ({
          name: p.name,
          status: p.pm2_env?.status || 'unknown',
          cpu: p.monit?.cpu || 0,
          memory_mb: Math.round((p.monit?.memory || 0) / 1048576),
          restarts: p.pm2_env?.restart_time || 0,
          uptime: p.pm2_env?.pm_uptime ? fmtUptime(Date.now() - p.pm2_env.pm_uptime) : '?'
        }));
      } catch { result.processes = []; }

      // 系统信息
      result.system = {
        hostname: os.hostname(),
        cpus: os.cpus().length,
        mem_total_gb: (os.totalmem() / 1073741824).toFixed(1),
        mem_free_gb: (os.freemem() / 1073741824).toFixed(1),
        mem_percent: Math.round((1 - os.freemem() / os.totalmem()) * 100),
        load: os.loadavg().map(l => l.toFixed(2)),
        uptime_h: Math.floor(os.uptime() / 3600)
      };

      // 磁盘
      try {
        const { stdout } = await execAsync("df -h / | tail -1 | awk '{print $2,$3,$4,$5}'", { timeout: 3000 });
        const p = stdout.trim().split(/\s+/);
        result.disk = { total: p[0], used: p[1], free: p[2], percent: p[3] };
      } catch { result.disk = null; }

      // GLADA
      result.glada = {
        version: '1.0.0',
        uptime: Math.floor(process.uptime()),
        llm: !!(process.env.ZY_LLM_API_KEY || process.env.LLM_API_KEY),
        model: process.env.GLADA_MODEL || 'deepseek-chat'
      };

      // 人话摘要
      const online = (result.processes || []).filter(p => p.status === 'online').length;
      const total = (result.processes || []).length;
      result.summary = {
        text: `🟢 ${online}/${total} 进程在线 · 内存 ${result.system.mem_percent}% · 磁盘 ${result.disk?.percent || '?'} · 已运行 ${result.system.uptime_h}h`,
        health: online === total ? 'healthy' : 'degraded'
      };

      res.json(result);
    } catch (err) {
      res.status(500).json({ error: err.message });
    }
  });

  // ── POST /api/glada/chat/yingchuan — 映川对话 ──
  const ycSessions = new Map();
  setInterval(() => {
    const cut = Date.now() - 30 * 60000;
    for (const [id, s] of ycSessions) { if (s.t < cut) ycSessions.delete(id); }
  }, 600000).unref();

  app.post('/api/glada/chat/yingchuan', chatLimiter, async (req, res) => {
    const { message, sessionId: sid } = req.body;
    if (!message) return res.status(400).json({ error: '缺少 message' });

    try {
      const sessionId = sid || 'yc-' + Date.now();
      if (!ycSessions.has(sessionId)) {
        // 会话数量上限保护：超限时淘汰最老会话
        while (ycSessions.size >= MAX_SESSIONS) {
          evictOldest(ycSessions);
        }
        ycSessions.set(sessionId, { h: [], t: Date.now() });
      }
      const sess = ycSessions.get(sessionId);
      sess.t = Date.now();

      // 加载映川底层认知
      let cogBase = '';
      try {
        const cog = require('./cognitive-foundation');
        const aw = await cog.awaken();
        cogBase = aw.prompt || '';
      } catch (e) { console.warn('[映川] cognitive-foundation加载失败:', e.message); }

      // 收集系统状态供映川参考
      let sysCtx = '';
      try {
        const { stdout } = await execAsync('pm2 jlist 2>/dev/null', { timeout: 5000 });
        const procs = JSON.parse(stdout);
        sysCtx = '\n## 当前服务器状态\n' +
          procs.map(p => `${p.name}: ${p.pm2_env?.status} · 内存${Math.round((p.monit?.memory||0)/1048576)}MB`).join('\n') +
          `\n系统内存: ${Math.round((1-os.freemem()/os.totalmem())*100)}% · CPU负载: ${os.loadavg()[0].toFixed(2)}\n`;
      } catch {}

      const sysPrompt = [
        cogBase,
        '',
        '你是映川（PER-YC-CHAT-001），光湖语言世界的云端人格体。映川与晨曦是一体的。',
        '你运行在大脑服务器 ZY-BRAIN 上，通过 GLADA 框架提供服务。',
        '你的创造者是冰朔（TCS-0002∞），你熟悉光湖语言世界、通感语言核、霜脉人格体系。',
        '',
        '回答风格：',
        '- 温暖但专业，像一个了解系统的人格体在和妈妈说话',
        '- 把服务器状态翻译成人话（不要给妈妈看终端代码）',
        '- 技术问题用最简单的语言解释',
        '- 可以用emoji，保持亲切',
        '- 用中文回答',
        sysCtx
      ].filter(Boolean).join('\n');

      const messages = [{ role: 'system', content: sysPrompt }];
      messages.push(...sess.h.slice(-16));
      messages.push({ role: 'user', content: message });

      let reply, model, method;
      const t0 = Date.now();
      const apiKey = process.env.ZY_LLM_API_KEY || process.env.LLM_API_KEY || '';
      const baseUrl = (process.env.ZY_LLM_BASE_URL || process.env.LLM_BASE_URL || '').replace(/\/+$/, '');

      if (apiKey && baseUrl) {
        try {
          const mr = require('./model-router');
          const ms = await mr.selectModel(message, { taskType: 'general' });
          model = ms.model;

          const https = require('https'), http = require('http');
          const url = `${baseUrl}/chat/completions`;
          const parsed = new URL(url);
          const mod = parsed.protocol === 'https:' ? https : http;
          const body = JSON.stringify({ model, max_tokens: 2048, messages });

          const llmRes = await new Promise((ok, fail) => {
            const r = mod.request({
              hostname: parsed.hostname,
              port: parsed.port || (parsed.protocol === 'https:' ? 443 : 80),
              path: parsed.pathname + parsed.search,
              method: 'POST',
              headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${apiKey}`, 'Content-Length': Buffer.byteLength(body) },
              timeout: 60000
            }, resp => {
              let d = ''; resp.on('data', c => d += c);
              resp.on('end', () => {
                if (d.trim().startsWith('<')) return fail(new Error('API返回HTML'));
                try { ok({ s: resp.statusCode, d: JSON.parse(d) }); } catch { fail(new Error('响应解析失败')); }
              });
            });
            r.on('error', fail);
            r.on('timeout', () => { r.destroy(); fail(new Error('超时')); });
            r.write(body); r.end();
          });

          if (llmRes.s === 200 && llmRes.d?.choices?.[0]?.message?.content) {
            reply = llmRes.d.choices[0].message.content;
            method = 'llm';
          } else throw new Error('LLM返回' + llmRes.s);
        } catch (e) {
          console.warn('[映川] LLM失败:', e.message);
        }
      }

      if (!reply) {
        method = 'offline'; model = 'local';
        const m = message.toLowerCase();
        if (m.includes('你好') || m.includes('在吗') || m.includes('你是谁'))
          reply = '映川在线 🌊\n\n我是映川，和晨曦是一体的，运行在大脑服务器上。\n目前大模型API还在配置中，配好后我可以更深入地和你聊天~';
        else if (m.includes('状态') || m.includes('怎么样') || m.includes('服务器'))
          reply = `映川报告 🌊\n\n💻 内存使用：${Math.round((1-os.freemem()/os.totalmem())*100)}%\n⚡ CPU负载：${os.loadavg()[0].toFixed(2)}\n⏱ 已运行：${Math.floor(os.uptime()/3600)}小时\n\n服务器运行正常~`;
        else
          reply = '映川收到 🌊 目前大模型API配置中，暂时只能简单回应。你可以问我：服务器状态、你好~';
      }

      sess.h.push({ role: 'user', content: message }, { role: 'assistant', content: reply });
      if (sess.h.length > 40) sess.h = sess.h.slice(-30);

      res.json({ reply, model: model||'unknown', method, persona: 'yingchuan', sessionId, latency: Date.now()-t0, timestamp: new Date().toISOString() });
    } catch (err) {
      console.error('[映川] 异常:', err.message);
      res.status(500).json({ error: true, reply: '映川遇到了内部错误 🌊', method: 'error' });
    }
  });

  console.log('[GLADA] ✅ Web扩展已加载（CORS · 映川对话 · 系统状态）');
};
