#!/usr/bin/env node
/**
 * 将 build/assets/level-prefabs 拆为：
 *   - shell: config.json + index.js（进 assets_all 首屏）
 *   - 每关一包: assets/level-prefabs/import/.../*.json
 *
 * 输出 levels-manifest.json 供 loader 按 levelId 按需下载。
 */
const crypto = require('crypto');
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
const { formatBytes } = require('./package-optimize');

function mkdirp(p) { fs.mkdirSync(p, { recursive: true }); }

function hashFileMd5(filePath) {
  return crypto.createHash('md5').update(fs.readFileSync(filePath)).digest('hex');
}

function zipDir(srcDir, outFile) {
  const absOut = path.resolve(outFile);
  mkdirp(path.dirname(absOut));
  if (fs.existsSync(absOut)) fs.unlinkSync(absOut);
  execSync(`cd "${path.resolve(srcDir)}" && zip -0 -q -r "${absOut}" .`, { stdio: 'pipe' });
}

function walkJsonFiles(dir, out = []) {
  if (!fs.existsSync(dir)) return out;
  for (const ent of fs.readdirSync(dir, { withFileTypes: true })) {
    const p = path.join(dir, ent.name);
    if (ent.isDirectory()) walkJsonFiles(p, out);
    else if (ent.name.endsWith('.json')) out.push(p);
  }
  return out;
}

/** 扫描 import/*.json，从预制体名 Level{id} 建立 levelId → 相对路径 */
function indexImportFiles(levelPrefabsDir) {
  const importRoot = path.join(levelPrefabsDir, 'import');
  const byLevelId = new Map();
  for (const abs of walkJsonFiles(importRoot)) {
    const rel = path.relative(levelPrefabsDir, abs).split(path.sep).join('/');
    const txt = fs.readFileSync(abs, 'utf8');
    const m = /"Level(\d+)"/.exec(txt);
    if (!m) continue;
    const levelId = m[1];
    if (byLevelId.has(levelId)) {
      console.warn(`>>> 警告: Level${levelId} 重复 import，保留 ${byLevelId.get(levelId)}`);
      continue;
    }
    byLevelId.set(levelId, rel);
  }
  return byLevelId;
}

/** 从 config.json 校验 path ↔ uuid */
function readConfigLevels(levelPrefabsDir) {
  const cfg = JSON.parse(fs.readFileSync(path.join(levelPrefabsDir, 'config.json'), 'utf8'));
  const out = new Map();
  for (const [idx, entry] of Object.entries(cfg.paths || {})) {
    const m = /^level-prefabs\/Level(\d+)$/.exec(entry[0]);
    if (!m) continue;
    out.set(m[1], {
      path: entry[0],
      pathIndex: idx,
      uuid: (cfg.uuids || [])[+idx],
    });
  }
  return out;
}

/**
 * @param {string} levelPrefabsDir  Cocos 构建产物 assets/level-prefabs
 * @param {string} webglDir         输出 WebGL/*.bundle
 * @param {string} manifestOutPath  输出 levels-manifest.json
 */
function splitLevelBundles(levelPrefabsDir, webglDir, manifestOutPath, packTmpDir) {
  if (!fs.existsSync(path.join(levelPrefabsDir, 'config.json'))) {
    throw new Error(`缺少 level-prefabs/config.json: ${levelPrefabsDir}`);
  }

  const configLevels = readConfigLevels(levelPrefabsDir);
  const importByLevel = indexImportFiles(levelPrefabsDir);
  mkdirp(webglDir);
  const stageBase = packTmpDir || path.join(webglDir, '..', '.level-pack-tmp');
  mkdirp(stageBase);

  const manifest = {
    version: 1,
    shell: ['assets/level-prefabs/config.json', 'assets/level-prefabs/index.js'],
    levels: {},
  };

  let packed = 0;
  let totalBytes = 0;
  const missing = [];

  for (const [levelId, meta] of configLevels) {
    const importRel = importByLevel.get(levelId);
    if (!importRel) {
      missing.push(levelId);
      continue;
    }
    const stageRoot = path.join(stageBase, levelId);
    const assetRoot = path.join(stageRoot, 'assets', 'level-prefabs');
    mkdirp(path.dirname(path.join(assetRoot, importRel)));
    fs.copyFileSync(
      path.join(levelPrefabsDir, importRel),
      path.join(assetRoot, importRel),
    );

    const zipTmp = path.join(stageBase, `${levelId}.zip`);
    zipDir(stageRoot, zipTmp);
    const hash = hashFileMd5(zipTmp);
    const bundleName = `defaultlocalgroup_level_${levelId}_${hash}.bundle`;
    fs.copyFileSync(zipTmp, path.join(webglDir, bundleName));
    const size = fs.statSync(zipTmp).size;
    totalBytes += size;

    manifest.levels[levelId] = {
      bundle: bundleName,
      path: meta.path,
      uuid: meta.uuid,
      files: [`assets/level-prefabs/${importRel}`],
      bytes: size,
    };
    packed += 1;
    fs.rmSync(stageRoot, { recursive: true, force: true });
    fs.unlinkSync(zipTmp);
  }

  fs.rmSync(stageBase, { recursive: true, force: true });

  fs.writeFileSync(manifestOutPath, JSON.stringify(manifest), 'utf8');

  console.log(`>>> 关卡分包: ${packed} 关, 合计 ${formatBytes(totalBytes)}, 均 ${formatBytes(Math.round(totalBytes / Math.max(packed, 1)))}/关`);
  if (missing.length) {
    console.warn(`>>> 警告: ${missing.length} 关在 config 中无 import 文件 (例: ${missing.slice(0, 5).join(', ')})`);
  }
  return { manifest, packed, missing, totalBytes };
}

module.exports = {
  splitLevelBundles,
  indexImportFiles,
  readConfigLevels,
};

if (require.main === module) {
  const levelDir = path.resolve(process.argv[2]);
  const webgl = path.resolve(process.argv[3]);
  const manifest = path.resolve(process.argv[4] || path.join(path.dirname(webgl), 'levels-manifest.json'));
  splitLevelBundles(levelDir, webgl, manifest);
}
