import type { BackendSnapshot } from "../lib/api";
import type { ConnectionState } from "../lib/model";
import type { View } from "../pages/registry";

export const SDK_CHANNEL = "wasmeld-console";

/**
 * Compatibility version for the same-origin Host/Page document protocol.
 *
 * Additive optional messages may keep the version. Removing a field or
 * changing its meaning requires a version bump and an atomic Host/Page
 * release, which is naturally provided by the embedded Console binary.
 */
export const SDK_VERSION = 1;

export type RuntimeAction = "start" | "stop" | "restart";
export type ServiceAction = "start" | "stop" | "restart";
export type PageLifecyclePhase = "activate" | "deactivate" | "dispose";

/**
 * Control-plane state shared unconditionally between same-origin Host tabs.
 *
 * Navigation, search, dialogs, notifications and iframe caches are excluded:
 * those values describe one browser tab rather than the Wasmeld runtime.
 */
export type HostGlobalState = {
  snapshot: BackendSnapshot | null;
  connection: ConnectionState;
  apiBase: string;
  runtimeAction: RuntimeAction | null;
  serviceAction: string | null;
  deploymentAction: string | null;
};

export type HostSharedState = HostGlobalState & {
  // This is a serializable snapshot, never a Solid signal crossing realms.
  view: View;
  query: string;
};

export type PageCommand =
  | { type: "refresh" }
  | { type: "navigate"; view: View }
  | { type: "open-register" }
  | { type: "open-wit-publish" }
  | { type: "open-invoke"; serviceKey: string }
  | { type: "open-activate"; serviceKey: string }
  | { type: "runtime-action"; action: RuntimeAction }
  | { type: "service-action"; serviceKey: string; action: ServiceAction }
  | { type: "save-api-base"; value: string };

export type PageMessage =
  | {
      channel: typeof SDK_CHANNEL;
      version: typeof SDK_VERSION;
      source: "page";
      type: "ready";
      view: View;
    }
  | {
      channel: typeof SDK_CHANNEL;
      version: typeof SDK_VERSION;
      source: "page";
      type: "command";
      view: View;
      command: PageCommand;
    };

export type HostMessage =
  | {
      channel: typeof SDK_CHANNEL;
      version: typeof SDK_VERSION;
      source: "host";
      type: "state";
      state: HostSharedState;
    }
  | {
      channel: typeof SDK_CHANNEL;
      version: typeof SDK_VERSION;
      source: "host";
      type: "lifecycle";
      phase: PageLifecyclePhase;
    };

export function isPageMessage(value: unknown): value is PageMessage {
  if (!isRecord(value)) return false;
  return (
    value.channel === SDK_CHANNEL &&
    value.version === SDK_VERSION &&
    value.source === "page" &&
    (value.type === "ready" || value.type === "command")
  );
}

export function isHostMessage(value: unknown): value is HostMessage {
  if (!isRecord(value)) return false;
  if (value.channel !== SDK_CHANNEL || value.version !== SDK_VERSION || value.source !== "host") {
    return false;
  }
  if (value.type === "state") return isRecord(value.state);
  return value.type === "lifecycle" && isPageLifecyclePhase(value.phase);
}

function isPageLifecyclePhase(value: unknown): value is PageLifecyclePhase {
  return value === "activate" || value === "deactivate" || value === "dispose";
}

function isRecord(value: unknown): value is Record<string, unknown> {
  return typeof value === "object" && value !== null;
}
