import { createSignal, onCleanup, onMount } from "solid-js";
import type { View } from "../pages/registry";
import { createPageLifecycleDispatcher, type PageLifecycleEventType } from "../sdk/lifecycle";
import { isHostMessage, type HostSharedState, type PageCommand } from "../sdk/protocol";
import { notifyReady, sendCommand } from "../sdk/page";

/**
 * Connects one iframe document to the persistent Host Shell.
 *
 * A keep-alive document remains mounted while inactive and exposes that state
 * through `active`. Host `dispose`, browser `pagehide` and Solid cleanup all
 * converge on one deduplicated SDK unload transition. The document boundary
 * itself remains the final guarantee that an evicted Page releases its realm.
 */
export function createFrameBridge(view: View) {
  const [state, setState] = createSignal<HostSharedState | null>(null);
  const [active, setActive] = createSignal(false);
  const pageLifecycle = createPageLifecycleDispatcher();

  const receive = (event: MessageEvent<unknown>) => {
    if (event.origin !== window.location.origin || event.source !== window.parent) return;
    if (!isHostMessage(event.data)) return;
    if (event.data.type === "state" && event.data.state.view === view) {
      setState(event.data.state);
    }
    if (event.data.type === "lifecycle") {
      const phase = event.data.phase;
      const lifecycleEvent = pageLifecycle.dispatch(eventForPhase(phase), "host");
      if (!lifecycleEvent) return;
      setActive(lifecycleEvent.current === "visible");

      // Preserve the original DOM events for existing pages. New code should
      // use the typed SDK lifecycle returned by this bridge.
      window.dispatchEvent(
        new CustomEvent("wasmeld:lifecycle", {
          detail: { phase },
        }),
      );
      window.dispatchEvent(new Event(`wasmeld:${phase}`));
    }
  };

  onMount(() => {
    window.addEventListener("message", receive);
    const unload = (event: PageTransitionEvent) => {
      // A persisted document is frozen in the back-forward cache, not unloaded.
      // Its Host and iframe will resume together when the browser restores it.
      if (event.persisted) return;
      if (pageLifecycle.dispatch("unload", "document")) setActive(false);
    };
    window.addEventListener("pagehide", unload);
    notifyReady(view);
    onCleanup(() => window.removeEventListener("pagehide", unload));
  });

  onCleanup(() => {
    window.removeEventListener("message", receive);
    pageLifecycle.dispatch("unload", "document");
  });

  return {
    state,
    active,
    lifecycle: pageLifecycle.lifecycle,
    command(command: PageCommand) {
      sendCommand(view, command);
    },
  };
}

function eventForPhase(phase: "activate" | "deactivate" | "dispose"): PageLifecycleEventType {
  switch (phase) {
    case "activate":
      return "show";
    case "deactivate":
      return "hide";
    case "dispose":
      return "unload";
  }
}
