const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const path = require('path');
const rateLimit = require('express-rate-limit');
const cookieParser = require('cookie-parser');
require('dotenv').config();
const config = require('../config');
const { initDatabase } = require('./init-db');
// ========== 全局异常捕获 ==========
// 记录但保持进程存活，PM2会自动重启
process.on('uncaughtException', (err) => {
  console.error('[崩溃] 未捕获异常:', err.message);
  console.error(err.stack);
  // 通知PM2重启（优雅退出）
  setTimeout(() => process.exit(0), 1000).unref();
});
process.on('unhandledRejection', (reason, promise) => {
  console.error('[崩溃] 未处理的Promise拒绝:', reason?.message || reason);
  // 通知PM2重启
  setTimeout(() => process.exit(0), 1000).unref();
});
// 初始化数据库
initDatabase();
// ========== 授权系统初始化 ==========
const license = require('./lib/license');
try {
  license.initLicenseConfig();
  license.initFileHashes();
  console.log('[授权] 授权系统初始化完成，当前版本:', license.getLicenseType() === 'free' ? '免费版' : '完整版');
  
  // 启动时检测完整性
  const tamperReport = license.checkTamper();
  const tampered = tamperReport.filter(r => r.status === 'tampered');
  if (tampered.length > 0) {
    console.warn('⚠️ [授权] 检测到核心文件被篡改:', tampered.map(r => r.file).join(', '));
    try {
      const { run } = require('./utils/db');
      run("UPDATE system_config SET value='tampered' WHERE key='integrity_check'");
      // 发送破解告警邮件
      const email = require('./lib/notification/email');
      email.sendAlert({
        type: '文件完整性校验失败',
        detail: `检测到 ${tampered.length} 个核心文件被篡改: ${tampered.map(r => r.file).join(', ')}`,
        serverInfo: {
          hostname: require('os').hostname(),
          systemId: license.getSystemId(),
          platform: process.platform,
          node: process.version,
        }
      }).catch(() => {});
    } catch(e) {}
  } else {
    try {
      const { run } = require('./utils/db');
      run("UPDATE system_config SET value='pass' WHERE key='integrity_check'");
    } catch(e) {}
  }

  // ===== 定时自检：每小时检查一次完整性 =====
  setInterval(() => {
    try {
      const report = license.checkTamper();
      const tamperedFiles = report.filter(r => r.status === 'tampered');
      if (tamperedFiles.length > 0) {
        console.warn('⚠️ [授权] 定时检测发现文件被篡改:', tamperedFiles.map(r => r.file).join(', '));
        const email = require('./lib/notification/email');
        email.sendAlert({
          type: '定时检查-文件篡改',
          detail: `检测到 ${tamperedFiles.length} 个文件被篡改: ${tamperedFiles.map(r => r.file).join(', ')}\n预期hash: ${tamperedFiles[0]?.expected}\n当前hash: ${tamperedFiles[0]?.actual}`,
          serverInfo: {
            hostname: require('os').hostname(),
            systemId: license.getSystemId(),
            platform: process.platform,
            node: process.version,
          }
        }).catch(() => {});
      }
    } catch(e) {}
  }, 60 * 60 * 1000); // 每小时检查一次
} catch (e) {
  console.error('[授权] 授权系统初始化失败:', e.message);
}
const app = express();
// 中间件
app.use(helmet({ contentSecurityPolicy: false }));
app.use(cors({ credentials: true }));
app.use(cookieParser());
// 保留原始body中间件（必须在body parsers之前注册，用于微信支付XML回调）
app.use((req, res, next) => {
  if (req.headers['content-type']?.includes('xml')) {
    let data = '';
    req.on('data', chunk => data += chunk);
    req.on('end', () => {
      req.rawBody = data;
      next();
    });
  } else {
    next();
  }
});
app.use(express.json({ limit: "50mb" }));
app.use(express.urlencoded({ limit: "50mb", extended: true, verify: (req, res, buf) => {
  // 保留原始body用于微信支付XML回调验签（兼容旧的verify机制）
  if (buf && buf.length && req.headers['content-type']?.includes('xml')) {
    req.rawBody = buf.toString('utf8');
  }
} }));
// 初始化支付日志表
const payLogger = require('./lib/payment/logger');
// SEO优化中间件（爬虫检测 → 服务端渲染）
// 必须在静态文件和SPA路由之前注册，优先拦截爬虫请求
const { seoMiddleware } = require('./lib/seo');
const pageCache = require('./lib/page-cache');
const downloadRoutes = require("./routes/download");
// 静态页面缓存中间件（优先于SEO中间件）
app.use((req, res, next) => {
  const ua = req.headers['user-agent'] || '';
  const isCrawler = /bot|crawl|spider|slurp|fetch|seo/i.test(ua);
  
  if (isCrawler && req.method === 'GET') {
    const cached = pageCache.get(req.url, ua);
    if (cached) {
      res.setHeader('Content-Type', 'text/html; charset=utf-8');
      res.setHeader('X-Cache', 'HIT');
      return res.send(cached);
    }
    
    // 拦截res.send缓存响应
    const originalSend = res.send.bind(res);
    res.send = (body) => {
      if (typeof body === 'string' && res.statusCode === 200) {
        pageCache.set(req.url, ua, body);
      }
      return originalSend(body);
    };
  }
  next();
});
app.use(seoMiddleware);
// ========== 版权水印中间件（在所有HTML响应中注入版权） ==========
const { copyrightMiddleware } = require('./middleware/copyright');
app.use(copyrightMiddleware);
// ========== 版权强制中间件（全局API校验，防绕过核心）==========
const { enforceCopyright, initTamperTable, injectCopyrightWatermarks } = require("./middleware/copyright-enforce");
try {
  const results = injectCopyrightWatermarks();
  const injected = results.filter(r => r.status === "injected").length;
  if (injected > 0) console.log("[版权强制] 已注入", injected, "个核心文件的版权暗码");
} catch(e) { console.error("[版权强制] 暗码注入:", e.message); }
// 静态文件
app.use('/uploads', express.static(path.join(__dirname, '..', 'uploads')));
app.use('/admin', express.static(path.join(__dirname, '..', '..', 'admin', 'dist'), { setHeaders(res, path) { if (path.endsWith('.html')) res.setHeader('Cache-Control', 'no-cache, must-revalidate'); } }));
app.use('/assets', express.static(path.join(__dirname, '..', '..', 'admin', 'dist', 'assets')));
app.use('/vite.svg', express.static(path.join(__dirname, '..', '..', 'admin', 'dist', 'vite.svg')));
app.use('/mobile', express.static(path.join(__dirname, '..', '..', 'mobile')));
// ========== 版本入口控制（免费版隐藏PC前端和小程序）==========
const { isFeatureEnabled, getLicenseType } = require('./lib/license');
app.use((req, res, next) => {
  // 免费版隐藏PC前台入口
  if (req.path.startsWith('/pc') && !isFeatureEnabled('pc_frontend')) {
    // 对爬虫返回真实内容（SEO不受影响），浏览器访问跳转
    const ua = req.headers['user-agent'] || '';
    const isCrawler = /bot|crawl|spider|slurp/i.test(ua);
    if (isCrawler) return next();
    return res.redirect('/mobile/');
  }
  // 免费版隐藏小程序API入口
  if (req.path.startsWith('/api/user/wx-login') && !isFeatureEnabled('miniprogram')) {
    return res.json({ code: 403, message: '当前版本不支持微信小程序功能' });
  }
  next();
});
app.use('/pc', express.static(path.join(__dirname, '..', '..', 'pc')));
app.use('/lib', express.static(path.join(__dirname, '..', 'public', 'lib')));
app.use(express.static(path.join(__dirname, '..', 'public')));
// 限流
const limiter = rateLimit({
  windowMs: 15 * 60 * 1000,
  max: 1000,
  message: { code: 429, message: '请求过于频繁，请稍后再试' }
});
app.use('/api', limiter);
// 安全中间件（对API路由生效）
const { sanitizeRequest } = require('./middleware/security');
app.use('/api', sanitizeRequest);
app.use("/api", enforceCopyright);
// ========== Admin 全局鉴权守卫（排除登录/验证码等公开接口） ==========
const { authAdmin } = require('./middleware/auth');
const ADMIN_PUBLIC_PATHS = ['/login', '/captcha', '/register', '/forgot-password', '/reset-password', '/check-token', '/provinces'];
app.use('/api/admin', (req, res, next) => {
  if (ADMIN_PUBLIC_PATHS.some(p => req.path === p || req.path.startsWith(p + '/'))) {
    return next();
  }
  authAdmin(req, res, next);
});
// 路由
app.use('/api/user', require('./routes/user'));
app.use('/api/product', require('./routes/product'));
app.use('/api/cart', require('./routes/cart'));
app.use('/api/order', require('./routes/order'));
// ⑥ 售后精细化（统一售后路由：仅退款/退货退款双流程）
const aftersaleRoutes = require('./routes/aftersale');
app.use('/api/aftersale', aftersaleRoutes.userRouter);
app.use('/api/admin/aftersale', aftersaleRoutes.adminRouter);
app.use('/api/admin/seo', require('./routes/admin-seo')); // 自动SEO文章生成器（管理端任务/关键词/日志）
app.use('/api/membership', require('./routes/membership'));
app.use('/api/distribution', require('./routes/distribution'));
app.use('/api/marketing', require('./routes/marketing'));
app.use('/api/track', require('./routes/track'));  // P2 行为埋点
app.use('/api/admin/funnel', require('./routes/funnel'));  // P2 转化漏斗+RFM
app.use('/api/miniapp-pay', require('./routes/miniapp-pay'));  // P1 小程序原生支付
const marketingAutomation = require('./routes/marketing-automation');
app.use('/api/user', marketingAutomation);        // 用户生日设置 /api/user/birthday
app.use('/api/admin/marketing/automation', marketingAutomation);  // 管理端规则/logs/stats
// 页面装修可视化（管理端）
// 团购/预售 E3
const { presaleRouter, bulkGroupRouter, adminPresale } = require('./routes/group-bulk');
const { bargainRouter, adminBargain } = require('./routes/bargain');
app.use('/api/presale', presaleRouter);
app.use('/api/bulk-group', bulkGroupRouter);
app.use('/api/bargain', bargainRouter);
app.use('/api/theme', require('./routes/theme'));
app.use('/api/exchange', require('./routes/exchange'));
app.use('/api/admin/exchange', require('./routes/exchange'));
// 团购/预售后台管理
app.use('/api/admin', adminPresale);
app.use('/api/admin/bargain', adminBargain);
app.use('/api/ai', require('./routes/ai'));
app.use('/api/ai-gen', require('./routes/ai-gen'));
app.use('/api/ai-product', require('./routes/ai-product'));
app.use('/api/ai-analysis', require('./routes/ai-analysis'));
app.use('/api/ai-prompt', require('./routes/ai-prompt'));
app.use('/api/upload', require('./routes/upload'));
app.use('/api/config', require('./routes/config'));
app.use('/api/coupon', require('./routes/coupon'));
app.use('/api/banner', require('./routes/banner'));
app.use('/api/admin/banner', require('./routes/admin-banner'));
app.use('/api/category', require('./routes/category'));
const { loginLimiter } = require('./middleware/security');
app.use('/api/admin', require('./routes/admin'));
const { router: collectRouter } = require('./routes/collect');
app.use('/api/collect', collectRouter);
app.use('/api/design', require('./routes/design'));
app.use('/api/admin/design', require('./routes/design'));
app.use('/api/terminal', require('./routes/terminal'));
app.use('/api/stats', require('./routes/stats'));
app.use('/api/system', require('./routes/system'));
app.use('/api/payment', require('./routes/payment'));
app.use("/api", downloadRoutes);
app.use('/api/pagebuilder', require('./routes/pagebuilder'));
app.use('/api/collector', require('./routes/collector'));
app.use('/api/captcha', require('./routes/captcha'));
app.use('/api/review', require('./routes/review'));
app.use('/api/address', require('./routes/address'));
app.use('/api/notification', require('./routes/notification'));
app.use('/api/license', require('./routes/license'));
// ========== 省胜优铺授权（199/599 + 独立授权服务器） ==========
app.use('/api/ssyp-license', require('./routes/ssyp-license'));
// 启动时同步功能解锁状态（若已激活则解锁全部功能）
const ssypAuth = require('./lib/ssyp-license');
try { ssypAuth.syncFeatureUnlock(); } catch(e) { console.error('[ssyp] 同步功能解锁失败:', e.message); }
// 启动授权在线复查（退款撤销锁定）：仅在付费态生效，断网不误伤
const licenseHeartbeat = require('./lib/license-heartbeat');
try { licenseHeartbeat.startHeartbeat(); } catch(e) { console.error('[ssyp] 启动授权复查失败:', e.message); }
app.use('/api/ai-assistant', require('./routes/ai-assistant'));
app.use('/api/ai-review', require('./routes/ai-review'));
// 微信视频号解析器（挂载到 /wechat-video 路径）
const wechatVideoRouter = require('./routes/wechat-video');
app.use('/wechat-video', wechatVideoRouter);
app.use('/api/locale', require('./routes/locale'));
app.use('/api/admin/app', require('./routes/app'));
// 官网商城 - 独立支付模块（数据隔离，不共享轻商城数据库）
app.use('/api/pay', require('./routes/pay'));
// 一键搬家 - 数据迁移
app.use('/api/admin/migration', require('./routes/migration'));
// 一键搬家 - 外部客户门户
// 🔒 门户公开的 convert/preview/detect 会解析大JSON/xlsx，加限流防未授权 DoS/资源耗尽
const migrationLimiter = rateLimit({ windowMs: 60 * 1000, max: 60, standardHeaders: true, legacyHeaders: false });
app.use('/migration', migrationLimiter, require('./routes/migration-portal'));
// 充值模块
app.use('/api/recharge', require('./routes/recharge'));
app.use('/api/admin/recharge', require('./routes/recharge'));
// APP版本检查（公开接口，无需认证）
app.get('/api/app/version/latest', (req, res) => {
  try {
    const { get } = require('./utils/db');
    const latest = get("SELECT * FROM app_versions WHERE status=1 ORDER BY version_code DESC LIMIT 1");
    res.json({ code: 0, message: '操作成功', data: latest || null });
  } catch(e) {
    res.json({ code: 1, message: '获取失败', data: null });
  }
});
// 静态页fallback
// 授权信息页（在admin SPA路由之前注册，避免被SPA catch-all捕获）
app.get('/admin/license', (req, res) => res.sendFile(path.join(__dirname, '..', '..', 'admin', 'dist', 'license.html')));
// SPA fallback
app.get('/admin', (req, res) => res.sendFile(path.join(__dirname, '..', '..', 'admin', 'dist', 'index.html')));
app.get('/admin/*', (req, res) => res.sendFile(path.join(__dirname, '..', '..', 'admin', 'dist', 'index.html')));

app.get('/mobile', (req, res) => res.sendFile(path.join(__dirname, '..', '..', 'mobile', 'index.html')));
// ====== 小程序壳H5入口（修改版，支持URL注入token/page） ======
app.use('/h5', express.static(path.join(__dirname, '..', '..', 'mobile', 'h5')));
app.get('/h5', (req, res) => res.sendFile(path.join(__dirname, '..', '..', 'mobile', 'h5', 'index.html')));
// ====== 图片代理（绕过OSS防盗链） ======
app.get('/proxy-img', async (req, res) => {
  const url = req.query.url;
  if (!url) return res.status(400).send('Missing url');
  try {
    // 🔒 防 SSRF：仅允许 http/https，且拒绝内网/保留地址/云元数据
    const parsed = new URL(url);
    if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
      return res.status(400).send('Only http/https allowed');
    }
    const host = parsed.hostname;
    const isPrivate = /^(127\.|10\.|192\.168\.|172\.(1[6-9]|2\d|3[01])\.|169\.254\.|0\.)/.test(host)
      || host === 'localhost' || host.endsWith('.local') || host === 'metadata.google.internal'
      || host === '169.254.169.254'
      || (/^[a-f0-9:]+$/i.test(host) && host.includes(':'));
    if (isPrivate) return res.status(400).send('Private/internal addresses blocked');
    // 🔒 仅允许图片类型
    const response = await fetch(url, {
      headers: {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
        'Referer': 'https://ssypco.com/'
      },
      redirect: 'manual',
      signal: AbortSignal.timeout(6000)
    });
    if (!response.ok) return res.status(response.status).send('Fetch failed');
    const contentType = response.headers.get('content-type') || '';
    if (!/^image\//.test(contentType)) return res.status(400).send('Only image content allowed');
    // 🔒 限制响应体大小(防拉爆内存)
    const maxBytes = 5 * 1024 * 1024;
    const buffer = Buffer.from(await response.arrayBuffer());
    if (buffer.length > maxBytes) return res.status(413).send('Image too large');
    res.setHeader('Content-Type', contentType || 'image/jpeg');
    res.setHeader('Cache-Control', 'public, max-age=86400');
    res.send(buffer);
  } catch (err) {
    res.status(500).send(err.message);
  }
});
app.get('/m', (req, res) => res.sendFile(path.join(__dirname, '..', 'public', 'mobile-index.html')));
app.get('/m/index', (req, res) => res.sendFile(path.join(__dirname, '..', 'public', 'mobile-index.html')));
app.get('/m/cart', (req, res) => res.sendFile(path.join(__dirname, '..', 'public', 'mobile-cart.html')));
app.get('/m/user', (req, res) => res.sendFile(path.join(__dirname, '..', 'public', 'mobile-user.html')));
app.get('/m/orders', (req, res) => res.sendFile(path.join(__dirname, '..', 'public', 'mobile-orders.html')));
app.get('/m/detail/:id', (req, res) => res.sendFile(path.join(__dirname, '..', 'public', 'mobile-detail.html')));
app.get('/pc', (req, res) => res.sendFile(path.join(__dirname, '..', '..', 'pc', 'index.html')));
app.get('/', (req, res) => res.redirect('/admin'));
// ========== 健康检查 ==========
let startTime = Date.now();
app.get('/api/health', (req, res) => {
  try {
    const { get } = require('./utils/db');
    const dbOk = get('SELECT 1 AS ok');
    const mem = process.memoryUsage();
    res.json({
      code: 0,
      message: 'ok',
      data: {
        status: 'healthy',
        uptime: Math.floor((Date.now() - startTime) / 1000),
        db: !!dbOk,
        memory: {
          rss: Math.round(mem.rss / 1024 / 1024) + 'MB',
          heapUsed: Math.round(mem.heapUsed / 1024 / 1024) + 'MB',
          heapTotal: Math.round(mem.heapTotal / 1024 / 1024) + 'MB'
        },
        version: '1.0.0'
      }
    });
  } catch(e) {
    res.status(503).json({ code: 503, message: 'unhealthy', data: { status: 'error', error: e.message } });
  }
});
// ========== 全局错误兜底 ==========
// Express错误中间件 - 确保任何路由异常都能返回正确响应而不是挂掉
app.use((err, req, res, next) => {
  // 静态文件请求错误：静默处理，返回404而不是崩溃
  if (err.code === 'ENOENT' || err.code === 'EACCES') {
    return res.status(404).send('Not Found');
  }
  console.error('[路由错误]', req.method, req.path, err.message);
  // 判断请求类型返回合适格式
  if (req.path.startsWith('/api/')) {
    return res.status(500).json({ code: 500, message: '服务器内部错误，请稍后重试' });
  }
  // 前端页面请求：返回静态页而非空白
  res.status(500).send('<h1>500 服务器繁忙</h1><p>请稍后刷新</p>');
});
// ========== 静态文件故障转移 ==========
// 如果主路由挂掉，最后的防线
app.use((req, res, next) => {
  // 移动端/管理端请求，即使前面的路由都失败了也尝试返回静态文件
  if (req.path === '/mobile' || req.path.startsWith('/mobile/')) {
    const fallbackPath = path.join(__dirname, '..', '..', 'mobile', 'index.html');
    return res.sendFile(fallbackPath, (err) => {
      if (err) next(err);
    });
  }
  if (req.path === '/' || req.path === '/admin' || req.path.startsWith('/admin/')) {
    const fallbackPath = path.join(__dirname, '..', '..', 'admin', 'dist', 'index.html');
    return res.sendFile(fallbackPath, (err) => {
      if (err) next(err);
    });
  }
  next();
});
// 404
// ========== 版权强制 API 路由 ==========
const { queryTamperLogs, verifyCopyrightWatermark, logTamper } = require('./middleware/copyright-enforce');
// 查询篡改日志（管理员）
// 基础版权信息接口
app.get('/api/copyright', (req, res) => {
  try {
    const { getCopyrightInfo } = require('./lib/copyright');
    res.json({ code: 0, data: getCopyrightInfo() });
  } catch(e) {
    res.json({ code: 1, message: '获取失败' });
  }
});
app.get('/api/copyright/tamper-logs', (req, res) => {
  const page = parseInt(req.query.page) || 1;
  const pageSize = parseInt(req.query.pageSize) || 20;
  const type = req.query.type || '';
  const result = queryTamperLogs(page, pageSize, type);
  res.json({ code: 0, data: result });
});
// 校验核心文件完整性（管理员）
app.get('/api/copyright/watermark-check', (req, res) => {
  const results = verifyCopyrightWatermark();
  const tampered = results.filter(r => r.status === 'tampered' || r.status === 'missing');
  res.json({
    code: 0,
    data: {
      files: results,
      summary: {
        total: results.length,
        ok: results.filter(r => r.status === 'ok').length,
        tampered: tampered.length
      }
    }
  });
});
app.use((req, res) => {
  res.status(404).json({ code: 404, message: '接口不存在' });
});
// ========== 定时任务：超时订单自动取消 ==========
const TIMEOUT_MINUTES = config.orderTimeoutMinutes;
const CHECK_INTERVAL = 5 * 60 * 1000;
setInterval(() => {
  const { get, run, all, transaction } = require('./utils/db');
  try {
    const expired = all(`SELECT id, order_no FROM orders WHERE status='pending_pay' AND 
      datetime(created_at, '+${TIMEOUT_MINUTES} minutes') < datetime('now','localtime')`);
    if (expired.length > 0) {
      transaction(() => {
        for (const order of expired) {
          run("UPDATE orders SET status='closed', close_reason='超时未支付', close_time=datetime('now','localtime') WHERE id=?", [order.id]);
          run('INSERT INTO order_logs(order_id,action,content) VALUES(?,?,?)', [order.id, 'auto_close', '系统自动关闭超时订单']);
          const items = all('SELECT * FROM order_items WHERE order_id=?', [order.id]);
          for (const item of items) {
            if (item.sku_id) {
              run('UPDATE product_skus SET stock=stock+? WHERE id=?', [item.quantity, item.sku_id]);
            } else {
              run('UPDATE products SET stock=stock+? WHERE id=?', [item.quantity, item.product_id]);
            }
          }
        }
      });
      console.log('[定时任务] 已自动关闭 ' + expired.length + ' 个超时订单');
    }
  } catch(e) { console.error('[定时任务] 超时订单取消失败:', e.message); }
}, CHECK_INTERVAL);
// ===== 自动SEO文章生成器：每日定时调度 =====
try {
  const seoScheduler = require('./jobs/seo-scheduler');
  seoScheduler.start();
} catch (e) { console.error('[SEO调度器] 启动失败:', e.message); }
// ===== 营销自动化：定时调度器 =====
try {
  const mktScheduler = require('./jobs/marketing-scheduler');
  mktScheduler.start();
} catch (e) { console.error('[营销自动化] 调度器启动失败:', e.message); }

const server = app.listen(config.port, config.host, () => {
  console.log('LiteMall 轻商城 API 已启动: http://' + config.host + ':' + config.port);
});
// ========== 优雅关闭 ==========
function gracefulShutdown(signal) {
  console.log(`[${signal}] 正在关闭服务...`);
  server.close(() => {
    console.log('[关闭] HTTP服务已停止');
    process.exit(0);
  });
  // 10秒后强制退出
  setTimeout(() => {
    console.error('[关闭] 强制退出');
    process.exit(1);
  }, 10000).unref();
}
process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
process.on('SIGINT', () => gracefulShutdown('SIGINT'));
