import { partialPrerender } from "@wrnexus/ssr";
import {
  fillSlots,
  normalizeComponentName,
  parseComponentProps,
  readElementBody,
} from "./runtime.ts";

export interface PartialBuildModule {
  default?: unknown;
  render?: (props?: Record<string, unknown>) => string | Promise<string>;
  layout?: string | { name?: string; render?: (props?: Record<string, unknown>) => string };
  __wrnexusBuildStaticShell?: (ctx?: Record<string, unknown>) => string | Promise<string>;
}

export interface PartialBuildEntry {
  name: string;
  mod: PartialBuildModule;
}

const MOUNT_OPEN_RE =
  /<([A-Za-z][A-Za-z0-9-]*)\b([^>]*?\bdata-component="([A-Za-z0-9_-]+)"[^>]*?)(\/?)>/;

/** Expand compiler component mounts at build time using only their pure render exports. */
export async function expandStaticComponents(
  html: string,
  components: readonly PartialBuildEntry[],
  depth = 0,
): Promise<string> {
  if (depth > 15) throw new Error("WRN-PARTIAL-STATIC-DEPTH: component nesting exceeds 15");
  if (!html.includes("data-component=")) return html;
  let output = "";
  let cursor = 0;
  for (;;) {
    const match = MOUNT_OPEN_RE.exec(html.slice(cursor));
    if (!match) return output + html.slice(cursor);
    const start = cursor + match.index;
    output += html.slice(cursor, start);
    const [open, tag, attributes, name, selfClosing] = match;
    const openEnd = start + open.length;
    const body =
      selfClosing === "/" ? { inner: "", end: openEnd } : readElementBody(html, tag!, openEnd);
    const component = components.find(
      (entry) => normalizeComponentName(entry.name) === normalizeComponentName(name!),
    );
    if (!component || typeof component.mod.render !== "function") {
      throw new Error(`WRN-PARTIAL-STATIC-COMPONENT: '${name}' has no build-time renderer`);
    }
    const rendered = await component.mod.render(parseComponentProps(attributes!));
    output += await expandStaticComponents(
      fillSlots(String(rendered), body.inner, true),
      components,
      depth + 1,
    );
    cursor = body.end;
  }
}

/** Produce the body shell stored in dist; dynamic region bodies are never evaluated here. */
export async function precomputePartialStaticShell(
  page: PartialBuildModule,
  components: readonly PartialBuildEntry[],
): Promise<{ shell: string; regions: number }> {
  if (typeof page.__wrnexusBuildStaticShell !== "function") {
    throw new Error("WRN-PARTIAL-STATIC-EXPORT: compiler did not emit a static-shell renderer");
  }
  const body = await expandStaticComponents(
    String(await page.__wrnexusBuildStaticShell({})),
    components,
  );
  const result = partialPrerender(body);
  return { shell: result.shell, regions: result.regions.length };
}
