// Lightweight update-check + verified download (manual prompt flow)
const https = require('https');
const crypto = require('crypto');
const fs = require('fs');
const path = require('path');
const os = require('os');

// User's own server endpoint. Change host after deploy.
const UPDATE_URL = 'https://agnetpojia.top/update/latest.json';
const JSON_CAP = 64 * 1024;        // latest.json 不应超过 64 KB
const DOWNLOAD_CAP = 300 * 1024 * 1024; // 安装包上限 300 MB
const MAX_REDIRECTS = 3;

let APP_VERSION = '1.0.0';
function init(appVersion) { if (appVersion) APP_VERSION = appVersion; }

// 极简 semver 比较：a > b 才返回 true（仅支持 x.y.z 数字段）
function semverGt(a, b) {
  const pa = String(a).replace(/^v/, '').split('.').map(n => parseInt(n, 10));
  const pb = String(b).replace(/^v/, '').split('.').map(n => parseInt(n, 10));
  if (pa.some(Number.isNaN) || pb.some(Number.isNaN)) return false;
  for (let i = 0; i < 3; i++) {
    const x = pa[i] || 0, y = pb[i] || 0;
    if (x !== y) return x > y;
  }
  return false;
}

function expectedHost() {
  try { return new URL(UPDATE_URL).host; } catch { return null; }
}

// 只允许 https 且与更新端点同源的下载地址
function validateDownloadUrl(url) {
  try {
    const u = new URL(url);
    if (u.protocol !== 'https:') return null;
    const host = expectedHost();
    if (host && u.host !== host) return null;
    return u.href;
  } catch { return null; }
}

function getJson(url, redirectsLeft) {
  return new Promise((resolve) => {
    const req = https.get(url, { timeout: 8000 }, (res) => {
      if ([301, 302, 307, 308].includes(res.statusCode) && res.headers.location && redirectsLeft > 0) {
        res.resume();
        try {
          const next = new URL(res.headers.location, url);
          if (next.protocol !== 'https:') return resolve({ ok: false, reason: 'redirect-not-https' });
          return resolve(getJson(next.href, redirectsLeft - 1));
        } catch { return resolve({ ok: false, reason: 'bad-redirect' }); }
      }
      if (res.statusCode !== 200) { res.resume(); return resolve({ ok: false, reason: 'http-' + res.statusCode }); }
      let size = 0;
      const chunks = [];
      res.on('data', c => {
        size += c.length;
        if (size > JSON_CAP) { req.destroy(); return resolve({ ok: false, reason: 'too-large' }); }
        chunks.push(c);
      });
      res.on('end', () => {
        try { resolve({ ok: true, json: JSON.parse(Buffer.concat(chunks).toString('utf8')) }); }
        catch { resolve({ ok: false, reason: 'parse' }); }
      });
      res.on('error', () => resolve({ ok: false, reason: 'network' }));
    });
    req.on('error', () => resolve({ ok: false, reason: 'network' }));
    req.on('timeout', () => { req.destroy(); resolve({ ok: false, reason: 'timeout' }); });
  });
}

function checkUpdate() {
  return getJson(UPDATE_URL, MAX_REDIRECTS).then(r => {
    if (!r.ok) return r;
    const j = r.json;
    if (j && j.version && semverGt(j.version, APP_VERSION)) {
      return {
        ok: true,
        hasUpdate: true,
        version: j.version,
        url: j.url ? validateDownloadUrl(j.url) : null,
        sha512: typeof j.sha512 === 'string' && /^[0-9a-fA-F]{128}$/.test(j.sha512) ? j.sha512.toLowerCase() : null,
        notes: j.notes || ''
      };
    }
    return { ok: true, hasUpdate: false, version: APP_VERSION };
  });
}

// 下载安装包到临时目录并校验 sha512，全部通过才返回文件路径
function downloadAndVerify(url, sha512Hex) {
  return new Promise((resolve) => {
    const safe = validateDownloadUrl(url);
    if (!safe) return resolve({ ok: false, reason: 'bad-url' });
    if (!sha512Hex) return resolve({ ok: false, reason: 'no-sha512' });
    const dest = path.join(fs.mkdtempSync(path.join(os.tmpdir(), 'agent-pojia-update-')), path.basename(new URL(safe).pathname) || 'update.exe');
    const file = fs.createWriteStream(dest);
    const cleanup = () => { try { fs.unlinkSync(dest); } catch {} };
    const doGet = (u, redirectsLeft) => {
      const req = https.get(u, { timeout: 15000 }, (res) => {
        if ([301, 302, 307, 308].includes(res.statusCode) && res.headers.location && redirectsLeft > 0) {
          res.resume();
          try {
            const next = new URL(res.headers.location, u);
            if (next.protocol !== 'https:') { file.close(cleanup); return resolve({ ok: false, reason: 'redirect-not-https' }); }
            return doGet(next.href, redirectsLeft - 1);
          } catch { file.close(cleanup); return resolve({ ok: false, reason: 'bad-redirect' }); }
        }
        if (res.statusCode !== 200) { res.resume(); file.close(cleanup); return resolve({ ok: false, reason: 'http-' + res.statusCode }); }
        let size = 0;
        const hash = crypto.createHash('sha512');
        res.on('data', c => {
          size += c.length;
          if (size > DOWNLOAD_CAP) { req.destroy(); file.close(cleanup); return resolve({ ok: false, reason: 'too-large' }); }
          hash.update(c);
          file.write(c);
        });
        res.on('end', () => {
          file.close(() => {
            if (hash.digest('hex') !== sha512Hex.toLowerCase()) { cleanup(); return resolve({ ok: false, reason: 'sha512-mismatch' }); }
            resolve({ ok: true, path: dest, size });
          });
        });
        res.on('error', () => { file.close(cleanup); resolve({ ok: false, reason: 'network' }); });
      });
      req.on('error', () => { file.close(cleanup); resolve({ ok: false, reason: 'network' }); });
      req.on('timeout', () => { req.destroy(); file.close(cleanup); resolve({ ok: false, reason: 'timeout' }); });
    };
    doGet(safe, MAX_REDIRECTS);
  });
}

module.exports = { checkUpdate, downloadAndVerify, init, UPDATE_URL };
