import { mkdir, writeFile } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
import sharp from "sharp";
import { createGameAssetIO } from "./lib/ktx2.mjs";

const repositoryRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const sourcePath = path.join(repositoryRoot, "game_assets/models/claudecraft/chars/players/mage.glb");
const outputPath = path.join(repositoryRoot, "game_assets/textures/claudecraft/chars/players/priest-vestments.webp");

function clamp01(value) {
  return Math.max(0, Math.min(1, value));
}

function luminance(red, green, blue) {
  return (red * 0.2126 + green * 0.7152 + blue * 0.0722) / 255;
}

function paintSwatch(pixels, width, channels, rectangle, shadow, highlight) {
  const samples = [];
  for (let y = rectangle.y; y < rectangle.y + rectangle.height; y += 1) {
    for (let x = rectangle.x; x < rectangle.x + rectangle.width; x += 1) {
      const offset = (y * width + x) * channels;
      if (pixels[offset + 3] === 0) continue;
      samples.push(luminance(pixels[offset], pixels[offset + 1], pixels[offset + 2]));
    }
  }
  const minimum = Math.min(...samples);
  const maximum = Math.max(...samples);
  const range = Math.max(0.001, maximum - minimum);

  for (let y = rectangle.y; y < rectangle.y + rectangle.height; y += 1) {
    for (let x = rectangle.x; x < rectangle.x + rectangle.width; x += 1) {
      const offset = (y * width + x) * channels;
      if (pixels[offset + 3] === 0) continue;
      const sourceLuminance = luminance(pixels[offset], pixels[offset + 1], pixels[offset + 2]);
      const mix = clamp01((sourceLuminance - minimum) / range);
      for (let channel = 0; channel < 3; channel += 1) {
        pixels[offset + channel] = Math.round(shadow[channel] + (highlight[channel] - shadow[channel]) * mix);
      }
    }
  }
}

const io = await createGameAssetIO();
const document = await io.read(sourcePath);
const sourceTexture = document.getRoot().listTextures().find((texture) => texture.getName() === "mage_texture");
if (!sourceTexture?.getImage()) throw new Error("Mage source is missing mage_texture.");

const decoded = await sharp(sourceTexture.getImage()).ensureAlpha().raw().toBuffer({ resolveWithObject: true });
const { width, height, channels } = decoded.info;
if (width !== 512 || height !== 512 || channels !== 4) {
  throw new Error(`Unexpected mage palette shape: ${width}x${height}x${channels}.`);
}

const pixels = new Uint8Array(decoded.data);
paintSwatch(pixels, width, channels, { x: 0, y: 128, width: 128, height: 128 }, [48, 58, 94], [244, 239, 211]);
paintSwatch(pixels, width, channels, { x: 128, y: 128, width: 64, height: 128 }, [105, 66, 18], [255, 218, 109]);
paintSwatch(pixels, width, channels, { x: 64, y: 256, width: 64, height: 128 }, [105, 66, 18], [255, 218, 109]);
paintSwatch(pixels, width, channels, { x: 128, y: 256, width: 64, height: 128 }, [37, 71, 101], [157, 215, 221]);

await mkdir(path.dirname(outputPath), { recursive: true });
await writeFile(outputPath, await sharp(pixels, { raw: { width, height, channels } })
  .webp({ quality: 92, smartSubsample: true })
  .toBuffer());
console.log(`Built ${path.relative(repositoryRoot, outputPath)}`);
