"use client";

const MANIFEST_URL = "/api/leptos-tiptap-runtime/manifest.json";

type RuntimeManifest = {
  bridgeRuntimePath: string | null;
  extensionModulePaths: string[];
  generatedRootPath: string | null;
  entryScriptPath: string | null;
  wasmPath: string | null;
};

type BridgeSuccess<T> = {
  ok: true;
  value: T;
};

type BridgeFailure = {
  ok: false;
  error: {
    kind: string;
    message: string;
    operation?: string;
  };
};

type BridgeResult<T> = BridgeSuccess<T> | BridgeFailure;

type BridgeRuntimeModule = {
  init_bridge_runtime: () => void;
  create: (
    request: unknown,
    onReady: (payload: { generation: number }) => void,
    onChange: () => void,
    onSelectionChange: (selection: Record<string, unknown>) => void,
    onError: (error: { kind: string; message: string; operation?: string }) => void,
  ) => void;
  destroy: (id: string) => void;
  command: (request: unknown) => BridgeResult<{ kind: "empty" }>;
  document: (request: unknown) => BridgeResult<{ kind: "content"; content: { format: "json" | "html"; value: unknown } }>;
};

export type LoadedLeptosTiptapRuntime = {
  manifest: RuntimeManifest;
  bridge: BridgeRuntimeModule;
};

let runtimePromise: Promise<LoadedLeptosTiptapRuntime> | null = null;

function formatRuntimeLoaderError(stage: string, error: unknown): Error {
  const message = error instanceof Error ? error.message : "未知错误";
  return new Error(`[leptos_tiptap_runtime_loader:${stage}] ${message}`);
}

function toRuntimeAssetUrl(relativePath: string): string {
  return `/api/leptos-tiptap-runtime/${relativePath}`;
}

async function importRuntimeModule<T>(relativePath: string): Promise<T> {
  return import(/* webpackIgnore: true */ toRuntimeAssetUrl(relativePath)) as Promise<T>;
}

function extractRegisterFunction(module: Record<string, unknown>): (() => void) | null {
  for (const value of Object.values(module)) {
    if (typeof value === "function") {
      return value as () => void;
    }
  }
  return null;
}

async function fetchRuntimeManifest(): Promise<RuntimeManifest> {
  const response = await fetch(MANIFEST_URL, { cache: "no-store" }).catch((error) => {
    throw formatRuntimeLoaderError("manifest_fetch", error);
  });
  if (!response.ok) {
    throw new Error(
      `[leptos_tiptap_runtime_loader:manifest_fetch] 读取 leptos-tiptap runtime manifest 失败（${response.status}）`,
    );
  }
  const manifest = (await response.json().catch((error) => {
    throw formatRuntimeLoaderError("manifest_parse", error);
  })) as RuntimeManifest;
  if (!manifest.bridgeRuntimePath) {
    throw new Error("[leptos_tiptap_runtime_loader:manifest_validate] runtime manifest 缺少 bridgeRuntimePath");
  }
  return manifest;
}

async function loadRuntimeModules(): Promise<LoadedLeptosTiptapRuntime> {
  const manifest = await fetchRuntimeManifest().catch((error) => {
    throw formatRuntimeLoaderError("manifest", error);
  });
  const bridge = await importRuntimeModule<BridgeRuntimeModule>(manifest.bridgeRuntimePath!).catch((error) => {
    throw formatRuntimeLoaderError("bridge_import", error);
  });
  try {
    bridge.init_bridge_runtime();
  } catch (error) {
    throw formatRuntimeLoaderError("bridge_init", error);
  }

  for (const modulePath of manifest.extensionModulePaths) {
    const extensionModule = await importRuntimeModule<Record<string, unknown>>(modulePath).catch((error) => {
      throw formatRuntimeLoaderError(`extension_import:${modulePath}`, error);
    });
    const register = extractRegisterFunction(extensionModule);
    try {
      register?.();
    } catch (error) {
      throw formatRuntimeLoaderError(`extension_register:${modulePath}`, error);
    }
  }

  return { manifest, bridge };
}

export async function loadLeptosTiptapRuntime(): Promise<LoadedLeptosTiptapRuntime> {
  if (!runtimePromise) {
    runtimePromise = loadRuntimeModules().catch((error) => {
      runtimePromise = null;
      throw error;
    });
  }
  return runtimePromise;
}
