import { spawn } from "node:child_process";
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import path from "node:path";
import { NodeIO } from "@gltf-transform/core";
import { ALL_EXTENSIONS, EXTTextureWebP, KHRTextureBasisu } from "@gltf-transform/extensions";
import { MeshoptDecoder, MeshoptEncoder } from "meshoptimizer";
import sharp from "sharp";

export const TOKTX_COMMAND = process.env.TOKTX ?? "toktx";

export async function createGameAssetIO() {
  await MeshoptDecoder.ready;
  await MeshoptEncoder.ready;
  return new NodeIO()
    .registerExtensions(ALL_EXTENSIONS)
    .registerDependencies({
      "meshopt.decoder": MeshoptDecoder,
      "meshopt.encoder": MeshoptEncoder,
    });
}

function run(command, args) {
  return new Promise((resolve, reject) => {
    const child = spawn(command, args, { stdio: "inherit" });
    child.once("error", reject);
    child.once("exit", (code) => {
      if (code === 0) resolve();
      else reject(new Error(`${command} exited with code ${code ?? "unknown"}.`));
    });
  });
}

export async function convertDocumentTexturesToKtx2(document, temporaryPrefix) {
  const textures = document.getRoot().listTextures();
  if (textures.length === 0) throw new Error("Asset contains no textures to convert.");

  const temporaryDirectory = await mkdtemp(path.join(tmpdir(), temporaryPrefix));
  try {
    for (const [index, texture] of textures.entries()) {
      const sourceImage = texture.getImage();
      if (!sourceImage) throw new Error(`Texture ${texture.getName() || index} contains no image data.`);

      const pngPath = path.join(temporaryDirectory, `texture-${index}.png`);
      const ktx2Path = path.join(temporaryDirectory, `texture-${index}.ktx2`);
      await writeFile(pngPath, await sharp(sourceImage).png().toBuffer());
      await run(TOKTX_COMMAND, [
        "--t2",
        "--encode", "uastc",
        "--uastc_quality", "4",
        "--zcmp", "18",
        "--threads", process.env.TOKTX_THREADS ?? "4",
        "--genmipmap",
        "--assign_oetf", "srgb",
        "--assign_primaries", "bt709",
        ktx2Path,
        pngPath,
      ]);

      texture
        .setName(`${texture.getName() || `texture_${index}`}_uastc`)
        .setMimeType("image/ktx2")
        .setImage(new Uint8Array(await readFile(ktx2Path)));
    }
    document.getRoot().listExtensionsUsed()
      .find((extension) => extension.extensionName === EXTTextureWebP.EXTENSION_NAME)
      ?.dispose();
    document.createExtension(KHRTextureBasisu).setRequired(true);
  } finally {
    await rm(temporaryDirectory, { recursive: true, force: true });
  }
}
