import { spawn } from "child_process";
import fs from "fs";
import compressing from 'compressing';
import { v4, parse } from 'uuid';
import { Base64 } from 'js-base64';
import { copyDir, removeDir, fix2 } from "./utils.js";

export async function build() {

  const startTime = new Date().getTime();

  // 构建前检查
  const projectName = build_check();

  // 清理旧文件

  // 构建form
  await removeDir('./publish/form');
  await buildForm();

  // 替换uuid
  replaceUuids();

  // 改设置为非bundle
  changeSettingToWebDesktop();

  // 构建play
  await removeDir('./publish/play');
  await buildWebDesktop();
  await copyDir('../play/build/web-desktop', 'publish/play');
  replaceIndexHtml();
  console.log('构建 web desktop 成功！');

  // 改设置为bundle
  changeSettingsToBundle();

  await removeDir('./publish/android');
  await buildAndroidBundle();
  await copyDir('../play/build_android/jsb-link/remote', 'publish/android');
  console.log('构建 android bundle 成功！');

  await removeDir('./publish/ios');
  await buildIosBundle();
  await copyDir('../play/build_ios/jsb-link/remote', 'publish/ios');
  console.log('构建 ios bundle 成功！');

  await removeDir('./publish/web_desktop');
  await buildWebBundle();
  await copyDir(`../play/build_web_desktop/web-desktop/assets/${projectName}`, 'publish/web_desktop');
  console.log('构建 web bundle 成功！');

  // 改设置为非bundle
  changeSettingToWebDesktop();

  createConfigFile(projectName);

  compressAll(projectName);

  const endTime = new Date().getTime();
  const duration = new Date(endTime - startTime);
  console.log(`打包完成！`);
  console.log(`用时${duration.getMinutes()}分${duration.getSeconds()}秒。`);
}

async function buildForm() {
  const paths = fs.readdirSync('../form');

  if (!paths.includes('tsconfig.json')) {
    await copyDir('../form/src', 'publish/form');
    return;
  }

  if (process.platform == 'win32') {
    await execCmd('npm.cmd', ['install'], '../form');
  } else {
    await execCmd('npm', ['install'], '../form');
  }

  if (process.platform == 'win32') {
    await execCmd('npm.cmd', ['run', 'publish'], '../form');
  } else {
    await execCmd('npm', ['run', 'publish'], '../form');
  }

  await compressing.zip.uncompress('../form/publish/form.zip', './publish/form');
}

function execCmd(cmd, params, path) {
  return new Promise((resolve, reject) => {
    const buffer = spawn(
      cmd,
      params,
      { cwd: path }
    );

    buffer.stdout.on('data', (data) => {
      console.log(`stdout: ${data}`);
    });

    buffer.stderr.on('data', (data) => {
      console.error(`stderr: ${data}`);
    });

    buffer.on('close', (code) => {
      console.log(`child process exited with code ${code}`);
      resolve();
    });
  });
}


const creatorBasePath = 'C:\\CocosDashboard_1.0.6\\resources\\.editors\\Creator';
export function buildWebDesktop() {
  return new Promise((resolve, reject) => {
    const buffer = spawn(
      `${creatorBasePath}\\2.4.0\\CocosCreator.exe`,
      ['--path', './', '--build', 'platform=web-desktop;debug=true', '--force'],
      { cwd: '../play' }
    );

    buffer.stdout.on('data', (data) => {
      console.log(`stdout: ${data}`);
    });

    buffer.stderr.on('data', (data) => {
      console.error(`stderr: ${data}`);
    });

    buffer.on('close', (code) => {
      console.log(`child process exited with code ${code}`);
      resolve();
    });
  });
}

export function buildAndroidBundle() {
  return new Promise((resolve, reject) => {
    const buffer = spawn(
      `${creatorBasePath}\\2.4.4\\CocosCreator.exe`,
      ['--path', './', '--build', "platform=ios;debug=false;md5Cache=true;buildPath=build_android;appABIs=['armeabi-v7a','x86','x86_64','arm64-v8a'];encryptJs=true;xxteaKey=6bbfce23-28b4-4a;zipCompressJs=true", '--force'],
      { cwd: '../play' }
    );

    buffer.stdout.on('data', (data) => {
      console.log(`stdout: ${data}`);
    });

    buffer.stderr.on('data', (data) => {
      console.error(`stderr: ${data}`);
    });

    buffer.on('close', (code) => {
      console.log(`child process exited with code ${code}`);
      resolve();
    });
  });
}

export function buildIosBundle() {
  return new Promise((resolve, reject) => {
    const buffer = spawn(
      `${creatorBasePath}\\2.4.4\\CocosCreator.exe`,
      ['--path', './', '--build', "platform=ios;debug=false;md5Cache=true;buildPath=build_ios;encryptJs=true;xxteaKey=6bbfce23-28b4-4a;zipCompressJs=true", '--force'],
      { cwd: '../play' }
    );

    buffer.stdout.on('data', (data) => {
      console.log(`stdout: ${data}`);
    });

    buffer.stderr.on('data', (data) => {
      console.error(`stderr: ${data}`);
    });

    buffer.on('close', (code) => {
      console.log(`child process exited with code ${code}`);
      resolve();
    });
  });
}


export function buildWebBundle() {
  return new Promise((resolve, reject) => {
    const buffer = spawn(
      `${creatorBasePath}\\2.4.4\\CocosCreator.exe`,
      ['--path', './', '--build', "platform=web-desktop;debug=false;buildPath=build_web_desktop", '--force'],
      { cwd: '../play' }
    );

    buffer.stdout.on('data', (data) => {
      console.log(`stdout: ${data}`);
    });

    buffer.stderr.on('data', (data) => {
      console.error(`stderr: ${data}`);
    });

    buffer.on('close', (code) => {
      console.log(`child process exited with code ${code}`);
      resolve();
    });
  });
}

export function createConfigFile(projectName) {
  const androidPaths = fs.readdirSync(`publish/android/${projectName}`);
  const androidConfigFileName = androidPaths.find(path => path.indexOf('config') == 0);
  const androidVersion = androidConfigFileName.split('.')[1];

  const iosPaths = fs.readdirSync(`publish/ios/${projectName}`);
  const iosConfigFileName = iosPaths.find(path => path.indexOf('config') == 0);
  const iosVersion = iosConfigFileName.split('.')[1];

  const config = {
    "ios": {
      "sceneName": projectName,
      "version": iosVersion
    },
    "android": {
      "sceneName": projectName,
      "version": androidVersion
    }
  }

  fs.writeFileSync('publish/config.json', JSON.stringify(config));
}

export function compressAll(projectName) {
  const tarStream = new compressing.zip.Stream();
  tarStream.addEntry('./publish/play');
  tarStream.addEntry('./publish/form');
  tarStream.addEntry('./publish/ios');
  tarStream.addEntry('./publish/android');
  tarStream.addEntry('./publish/web_desktop');
  tarStream.addEntry('./publish/config.json');

  const destStream = fs.createWriteStream(`publish/${getReleaseFileName(projectName)}.zip`);

  tarStream.pipe(destStream);
}

function getReleaseFileName(projectName) {
  let date = new Date();
  let fileName = `${projectName}_${date.getFullYear()}${fix2(date.getMonth() + 1)}${fix2(date.getDate())} `;
  fileName += `${fix2(date.getHours())}-${fix2(date.getMinutes())}-${fix2(date.getSeconds())}`;
  return fileName;
}

export function build_check() {
  const dirNames = process.cwd().split(/\/|\\/);
  const projectName = dirNames[dirNames.length - 2];
  const path = '../play/assets'
  let folderName = '';
  fs.readdirSync(path).find(fileName => {
    const st = fs.statSync(`${path}/${fileName}`);
    if (st.isDirectory()) {
      folderName = fileName;
    }
  });

  if (projectName != folderName) {
    throw (`项目名(${projectName})与bundle文件夹名(${folderName})不相同`);
  }
  let same = false;
  const files = fs.readdirSync(`${path}/${folderName}/scene`);
  files.forEach(fileName => {
    fileName.split('.').forEach((str, idx, arr) => {
      if (str == 'fire') {
        const sceneName = arr[idx - 1];
        if (folderName == sceneName) {
          same = true;
        }
      }
    })
  });

  if (!same) {
    throw (`bundle文件夹名称(${folderName})与scene名称不相同`);
  }

  return projectName;
}

function getFolderName(path) {
  let folderName = '';
  fs.readdirSync(path).find(fileName => {
    const st = fs.statSync(`${path}/${fileName}`);
    if (st.isDirectory()) {
      folderName = fileName;
    }
  });
  return folderName;
}
function editFolderMeta(path, folderName, isBundle) {
  const metaPath = `${path}/${folderName}.meta`;
  const metaDataStr = fs.readFileSync(metaPath);
  const metaData = JSON.parse(metaDataStr);
  metaData.isBundle = isBundle;
  metaData.isRemoteBundle = {
    ios: isBundle,
    android: isBundle
  };
  fs.writeFileSync(metaPath, JSON.stringify(metaData));
}


export function changeSettingToWebDesktop() {
  const path = '../play/assets'
  const folderName = getFolderName(path);
  editFolderMeta(path, folderName, false);
}

export function changeSettingsToBundle() {
  const path = '../play/assets'
  const folderName = getFolderName(path);
  editFolderMeta(path, folderName, true);
}

export function replaceUuids() {
  console.log('build_step_0 开始~！');

  function getFolderName(path) {
    let folderName = '';
    fs.readdirSync(path).find(fileName => {
      const st = fs.statSync(`${path}/${fileName}`);
      if (st.isDirectory()) {
        folderName = fileName;
      }
    });
    return folderName;
  }
  function editFolderMeta(path, folderName) {
    const metaPath = `${path}/${folderName}.meta`;
    const metaDataStr = fs.readFileSync(metaPath);
    const metaData = JSON.parse(metaDataStr);
    metaData.isBundle = false;
    fs.writeFileSync(metaPath, JSON.stringify(metaData));
  }


  function fileReplace(path, replaceStr, newStr) {
    if (!fs.existsSync(path)) {
      return;
    }

    const fileStr = fs.readFileSync(path);
    const newFileStr = fileStr.toString().replace(new RegExp(replaceStr.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), 'g'), newStr);
    fs.writeFileSync(path, newFileStr);
  }

  const path = '../play/assets'
  const folderName = getFolderName(path);
  editFolderMeta(path, folderName);

  const oldFireUuid = '57ea7c61-9b8b-498a-b024-c98ee9124beb';
  const newFireUuid = v4();
  fileReplace(`../play/assets/${folderName}/scene/${folderName}.fire.meta`, oldFireUuid, newFireUuid);
  fileReplace(`../play/assets/${folderName}/scene/${folderName}.fire`, oldFireUuid, newFireUuid);
  fileReplace('../play/settings/builder.json', oldFireUuid, newFireUuid);


  function getShortUuid(uuid) {
    const bytes = parse(uuid).subarray(1);
    return uuid.substring(0, 5) + Base64.fromUint8Array(bytes).substring(2);
  }

  const oldJsUuid = 'f4ede462-f8d7-4069-ba80-915611c058ca';
  const oldJsShortUuid = 'f4edeRi+NdAabqAkVYRwFjK';
  const oldJsId = 'e687yyoRBIzZAOVRL8Sseh';
  const newJsUuid = v4();
  const newJsShortUuid = getShortUuid(newJsUuid);
  const newJsId = v4().replace(/-/g, '').substring(0, oldJsId.length);
  fileReplace(`../play/assets/${folderName}/scene/${folderName}.js.meta`, oldJsUuid, newJsUuid);
  fileReplace(`../play/assets/${folderName}/scene/${folderName}.fire`, oldFireUuid, newFireUuid);
  fileReplace(`../play/assets/${folderName}/scene/${folderName}.fire`, oldJsShortUuid, newJsShortUuid);
  fileReplace(`../play/assets/${folderName}/scene/${folderName}.fire`, oldJsId, newJsId);

  console.log('build_step_0 完成~！');
}

export function replaceIndexHtml() {
  const data = fs.readFileSync('./index.html');
  fs.writeFileSync('./publish/play/index.html', data);
}
