import type { JsonValue } from "../types";

type LiveContentListener = (cardId: string, snapshot: JsonValue | undefined) => void;
type SelectionListener = (cardId: string, selection: JsonValue | undefined) => void;

class CardRuntimeRegistry {
  private readonly liveContentStore = new Map<string, JsonValue>();
  private readonly selectionStore = new Map<string, JsonValue>();
  private readonly refreshHandlers = new Map<string, () => void>();
  private readonly liveContentListeners = new Set<LiveContentListener>();
  private readonly selectionListeners = new Set<SelectionListener>();

  private emitLiveContent(cardId: string, snapshot: JsonValue | undefined): void {
    for (const listener of this.liveContentListeners) listener(cardId, cloneJsonValue(snapshot));
  }

  private emitSelection(cardId: string, selection: JsonValue | undefined): void {
    for (const listener of this.selectionListeners) listener(cardId, cloneJsonValue(selection));
  }

  setCardLiveContent(
    cardId: string | null | undefined,
    snapshot: JsonValue | null | undefined,
  ): void {
    const key = String(cardId || "").trim();
    if (!key) return;
    const cloned = cloneJsonValue(snapshot ?? undefined);
    if (cloned === undefined) {
      this.liveContentStore.delete(key);
      this.emitLiveContent(key, undefined);
      return;
    }
    this.liveContentStore.set(key, cloned);
    this.emitLiveContent(key, cloned);
  }

  getCardLiveContent(cardId: string | null | undefined): JsonValue | undefined {
    const key = String(cardId || "").trim();
    if (!key) return undefined;
    return cloneJsonValue(this.liveContentStore.get(key));
  }

  subscribeCardLiveContent(listener: LiveContentListener): () => void {
    this.liveContentListeners.add(listener);
    return () => {
      this.liveContentListeners.delete(listener);
    };
  }

  setCardSelection(
    cardId: string | null | undefined,
    selection: JsonValue | null | undefined,
  ): void {
    const key = String(cardId || "").trim();
    if (!key) return;
    const cloned = cloneJsonValue(selection ?? undefined);
    if (cloned === undefined) {
      this.selectionStore.delete(key);
      this.emitSelection(key, undefined);
      return;
    }
    this.selectionStore.set(key, cloned);
    this.emitSelection(key, cloned);
  }

  getCardSelection(cardId: string | null | undefined): JsonValue | undefined {
    const key = String(cardId || "").trim();
    if (!key) return undefined;
    return cloneJsonValue(this.selectionStore.get(key));
  }

  clearCardSelection(cardId: string | null | undefined): void {
    const key = String(cardId || "").trim();
    if (!key) return;
    this.selectionStore.delete(key);
    this.emitSelection(key, undefined);
  }

  subscribeCardSelection(listener: SelectionListener): () => void {
    this.selectionListeners.add(listener);
    return () => {
      this.selectionListeners.delete(listener);
    };
  }

  setCardRefreshHandler(
    cardId: string | null | undefined,
    handler: (() => void) | null | undefined,
  ): void {
    const key = String(cardId || "").trim();
    if (!key) return;
    if (typeof handler !== "function") {
      this.refreshHandlers.delete(key);
      return;
    }
    this.refreshHandlers.set(key, handler);
  }

  hasCardRefreshHandler(cardId: string | null | undefined): boolean {
    const key = String(cardId || "").trim();
    if (!key) return false;
    return this.refreshHandlers.has(key);
  }

  runCardRefresh(cardId: string | null | undefined): boolean {
    const key = String(cardId || "").trim();
    if (!key) return false;
    const handler = this.refreshHandlers.get(key);
    if (!handler) return false;
    handler();
    return true;
  }

  disposeCardRuntimeEntry(cardId: string | null | undefined): void {
    const key = String(cardId || "").trim();
    if (!key) return;
    this.liveContentStore.delete(key);
    this.selectionStore.delete(key);
    this.refreshHandlers.delete(key);
    this.emitLiveContent(key, undefined);
    this.emitSelection(key, undefined);
  }
}

const registry = new CardRuntimeRegistry();

function cloneJsonValue(value: JsonValue | undefined): JsonValue | undefined {
  if (value === undefined) return undefined;
  try {
    return JSON.parse(JSON.stringify(value)) as JsonValue;
  } catch {
    return value;
  }
}

export function setCardLiveContent(
  cardId: string | null | undefined,
  snapshot: JsonValue | null | undefined,
): void {
  registry.setCardLiveContent(cardId, snapshot);
}

export function getCardLiveContent(cardId: string | null | undefined): JsonValue | undefined {
  return registry.getCardLiveContent(cardId);
}

export function subscribeCardLiveContent(listener: LiveContentListener): () => void {
  return registry.subscribeCardLiveContent(listener);
}

export function setCardSelection(
  cardId: string | null | undefined,
  selection: JsonValue | null | undefined,
): void {
  registry.setCardSelection(cardId, selection);
}

export function getCardSelection(cardId: string | null | undefined): JsonValue | undefined {
  return registry.getCardSelection(cardId);
}

export function clearCardSelection(cardId: string | null | undefined): void {
  registry.clearCardSelection(cardId);
}

export function subscribeCardSelection(listener: SelectionListener): () => void {
  return registry.subscribeCardSelection(listener);
}

export function setCardRefreshHandler(
  cardId: string | null | undefined,
  handler: (() => void) | null | undefined,
): void {
  registry.setCardRefreshHandler(cardId, handler);
}

export function hasCardRefreshHandler(cardId: string | null | undefined): boolean {
  return registry.hasCardRefreshHandler(cardId);
}

export function runCardRefresh(cardId: string | null | undefined): boolean {
  return registry.runCardRefresh(cardId);
}

export function disposeCardRuntimeEntry(cardId: string | null | undefined): void {
  registry.disposeCardRuntimeEntry(cardId);
}

export function requestCardFeedRefresh(): void {
  if (typeof window === "undefined") return;
  window.dispatchEvent(new Event("nanobot:cards-refresh"));
}
