import type { JsonValue } from "../types";
import type { RuntimeItem, RuntimeModule } from "./runtimeTypes";
import { runtimeItemId } from "./runtimeUtils";

const runtimeAssetCache = new Map<
  string,
  Promise<{ html: string | null; module: RuntimeModule } | null>
>();

function runtimeTemplateUrl(templateKey: string, filename: string): string {
  return `/card-templates/${encodeURIComponent(templateKey)}/${filename}`;
}

function escapeAttribute(value: string): string {
  return value
    .replace(/&/g, "&amp;")
    .replace(/"/g, "&quot;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;");
}

function jsonScriptText(payload: Record<string, JsonValue>): string {
  return JSON.stringify(payload, null, 0).replace(/<\//g, "<\\/");
}

export function materializeRuntimeHtml(
  templateHtml: string,
  item: RuntimeItem,
  state: Record<string, JsonValue>,
): string {
  const cardId = escapeAttribute(runtimeItemId(item));
  const templateKey = escapeAttribute(item.templateKey?.trim() || "");
  return [
    `<div data-nanobot-card-root data-card-id="${cardId}" data-template-key="${templateKey}">`,
    `<script type="application/json" data-card-state>${jsonScriptText(state)}</script>`,
    templateHtml,
    "</div>",
  ].join("");
}

export function syncRuntimeStateScript(
  root: HTMLDivElement | null,
  state: Record<string, JsonValue>,
): void {
  const stateEl = root?.querySelector('script[data-card-state][type="application/json"]');
  if (!(stateEl instanceof HTMLScriptElement)) return;
  stateEl.textContent = jsonScriptText(state);
}

export async function loadRuntimeAssets(
  templateKey: string,
): Promise<{ html: string | null; module: RuntimeModule } | null> {
  const cached = runtimeAssetCache.get(templateKey);
  if (cached) return cached;

  const loader = (async () => {
    const moduleUrl = runtimeTemplateUrl(templateKey, "card.js");
    const moduleProbe = await fetch(moduleUrl, { cache: "no-store" });
    if (!moduleProbe.ok) return null;

    const templatePromise = fetch(runtimeTemplateUrl(templateKey, "template.html"), {
      cache: "no-store",
    })
      .then(async (response) => (response.ok ? response.text() : null))
      .catch(() => null);

    const namespace = (await import(/* @vite-ignore */ `${moduleUrl}?runtime=1`)) as
      | RuntimeModule
      | { default?: RuntimeModule };
    const runtimeModule = ("default" in namespace ? namespace.default : namespace) as RuntimeModule;
    if (!runtimeModule || typeof runtimeModule.mount !== "function") return null;

    return {
      html: await templatePromise,
      module: runtimeModule,
    };
  })();

  runtimeAssetCache.set(templateKey, loader);
  return loader;
}
