import * as wasmModule from "./flock_wasm_bg.wasm";
import * as wasmBg from "./flock_wasm_bg.js";
import { __wbg_set_wasm } from "./flock_wasm_bg.js";

export * from "./flock_wasm_bg.js";

let wasm;
let initPromise;

function isRecordLike(value) {
  return (typeof value === "object" || typeof value === "function") && value !== null;
}

function toWasmExports(value) {
  if (value instanceof WebAssembly.Instance) {
    return value.exports;
  }
  if (!isRecordLike(value)) {
    return undefined;
  }
  if (value.memory instanceof WebAssembly.Memory) {
    return value;
  }
  return undefined;
}

function pickExistingWasmExports(value) {
  const queue = [value];
  const seen = new Set();
  while (queue.length > 0) {
    const current = queue.shift();
    const exports = toWasmExports(current);
    if (exports !== undefined) {
      return exports;
    }
    if (!isRecordLike(current)) {
      continue;
    }
    if (seen.has(current)) {
      continue;
    }
    seen.add(current);
    if ("default" in current) {
      queue.push(current.default);
    }
    if ("instance" in current) {
      queue.push(current.instance);
    }
    if ("module" in current) {
      queue.push(current.module);
    }
    if ("exports" in current) {
      queue.push(current.exports);
    }
  }
  return undefined;
}

function resolveModuleSource(moduleSource) {
  if (typeof moduleSource !== "string") {
    return moduleSource;
  }
  if (typeof URL !== "function") {
    return moduleSource;
  }
  try {
    return new URL(moduleSource);
  } catch (_error) {
    return new URL(moduleSource, import.meta.url);
  }
}

async function readFileUrlBytes(fileUrl) {
  const { readFile } = await import("node:fs/promises");
  const { fileURLToPath } = await import("node:url");
  const filePath = fileURLToPath(fileUrl);
  return readFile(filePath);
}

async function instantiateWasm(moduleSource) {
  if (moduleSource && typeof moduleSource.then === "function") {
    moduleSource = await moduleSource;
  }

  const existing = pickExistingWasmExports(moduleSource);
  if (existing !== undefined) {
    return existing;
  }

  // Some bundlers expose wasm assets as namespace objects with a default export.
  if (isRecordLike(moduleSource)) {
    const nestedCandidates = [
      "default",
      "module_or_path",
      "module",
      "wasm",
      "url",
    ];
    for (const key of nestedCandidates) {
      if (!(key in moduleSource)) {
        continue;
      }
      const nested = moduleSource[key];
      if (nested === undefined || nested === moduleSource) {
        continue;
      }
      return instantiateWasm(nested);
    }
  }

  const imports = { "./flock_wasm_bg.js": wasmBg };

  if (moduleSource instanceof WebAssembly.Module) {
    return new WebAssembly.Instance(moduleSource, imports).exports;
  }

  if (
    moduleSource instanceof ArrayBuffer ||
    ArrayBuffer.isView(moduleSource)
  ) {
    const result = await WebAssembly.instantiate(moduleSource, imports);
    if (result instanceof WebAssembly.Instance) {
      return result.exports;
    }
    return result.instance.exports;
  }

  if (
    typeof Response === "function" &&
    moduleSource instanceof Response
  ) {
    if (typeof WebAssembly.instantiateStreaming === "function") {
      try {
        const responseForStreaming = moduleSource.clone();
        const streaming = await WebAssembly.instantiateStreaming(
          responseForStreaming,
          imports,
        );
        return streaming.instance.exports;
      } catch (_error) {
        // Fall back to arrayBuffer in runtimes with strict MIME handling.
      }
    }
    const bytes = await moduleSource.arrayBuffer();
    const result = await WebAssembly.instantiate(bytes, imports);
    if (result instanceof WebAssembly.Instance) {
      return result.exports;
    }
    return result.instance.exports;
  }

  if (
    typeof moduleSource === "string" ||
    (typeof URL === "function" && moduleSource instanceof URL) ||
    (typeof Request === "function" && moduleSource instanceof Request)
  ) {
    let requestTarget = moduleSource;
    if (typeof moduleSource === "string") {
      requestTarget = resolveModuleSource(moduleSource);
    }
    if (typeof URL === "function" && requestTarget instanceof URL && requestTarget.protocol === "file:") {
      const bytes = await readFileUrlBytes(requestTarget);
      return instantiateWasm(bytes);
    }
    if (typeof fetch !== "function") {
      throw new TypeError("fetch is not available to load the wasm module");
    }
    return instantiateWasm(fetch(requestTarget));
  }

  throw new TypeError("Unsupported wasm module shape for flock-wasm bundler init");
}

async function __wbg_init(moduleOrExports = wasmModule) {
  if (wasm !== undefined) {
    return wasm;
  }
  if (initPromise !== undefined) {
    return initPromise;
  }
  initPromise = (async () => {
    const resolved = await instantiateWasm(moduleOrExports);
    __wbg_set_wasm(resolved);
    if (typeof resolved.__wbindgen_start === "function") {
      resolved.__wbindgen_start();
    }
    wasm = resolved;
    return wasm;
  })();
  try {
    return await initPromise;
  } catch (error) {
    initPromise = undefined;
    throw error;
  }
}

export default __wbg_init;
