import { access } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
import {
  EXTMeshoptCompression,
} from "@gltf-transform/extensions";
import { dedup, mergeDocuments, prune, unpartition } from "@gltf-transform/functions";
import { createGameAssetIO, convertDocumentTexturesToKtx2 } from "./lib/ktx2.mjs";

const repositoryRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const sourceDirectory = path.join(
  repositoryRoot,
  "game_assets",
  "models",
  "original",
  "environment",
  "kaykit-dungeon",
);
const outputPath = path.join(sourceDirectory, "dungeon-kit-uastc.glb");
const sources = ["pillar-decorated.glb", "wall-pillar.glb", "torch-lit.glb"];
const expectedMeshNames = ["pillar_decorated", "wall_pillar", "torch_lit"];
await Promise.all(sources.map((source) => access(path.join(sourceDirectory, source))));
const io = await createGameAssetIO();

const documents = await Promise.all(sources.map((source) => io.read(path.join(sourceDirectory, source))));
const document = documents[0];
const destinationScene = document.getRoot().listScenes()[0].setName("dungeon_kit");

for (const sourceDocument of documents.slice(1)) {
  const sourceScene = sourceDocument.getRoot().listScenes()[0];
  const propertyMap = mergeDocuments(document, sourceDocument);
  const copiedScene = propertyMap.get(sourceScene);
  if (!copiedScene) throw new Error(`Could not merge scene from ${sourceScene.getName() || "unnamed source"}.`);
  for (const child of [...copiedScene.listChildren()]) destinationScene.addChild(child);
  copiedScene.dispose();
}

await document.transform(
  dedup({ keepUniqueNames: false }),
  unpartition(),
  prune({ keepSolidTextures: true }),
);

const root = document.getRoot();
const meshNames = root.listMeshes().map((mesh) => mesh.getName()).sort();
const expectedSorted = [...expectedMeshNames].sort();
if (JSON.stringify(meshNames) !== JSON.stringify(expectedSorted)) {
  throw new Error(`Dungeon kit mesh names changed: expected ${expectedSorted.join(", ")}; received ${meshNames.join(", ")}.`);
}
if (root.listTextures().length !== 1) {
  throw new Error(`Dungeon kit must contain exactly one shared texture; received ${root.listTextures().length}.`);
}
if (root.listMaterials().length !== 1) {
  throw new Error(`Dungeon kit must contain exactly one shared material; received ${root.listMaterials().length}.`);
}

await convertDocumentTexturesToKtx2(document, "iwt-dungeon-kit-");
const meshoptExtension = root.listExtensionsUsed()
  .find((extension) => extension.extensionName === EXTMeshoptCompression.EXTENSION_NAME)
  ?? document.createExtension(EXTMeshoptCompression);
meshoptExtension
  .setRequired(true)
  .setEncoderOptions({ method: EXTMeshoptCompression.EncoderMethod.QUANTIZE });

await io.write(outputPath, document);

const outputDocument = await io.read(outputPath);
const outputRoot = outputDocument.getRoot();
const outputTexture = outputRoot.listTextures()[0];
if (
  outputRoot.listMeshes().length !== 3
  || outputRoot.listMaterials().length !== 1
  || outputRoot.listTextures().length !== 1
  || outputTexture.getMimeType() !== "image/ktx2"
) {
  throw new Error("Generated dungeon kit failed structural validation.");
}

console.log(`Built ${path.relative(repositoryRoot, outputPath)}`);
console.log("Meshes: pillar_decorated, wall_pillar, torch_lit; shared textures: 1; encoding: KTX2/UASTC.");
