import fs from "fs";
import path from "path";
import color from "picocolors";
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/converters.js";
import { runInteractive, showSummary } from "./ui.js";
import { stopKeypress, waitForKey } from "../keyboard.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 }
};

function 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;
}

function 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);

  // OBJ/FBX 默认输出 GLB，除非指定了 gltf
  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
  };
}

async function processFile(file, config, transforms) {
  const output = resolveOutput(file, config);
  if (!fs.existsSync(output.sourcePath)) {
    console.log(color.yellow("跳过，文件不存在: " + file));
    return { ok: false, file, reason: "missing" };
  }

  if (!transforms.length) {
    console.log(color.yellow("未选择任何 gltf-transform 操作，已中止"));
    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;

  // 如果是 OBJ/FBX，先转换为临时 GLB
  if (output.needsConversion) {
    const tempPath = path.join(output.cwd, `${output.baseName}_temp.glb`);
    console.log(color.dim("转换中: " + file + " → 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 });
  }

  if (output.mode === "overwrite" && output.targetPath !== output.sourcePath) {
    console.log(color.dim("已生成新文件: " + path.basename(output.targetPath)));
  }

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

export async function run() {
  const result = await runInteractive();
  if (!result) return "back";

  stopKeypress();

  const { results } = result;
  const config = {
    files: results[0] || [],
    commands: results[1] || [],
    quantizePreset: results[2] || "balanced",
    outputFormat: results[3] || "auto",
    outputOptions: results[4] || []
  };

  showSummary([
    "模型文件: " + (config.files.length ? config.files.join(", ") : "未选择"),
    "gltf-transform 命令: " + (config.commands.length ? config.commands.join(", ") : "无"),
    "量化级别: " + config.quantizePreset,
    "输出格式: " + config.outputFormat,
    "输出选项: " + (config.outputOptions.length ? config.outputOptions.join(", ") : "默认")
  ]);

  if (!config.files.length) {
    console.log(color.yellow("未选择任何模型文件"));
    await waitForKey();
    return "back";
  }

  const transforms = buildTransforms(config);
  if (!transforms.length) {
    console.log(color.yellow("未配置任何压缩命令，请至少选择一项 gltf-transform 操作"));
    await waitForKey();
    return "back";
  }

  const total = config.files.length;
  let success = 0;
  const failed = [];

  console.log(color.cyan(`开始压缩 ${total} 个文件...\n`));

  for (let i = 0; i < config.files.length; i++) {
    const file = config.files[i];
    const progress = `[${i + 1}/${total}]`;
    console.log(color.dim(`${progress} 处理中: ${file}`));

    try {
      const result = await processFile(file, config, transforms);
      if (result.ok) {
        success++;
        console.log(color.green(`${progress} ✓ ${file} → ${path.basename(result.output)}`));
      } else {
        failed.push(file);
        console.log(color.yellow(`${progress} ⊘ 跳过: ${file}`));
      }
    } catch (err) {
      failed.push(file);
      console.log(color.red(`${progress} ✖ 失败: ${file}`));
      console.log(color.dim("   " + String(err?.message || err)));
    }
  }

  console.log("\n" + color.bgGreen(color.black(" 压缩完成 ")));
  if (success) {
    console.log(color.green(`成功: ${success} 个`));
  }
  if (failed.length) {
    console.log(color.yellow(`失败: ${failed.length} 个 (${failed.join(", ")})`));
  }

  await waitForKey();
  return "back";
}
