import { mkdir, stat } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { NodeIO } from "@gltf-transform/core";
import { EXTMeshoptCompression, EXTTextureWebP, KHRMeshQuantization } from "@gltf-transform/extensions";
import { MeshoptDecoder, MeshoptEncoder } from "meshoptimizer";

const rootDirectory = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const playerDirectory = path.join(rootDirectory, "game_assets/models/claudecraft/chars/players");
const outputDirectory = path.join(rootDirectory, ".tmp/pruned-party-models");
const writeInPlace = process.argv.includes("--write");

const REQUIRED_CLIPS = {
  druid: ["Death_A", "Hit_A", "Idle", "Running_A", "Walking_A", "Spellcasting", "2H_Melee_Attack_Chop"],
  knight: ["Death_A", "Hit_A", "Idle", "Running_A", "Walking_A", "Spellcasting", "1H_Melee_Attack_Chop"],
  ranger: ["Death_A", "Hit_A", "Idle", "Running_A", "Walking_A", "Spellcasting", "2H_Ranged_Shoot"],
  mage: ["Death_A", "Hit_A", "Idle", "Running_A", "Walking_A", "Spellcasting", "Spellcast_Shoot"],
  rogue: ["Death_A", "Hit_A", "Idle", "Running_A", "Walking_A", "Spellcasting", "Dualwield_Melee_Attack_Chop"],
};

await MeshoptEncoder.ready;
const io = new NodeIO()
  .registerExtensions([EXTMeshoptCompression, EXTTextureWebP, KHRMeshQuantization])
  .registerDependencies({
    "meshopt.decoder": MeshoptDecoder,
    "meshopt.encoder": MeshoptEncoder,
  });

if (!writeInPlace) await mkdir(outputDirectory, { recursive: true });

for (const [modelName, requiredClips] of Object.entries(REQUIRED_CLIPS)) {
  const inputPath = path.join(playerDirectory, `${modelName}.glb`);
  const document = await io.read(inputPath);
  const animations = document.getRoot().listAnimations();
  const available = new Set(animations.map((animation) => animation.getName()));
  const missing = requiredClips.filter((clip) => !available.has(clip));
  if (missing.length) throw new Error(`${modelName}.glb is missing required clips: ${missing.join(", ")}`);

  for (const animation of animations) {
    if (!requiredClips.includes(animation.getName())) animation.dispose();
  }
  const outputPath = writeInPlace ? inputPath : path.join(outputDirectory, `${modelName}.glb`);
  await io.write(outputPath, document);
  const sourceSize = (await stat(inputPath)).size;
  const outputSize = (await stat(outputPath)).size;
  console.log(`${path.basename(outputPath)}: ${sourceSize} → ${outputSize} bytes (${requiredClips.length} clips)`);
}
