import init, {
  Options,
  Diagnostic,
  DefinedValue,
  InlayHint,
  HoverResult,
  ConversionOptions,
} from "./mq_wasm";

// Type definitions for WASM module
interface WasmModule {
  run: (code: string, content: string, options: Options) => Promise<string>;
  toAst: (code: string) => Promise<string>;
  format: (code: string) => Promise<string>;
  diagnostics: (
    code: string,
    enableTypeCheck?: boolean,
  ) => Promise<readonly Diagnostic[]>;
  inlayHints: (code: string) => Promise<readonly InlayHint[]>;
  hover: (
    code: string,
    line: number,
    column: number,
  ) => Promise<HoverResult | null>;
  definedValues: (
    code: string,
    module?: string,
  ) => Promise<readonly DefinedValue[]>;
  htmlToMarkdown(
    html_input: string,
    options?: ConversionOptions,
  ): Promise<string>;
  toHtml(markdown_input: string): Promise<string>;
}

let wasmModule: WasmModule | null = null;

async function initWasm(): Promise<WasmModule> {
  if (wasmModule) {
    return wasmModule;
  }

  await (async () => {
    try {
      await init();
      const wasmImport = await import("./mq_wasm.js");

      wasmModule = {
        run: wasmImport.run,
        toAst: wasmImport.toAst,
        format: wasmImport.format,
        diagnostics: wasmImport.diagnostics,
        inlayHints: wasmImport.inlayHints,
        hover: wasmImport.hover,
        definedValues: wasmImport.definedValues,
        htmlToMarkdown: wasmImport.htmlToMarkdown,
        toHtml: wasmImport.toHtml,
      };
    } catch (error) {
      throw new Error(`Failed to initialize mq WebAssembly module: ${error}`);
    }
  })();

  return wasmModule!;
}

/**
 * Run an mq
 */
export async function run(
  code: string,
  content: string,
  options: Partial<Options> = {},
): Promise<string> {
  const wasm = await initWasm();
  return await wasm.run(code, content, {
    isUpdate: false,
    inputFormat: "markdown",
    listStyle: "dash",
    linkUrlStyle: "none",
    linkTitleStyle: "paren",
    ...options,
  });
}

/**
 * Convert mq code to its AST (Abstract Syntax Tree) representation.
 */
export async function toAst(code: string): Promise<string> {
  const wasm = await initWasm();
  return await wasm.toAst(code);
}

/**
 * Format mq code
 */
export async function format(code: string): Promise<string> {
  const wasm = await initWasm();
  return await wasm.format(code);
}

/**
 * Get diagnostics for mq code
 */
export async function diagnostics(
  code: string,
  enableTypeCheck?: boolean,
): Promise<ReadonlyArray<Diagnostic>> {
  const wasm = await initWasm();
  return await wasm.diagnostics(code, enableTypeCheck);
}

/**
 * Get inlay type hints for mq code
 */
export async function inlayHints(
  code: string,
): Promise<ReadonlyArray<InlayHint>> {
  const wasm = await initWasm();
  return await wasm.inlayHints(code);
}

/**
 * Get defined values from mq code
 */
export async function definedValues(
  code: string,
  module?: string,
): Promise<ReadonlyArray<DefinedValue>> {
  const wasm = await initWasm();
  return await wasm.definedValues(code, module);
}

/**
 * Get hover information for the symbol at the given position (1-based line and column).
 *
 * Returns `null` when no symbol exists at the position.
 */
export async function hover(
  code: string,
  line: number,
  column: number,
): Promise<HoverResult | null> {
  const wasm = await initWasm();
  return await wasm.hover(code, line, column);
}

/**
 * Converts HTML input to Markdown
 */
export async function htmlToMarkdown(
  html: string,
  options?: ConversionOptions,
): Promise<string> {
  const wasm = await initWasm();
  return await wasm.htmlToMarkdown(html, options);
}

/**
 * Markdown Input to HTML
 */
export async function toHtml(markdownInput: string): Promise<string> {
  const wasm = await initWasm();
  return await wasm.toHtml(markdownInput);
}
