import { createHash } from "node:crypto";
import { copyFileSync, readFileSync, writeFileSync } from "node:fs";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";

const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const packageRoot = resolve(root, "node_modules/@rerun-io/web-viewer");
const manifest = JSON.parse(readFileSync(resolve(packageRoot, "package.json"), "utf8"));
const vendorRoot = resolve(root, "vendor/rerun-web-viewer-0.34.1");

const sha256File = (path) =>
  createHash("sha256").update(readFileSync(path)).digest("hex");

if (manifest.version !== "0.34.1") {
  throw new Error(
    `Refusing to patch @rerun-io/web-viewer ${manifest.version}; audit the new vendor lifecycle first.`,
  );
}

const vendorRuntime = [
  {
    label: "wasm runtime",
    packagePath: resolve(packageRoot, "re_viewer_bg.wasm"),
    vendorPath: resolve(vendorRoot, "re_viewer_bg.nodedc.wasm"),
    publishedSha256: "3fe7aab8ea6bb0fd03c3ef694932943411ee174029e398c539c390beb0824d35",
    previousNodedcSha256: "6aca9da47ad561d2046ad0c7c0b8a7f653f023a77132c5c6304789320db512c2",
    nodedcSha256: "ffe7543d28bb3394f289f6299de43d038767eef83d781c2b7f8f5683308a0469",
  },
  {
    label: "wasm JavaScript glue",
    packagePath: resolve(packageRoot, "re_viewer.js"),
    vendorPath: resolve(vendorRoot, "re_viewer.nodedc.js"),
    publishedSha256: "7c4ba900820137a6ba23e0f7f57d56e3b007db0de3825d5c8bf0d7684b3cc6a6",
    nodedcSha256: "0f7b76c9f24cbd8437021b5d37499894aeadc586183e422ebc82ef556d7b8339",
  },
];

for (const runtime of vendorRuntime) {
  const vendorSha256 = sha256File(runtime.vendorPath);
  if (vendorSha256 !== runtime.nodedcSha256) {
    throw new Error(
      `Refusing corrupt NODE.DC Rerun ${runtime.label}: ${vendorSha256} != ${runtime.nodedcSha256}`,
    );
  }

  const installedSha256 = sha256File(runtime.packagePath);
  if (
    ![
      runtime.publishedSha256,
      runtime.previousNodedcSha256,
      runtime.nodedcSha256,
    ].includes(installedSha256)
  ) {
    throw new Error(
      `Refusing to replace unexpected Rerun ${runtime.label}: ${installedSha256}`,
    );
  }

  copyFileSync(runtime.vendorPath, runtime.packagePath);
  if (sha256File(runtime.packagePath) !== runtime.nodedcSha256) {
    throw new Error(`NODE.DC Rerun ${runtime.label} verification failed after copy`);
  }
}

const patches = [
  {
    label: "compiled watchdog",
    path: resolve(packageRoot, "index.js"),
    before: `        function check_for_panic() {\n            if (self.#handle?.has_panicked()) {`,
    after: `        function check_for_panic() {\n            if (self.#state !== "ready" || !self.#handle) {\n                return;\n            }\n            if (self.#handle.has_panicked()) {`,
  },
  {
    label: "source watchdog",
    path: resolve(packageRoot, "index.ts"),
    before: `    function check_for_panic() {\n      if (self.#handle?.has_panicked()) {`,
    after: `    function check_for_panic() {\n      if (self.#state !== "ready" || !self.#handle) {\n        return;\n      }\n      if (self.#handle.has_panicked()) {`,
  },
  {
    label: "compiled singleton global listener",
    path: resolve(packageRoot, "index.js"),
    before: `function setupGlobalEventListeners() {\n    window.addEventListener("keyup", (e) => {`,
    after: `let globalEventListenersInstalled = false;\nfunction setupGlobalEventListeners() {\n    if (globalEventListenersInstalled) {\n        return;\n    }\n    globalEventListenersInstalled = true;\n    window.addEventListener("keyup", (e) => {`,
  },
  {
    label: "source singleton global listener",
    path: resolve(packageRoot, "index.ts"),
    before: `function setupGlobalEventListeners() {\n  window.addEventListener("keyup", (e) => {`,
    after: `let globalEventListenersInstalled = false;\nfunction setupGlobalEventListeners() {\n  if (globalEventListenersInstalled) {\n    return;\n  }\n  globalEventListenersInstalled = true;\n  window.addEventListener("keyup", (e) => {`,
  },
];

for (const patch of patches) {
  const source = readFileSync(patch.path, "utf8");
  if (source.includes(patch.after)) continue;
  if (!source.includes(patch.before)) {
    throw new Error(`Vendor ${patch.label} patch no longer matches ${patch.path}.`);
  }
  writeFileSync(patch.path, source.replace(patch.before, patch.after));
}

for (const path of [resolve(packageRoot, "index.js"), resolve(packageRoot, "index.ts")]) {
  const source = readFileSync(path, "utf8");
  const keyupListenerCount = source.split('window.addEventListener("keyup"').length - 1;
  if (!source.includes("let globalEventListenersInstalled = false;") || keyupListenerCount !== 1) {
    throw new Error(`Vendor singleton global listener verification failed for ${path}.`);
  }
}

console.log(
  "Patched @rerun-io/web-viewer 0.34.1 pointer navigation, camera continuity, watchdog teardown, and singleton global keyup listener.",
);
