export const NODEDC_SESSION_EVENT_TYPE = "nodedc:session:logout";
export const NODEDC_SESSION_CHANNEL_NAME = "nodedc-platform-session";
export const NODEDC_SESSION_STORAGE_KEY = "nodedc:platform-session-event";

export interface NodeDCSessionEvent {
  type: typeof NODEDC_SESSION_EVENT_TYPE;
  id: string;
  source: string;
  createdAt: number;
}

export function publishNodeDCLogoutEvent(source = "launcher") {
  if (typeof window === "undefined") return;

  const event: NodeDCSessionEvent = {
    type: NODEDC_SESSION_EVENT_TYPE,
    id: createEventId(),
    source,
    createdAt: Date.now(),
  };

  try {
    const channel = new BroadcastChannel(NODEDC_SESSION_CHANNEL_NAME);
    channel.postMessage(event);
    channel.close();
  } catch {
    void 0;
  }

  try {
    window.localStorage.setItem(NODEDC_SESSION_STORAGE_KEY, JSON.stringify(event));
  } catch {
    void 0;
  }
}

export function subscribeToNodeDCLogoutEvents(onLogout: (event: NodeDCSessionEvent) => void) {
  if (typeof window === "undefined") return () => undefined;

  let lastEventId: string | null = null;
  let channel: BroadcastChannel | null = null;

  const handleEvent = (payload: unknown) => {
    if (!isNodeDCSessionEvent(payload) || payload.id === lastEventId) return;

    lastEventId = payload.id;
    onLogout(payload);
  };

  try {
    channel = new BroadcastChannel(NODEDC_SESSION_CHANNEL_NAME);
    channel.addEventListener("message", (event) => handleEvent(event.data));
  } catch {
    channel = null;
  }

  const handleStorage = (event: StorageEvent) => {
    if (event.key !== NODEDC_SESSION_STORAGE_KEY || !event.newValue) return;

    try {
      handleEvent(JSON.parse(event.newValue));
    } catch {
      void 0;
    }
  };

  window.addEventListener("storage", handleStorage);

  return () => {
    channel?.close();
    window.removeEventListener("storage", handleStorage);
  };
}

function isNodeDCSessionEvent(value: unknown): value is NodeDCSessionEvent {
  if (!value || typeof value !== "object") return false;

  const event = value as Partial<NodeDCSessionEvent>;
  return (
    event.type === NODEDC_SESSION_EVENT_TYPE &&
    typeof event.id === "string" &&
    typeof event.source === "string" &&
    typeof event.createdAt === "number"
  );
}

function createEventId() {
  try {
    return crypto.randomUUID();
  } catch {
    return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
  }
}
