/**
 * TSS WASM Loader
 * Loads and initializes the TSS WebAssembly module
 */

// Extend Window interface for Go WASM
declare global {
  interface Window {
    Go: new () => GoInstance;
    tssStartKeygen: (
      sessionId: string,
      partyId: string,
      partyIndex: number,
      thresholdT: number,
      thresholdN: number,
      participantsJSON: string,
      password: string,
      onMessage: (msg: string) => void,
      onProgress: (current: number, total: number) => void,
      onComplete: (result: string) => void,
      onError: (error: string) => void
    ) => { success: boolean; data?: unknown; error?: string };
    tssStartSigning: (
      sessionId: string,
      partyId: string,
      partyIndex: number,
      thresholdT: number,
      participantsJSON: string,
      saveDataJSON: string,
      messageHashB64: string,
      onMessage: (msg: string) => void,
      onProgress: (current: number, total: number) => void,
      onComplete: (result: string) => void,
      onError: (error: string) => void
    ) => { success: boolean; data?: unknown; error?: string };
    tssHandleMessage: (
      sessionId: string,
      fromPartyIndex: number,
      isBroadcast: boolean,
      payloadB64: string
    ) => { success: boolean; error?: string };
    tssStopSession: (sessionId: string) => { success: boolean; error?: string };
    tssGetVersion: () => string;
  }
}

interface GoInstance {
  argv: string[];
  env: Record<string, string>;
  exit: (code: number) => void;
  importObject: WebAssembly.Imports;
  run: (instance: WebAssembly.Instance) => Promise<void>;
}

let goInstance: GoInstance | null = null;
let wasmInstance: WebAssembly.Instance | null = null;
let isInitialized = false;
let initPromise: Promise<void> | null = null;

/**
 * Initialize the TSS WASM module
 */
export async function initTSSWasm(): Promise<void> {
  if (isInitialized) {
    return;
  }

  if (initPromise) {
    return initPromise;
  }

  initPromise = doInit();
  return initPromise;
}

async function doInit(): Promise<void> {
  try {
    // Load wasm_exec.js (Go WASM runtime)
    if (!window.Go) {
      await loadScript('/wasm/wasm_exec.js');
    }

    // Create Go instance
    goInstance = new window.Go();

    // Fetch and instantiate WASM
    const wasmResponse = await fetch('/wasm/tss.wasm');
    const wasmBuffer = await wasmResponse.arrayBuffer();

    const result = await WebAssembly.instantiate(wasmBuffer, goInstance.importObject);
    wasmInstance = result.instance;

    // Run the Go program
    goInstance.run(wasmInstance);

    // Wait for initialization
    await new Promise<void>((resolve) => {
      const checkReady = () => {
        if (typeof window.tssGetVersion === 'function') {
          resolve();
        } else {
          setTimeout(checkReady, 50);
        }
      };
      checkReady();
    });

    isInitialized = true;
    console.log('TSS WASM initialized, version:', window.tssGetVersion());
  } catch (error) {
    console.error('Failed to initialize TSS WASM:', error);
    throw error;
  }
}

/**
 * Load a script dynamically
 */
function loadScript(src: string): Promise<void> {
  return new Promise((resolve, reject) => {
    const script = document.createElement('script');
    script.src = src;
    script.onload = () => resolve();
    script.onerror = () => reject(new Error(`Failed to load script: ${src}`));
    document.head.appendChild(script);
  });
}

/**
 * Check if TSS WASM is initialized
 */
export function isTSSWasmReady(): boolean {
  return isInitialized;
}

/**
 * Get TSS WASM version
 */
export function getTSSWasmVersion(): string {
  if (!isInitialized) {
    throw new Error('TSS WASM not initialized');
  }
  return window.tssGetVersion();
}
