/**
 * 打包优化：关卡库压缩、settings 预加载策略、体积报告。
 * 供 package-for-project.js / package-for-cdn.js 共用。
 */
const fs = require('fs');
const os = require('os');
const path = require('path');
const zlib = require('zlib');
const { execSync } = require('child_process');

function formatBytes(n) {
  if (n >= 1024 * 1024) return `${(n / 1024 / 1024).toFixed(1)} MB`;
  if (n >= 1024) return `${(n / 1024).toFixed(1)} KB`;
  return `${n} B`;
}

function dirSize(root) {
  if (!fs.existsSync(root)) return 0;
  let total = 0;
  for (const ent of fs.readdirSync(root, { withFileTypes: true })) {
    const p = path.join(root, ent.name);
    if (ent.isDirectory()) total += dirSize(p);
    else total += fs.statSync(p).size;
  }
  return total;
}

function fileSize(p) {
  return fs.existsSync(p) ? fs.statSync(p).size : 0;
}

/** 压缩关卡库 JSON（去掉空白，不改变语义） */
function minifyLevelsDatabase(srcPath, dstPath) {
  const raw = fs.readFileSync(srcPath, 'utf8');
  const data = JSON.parse(raw);
  const compact = JSON.stringify(data);
  fs.writeFileSync(dstPath, compact, 'utf8');
  return { before: Buffer.byteLength(raw, 'utf8'), after: Buffer.byteLength(compact, 'utf8') };
}

/** 可选：生成 .br 供静态服务器直接返回 */
function brotliCompressFile(srcPath, dstPath, quality = 9) {
  const input = fs.readFileSync(srcPath);
  const out = zlib.brotliCompressSync(input, {
    params: { [zlib.constants.BROTLI_PARAM_QUALITY]: quality },
  });
  fs.writeFileSync(dstPath, out);
  return { raw: input.length, br: out.length };
}

/** 为 WebGL 目录下所有 .bundle 生成 .bundle.br */
function brotliCompressWebglBundles(webglDir, opts = {}) {
  if (!fs.existsSync(webglDir)) return { count: 0, saved: 0 };
  let count = 0;
  let saved = 0;
  for (const name of fs.readdirSync(webglDir)) {
    if (!name.endsWith('.bundle') || name.endsWith('.bundle.br')) continue;
    const src = path.join(webglDir, name);
    if (!fs.statSync(src).isFile()) continue;
    const { raw, br } = brotliCompressFile(src, src + '.br', opts.quality);
    count += 1;
    saved += raw - br;
    console.log(`>>> ${name}.br: ${formatBytes(raw)} → ${formatBytes(br)}`);
  }
  return { count, saved };
}

/**
 * 调整预加载 bundle：默认只预加载 main，resources / level-prefabs 按需加载。
 * @param {object} opts
 * @param {boolean} [opts.preloadResources=false]
 * @param {boolean} [opts.preloadLevelPrefabs=false]
 */
function patchPreloadSettings(settingsObj, opts = {}) {
  const preloadResources = opts.preloadResources === true;
  const preloadLevelPrefabs = opts.preloadLevelPrefabs === true;
  const bundles = settingsObj.assets?.projectBundles || [];
  const preload = [{ bundle: 'main' }];
  if (preloadResources && bundles.includes('resources')) {
    preload.push({ bundle: 'resources' });
  }
  if (preloadLevelPrefabs && bundles.includes('level-prefabs')) {
    preload.push({ bundle: 'level-prefabs' });
  }
  settingsObj.assets = settingsObj.assets || {};
  settingsObj.assets.preloadBundles = preload;
  return settingsObj;
}

/**
 * 关闭 Cocos 启动闪屏（"Created with Cocos"）。
 * totalTime <= 0 时引擎跳过闪屏等待，首屏可快约 2s；不影响关卡切换与网络下载。
 * @param {object} opts
 * @param {boolean} [opts.disableSplash=true]
 * @param {boolean} [opts.stripSplashAssets=false] 去掉内嵌 logo/background，略减 scenes 包体积
 */
function patchSplashSettings(settingsObj, opts = {}) {
  const disableSplash = opts.disableSplash !== false;
  if (!disableSplash) return settingsObj;

  settingsObj.splashScreen = settingsObj.splashScreen || {};
  settingsObj.splashScreen.totalTime = 0;

  if (opts.stripSplashAssets) {
    delete settingsObj.splashScreen.logo;
    delete settingsObj.splashScreen.background;
  }
  return settingsObj;
}

/**
 * 就地修改 zip bundle 内的 settings.json（必须用 zip -0 store，loader 不支持 deflate）。
 */
function patchSplashInZipBundle(zipPath, opts = {}) {
  const absZip = path.resolve(zipPath);
  const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'cocos-splash-'));
  try {
    execSync(`unzip -q -o "${absZip}" -d "${tmp}"`, { stdio: 'pipe' });
    const settingsPath = path.join(tmp, 'src', 'settings.json');
    if (!fs.existsSync(settingsPath)) return false;
    const j = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
    patchSplashSettings(j, opts);
    fs.writeFileSync(settingsPath, JSON.stringify(j));
    const newZip = absZip + '.new';
    if (fs.existsSync(newZip)) fs.unlinkSync(newZip);
    execSync(`cd "${tmp}" && zip -0 -q -r "${newZip}" .`, { stdio: 'pipe' });
    fs.renameSync(newZip, absZip);
    return true;
  } finally {
    fs.rmSync(tmp, { recursive: true, force: true });
  }
}

function printPackageReport(outDir, opts = {}) {
  const lines = ['\n>>> 包体积报告:'];
  const entries = [
    ['cocos-js', path.join(outDir, 'cocos-js')],
    ['src', path.join(outDir, 'src')],
    ['assets/main', path.join(outDir, 'assets', 'main')],
    ['assets/resources', path.join(outDir, 'assets', 'resources')],
    ['assets/level-prefabs', path.join(outDir, 'assets', 'level-prefabs')],
    ['assets/internal', path.join(outDir, 'assets', 'internal')],
    ['levels-database.json', path.join(outDir, 'levels-database.json')],
    ['levels-database.json.br', path.join(outDir, 'levels-database.json.br')],
    ['Build', path.join(outDir, 'Build')],
  ];
  let total = 0;
  for (const [label, p] of entries) {
    if (!fs.existsSync(p)) continue;
    const sz = fs.statSync(p).isDirectory() ? dirSize(p) : fileSize(p);
    if (sz <= 0) continue;
    total += sz;
    lines.push(`    ${label.padEnd(28)} ${formatBytes(sz)}`);
  }
  const grand = dirSize(outDir);
  lines.push(`    ${'合计（顶层目录）'.padEnd(28)} ${formatBytes(grand)}`);
  if (opts.preloadNote) lines.push(`    预加载: ${opts.preloadNote}`);
  console.log(lines.join('\n'));
}

module.exports = {
  formatBytes,
  dirSize,
  fileSize,
  minifyLevelsDatabase,
  brotliCompressFile,
  brotliCompressWebglBundles,
  patchPreloadSettings,
  patchSplashSettings,
  patchSplashInZipBundle,
  printPackageReport,
};
