import fs from "fs";
import path from "path";
import { NodeIO } from "@gltf-transform/core";
import { ALL_EXTENSIONS } from "@gltf-transform/extensions";
import { dedup, prune, resample, weld, quantize } from "@gltf-transform/functions";
import { BACKUP_SUFFIX, needsConversion } from "../../utils/gltf.js";
import { convert } from "../convert/service.js";

const io = new NodeIO().registerExtensions(ALL_EXTENSIONS);

const QUANTIZE_PRESETS = {
  high: { position: 16, normal: 12, texcoord: 14, color: 10, generic: 12 },
  balanced: { position: 14, normal: 10, texcoord: 12, color: 8, generic: 12 },
  aggressive: { position: 12, normal: 8, texcoord: 10, color: 8, generic: 10 },
  light: { position: 10, normal: 8, texcoord: 10, color: 8, generic: 10 }
};

export const buildTransforms = (config) => {
  const transforms = [];
  const selected = new Set(config.commands);
  if (selected.has("dedup")) transforms.push(dedup());
  if (selected.has("prune")) transforms.push(prune());
  if (selected.has("resample")) transforms.push(resample());
  if (selected.has("weld")) transforms.push(weld());
  if (selected.has("quantize")) {
    const preset = QUANTIZE_PRESETS[config.quantizePreset] || QUANTIZE_PRESETS.balanced;
    transforms.push(quantize({
      quantizePosition: preset.position,
      quantizeNormal: preset.normal,
      quantizeTexcoord: preset.texcoord,
      quantizeColor: preset.color,
      quantizeGeneric: preset.generic
    }));
  }
  return transforms;
};

const resolveOutput = (file, config) => {
  const cwd = process.cwd();
  const originalExt = path.extname(file).toLowerCase() || ".gltf";
  const baseName = originalExt ? file.slice(0, -originalExt.length) : file;
  const isConvertible = needsConversion(file);

  const targetExt = config.outputFormat === "gltf" ? ".gltf"
    : (config.outputFormat === "glb" || isConvertible) ? ".glb"
    : originalExt;

  const wantsCopy = config.outputOptions.includes("copy");
  const wantsOverwrite = config.outputOptions.includes("overwrite");
  const mode = wantsCopy ? "copy" : (wantsOverwrite ? "overwrite" : "copy");
  const backup = config.outputOptions.includes("backup") && mode === "overwrite";

  const targetName = mode === "copy"
    ? `${baseName}_compressed${targetExt}`
    : `${baseName}${targetExt}`;

  return {
    cwd,
    mode,
    backup,
    sourcePath: path.join(cwd, file),
    sourceExt: originalExt,
    targetExt,
    targetPath: path.join(cwd, targetName),
    baseName,
    needsConversion: isConvertible
  };
};

export const processFile = async (file, config, transforms) => {
  const output = resolveOutput(file, config);
  if (!fs.existsSync(output.sourcePath)) {
    return { ok: false, file, reason: "missing" };
  }

  if (!transforms.length) {
    return { ok: false, file, reason: "no-transform" };
  }

  if (output.backup) {
    const backupName = `${output.baseName}${BACKUP_SUFFIX}${output.sourceExt}`;
    const backupPath = path.join(output.cwd, backupName);
    fs.copyFileSync(output.sourcePath, backupPath);
  }

  let inputPath = output.sourcePath;

  if (output.needsConversion) {
    const tempPath = path.join(output.cwd, `${output.baseName}_temp.glb`);
    await convert(output.sourcePath, tempPath, { binary: true });
    inputPath = tempPath;
  }

  const document = io.read(inputPath);
  await document.transform(...transforms);
  io.write(output.targetPath, document);

  if (output.needsConversion) {
    fs.rmSync(inputPath, { force: true });
  }

  if (output.mode === "overwrite" && output.targetPath !== output.sourcePath) {
    fs.rmSync(output.sourcePath, { force: true });
  }

  return { ok: true, file, output: output.targetPath };
};
