/**
 * PageManifest — JSON snapshot of what's on the page.
 *
 * Stored in session_state as key 'page_manifest'. Enables mechanical
 * page reconstruction in RR-7 (session resumption).
 *
 * See: ARCHITECTURE-REACTIVE-RECONSTRUCTION.md §6.5
 */

export interface PageManifestEntry {
  /** Logical component name: 'stock-ticker' */
  name: string;
  /** Version from component store */
  version: number;
  /** Canonical reference: 'component-store://stock-ticker@3' */
  codeRef: string;
  /** Bus mount path: 'chat.messages.component-stock-ticker' */
  mount: string;
  /** Fact domains consumed (for hydration in RR-10) */
  factDomains: string[];
  /** Capability metadata */
  capabilities: {
    reads: string[];
    emits: string[];
  };
  /** DOM order among components: 0, 1, 2, ... */
  position: number;
  /** Index in the canvas stream (historyEntries) — interleaves with messages. */
  historyIndex: number;
  /** Slot name in parent's render() output (null for linear chat layout) */
  slot: string | null;
}

export interface PageManifest {
  sessionId: string;
  components: PageManifestEntry[];
}

export type PageManifestEntryInput = Omit<PageManifestEntry, 'position' | 'historyIndex' | 'codeRef' | 'slot'> & {
  codeRef?: string;
  slot?: string | null;
  historyIndex?: number;
};

/** Build a canonical codeRef string. */
export function buildCodeRef(name: string, version: number): string {
  return `component-store://${name}@${version}`;
}

function cloneStringList(values: readonly string[]): string[] {
  return [...values];
}

function normalizeManifestEntry(
  entry: PageManifestEntryInput | PageManifestEntry,
  position: number,
): PageManifestEntry {
  const historyIndex = 'historyIndex' in entry && typeof entry.historyIndex === 'number'
    ? entry.historyIndex
    : position;
  return {
    name: entry.name,
    version: entry.version,
    codeRef: buildCodeRef(entry.name, entry.version),
    mount: entry.mount,
    factDomains: cloneStringList(entry.factDomains),
    capabilities: {
      reads: cloneStringList(entry.capabilities.reads),
      emits: cloneStringList(entry.capabilities.emits),
    },
    position,
    historyIndex,
    slot: entry.slot ?? null,
  };
}

/** Create a new empty manifest for a session. */
export function createEmptyManifest(sessionId: string): PageManifest {
  return { sessionId, components: [] };
}

/** Canonicalize a manifest for deterministic JSON persistence. */
export function normalizeManifest(manifest: PageManifest): PageManifest {
  return {
    sessionId: manifest.sessionId,
    components: manifest.components.map((entry, position) =>
      normalizeManifestEntry(entry, position),
    ),
  };
}

/** Add a component entry to a manifest, returning the updated manifest. */
export function addManifestEntry(
  manifest: PageManifest,
  entry: PageManifestEntryInput,
): PageManifest {
  const normalized = normalizeManifest(manifest);
  const normalizedEntry = normalizeManifestEntry(entry, normalized.components.length);
  return {
    sessionId: normalized.sessionId,
    components: [...normalized.components, normalizedEntry],
  };
}

/** Remove a component by name and re-index positions. */
export function removeManifestEntry(manifest: PageManifest, name: string): PageManifest {
  const normalized = normalizeManifest(manifest);
  const removeIndex = normalized.components.findIndex(component => component.name === name);
  const filtered = removeIndex === -1
    ? normalized.components
    : normalized.components.filter((_, index) => index !== removeIndex);
  return {
    sessionId: normalized.sessionId,
    components: filtered.map((entry, position) => normalizeManifestEntry(entry, position)),
  };
}
