import type { KdlInitOptions } from "./types.js";

export interface NativeKdlModule {
  ccall: (
    ident: string,
    returnType: string | null,
    argTypes: Array<string | null>,
    args: unknown[]
  ) => unknown;
  cwrap?: (
    ident: string,
    returnType: string | null,
    argTypes: Array<string | null>
  ) => (...args: unknown[]) => unknown;
  UTF8ToString?: (ptr: number) => string;
  stringToUTF8?: (value: string, outPtr: number, maxBytesToWrite: number) => void;
  lengthBytesUTF8?: (value: string) => number;
  _malloc?: (size: number) => number;
  _free?: (ptr: number) => void;
  HEAPF64?: Float64Array;
}

export type NativeKdlModuleLoader = (options: KdlInitOptions) => Promise<NativeKdlModule>;

interface NativeKdlModuleFactoryOptions {
  locateFile?: (path: string, prefix: string) => string;
  print?: (text: string) => void;
  printErr?: (text: string) => void;
}

type NativeKdlModuleFactory = (
  options?: NativeKdlModuleFactoryOptions
) => Promise<NativeKdlModule>;

export function createDefaultNativeKdlModuleLoader(defaultWrapperUrl?: string): NativeKdlModuleLoader {
  return async (options) => {
    const wrapperUrl =
      options.wrapperUrl ?? defaultWrapperUrl ?? new URL("../../../build-wasm/kdl.js", import.meta.url).href;
    const imported = (await import(/* @vite-ignore */ wrapperUrl)) as {
      default?: NativeKdlModuleFactory;
      createKdlModule?: NativeKdlModuleFactory;
    };
    const factory = imported.default ?? imported.createKdlModule;

    if (typeof factory !== "function") {
      throw new Error(`KDL WASM wrapper did not export a module factory: ${wrapperUrl}`);
    }

    return factory({
      locateFile: (path, prefix) => {
        if (path.endsWith(".wasm") && options.wasmUrl) {
          return options.wasmUrl;
        }
        return new URL(path, prefix || wrapperUrl).href;
      }
    });
  };
}
