// App-wide store of open floating chat windows (the Messenger-style dock, bottom-right).
// A window can be opened from anywhere (a conversation's "pop out" button, or automatically
// when a message arrives) and stays open across page navigation. The dock state (which chats
// are open + their minimised state) is persisted per user so it survives a reload.
import { useSyncExternalStore } from "react";
import { LS } from "./storage";

export interface DockChat {
  partnerId: number;
  name: string;
  avatar: string | null;
  minimized: boolean;
  flash: number; // bumped to trigger a one-shot attention flash on the window
}

type PartnerLike = { id: number; name: string; avatar_url: string | null };

const MAX_OPEN = 3; // keep the dock from overflowing the corner
let chats: DockChat[] = [];
let userId: number | null = null;
const subs = new Set<() => void>();

function storageKey(): string | null {
  return userId != null ? LS.chatDock(userId) : null;
}

function persist(): void {
  const k = storageKey();
  if (!k) return;
  try {
    // Don't persist the transient flash counter.
    localStorage.setItem(k, JSON.stringify(chats.map(({ flash, ...c }) => c)));
  } catch {
    /* ignore quota/serialization errors */
  }
}

function emit(next: DockChat[]): void {
  chats = next;
  persist();
  for (const s of subs) s();
}

// Bind the dock to the signed-in user and restore their persisted windows (once per user).
export function initDock(uid: number): void {
  if (userId === uid) return;
  userId = uid;
  let restored: DockChat[] = [];
  try {
    const raw = localStorage.getItem(storageKey()!);
    if (raw) restored = (JSON.parse(raw) as Omit<DockChat, "flash">[]).map((c) => ({ ...c, flash: 0 }));
  } catch {
    restored = [];
  }
  chats = restored.slice(0, MAX_OPEN);
  for (const s of subs) s();
}

export function openChat(partner: PartnerLike): void {
  const existing = chats.find((c) => c.partnerId === partner.id);
  if (existing) {
    // Re-opening focuses it: ensure expanded and move to the front.
    emit([
      { ...existing, minimized: false },
      ...chats.filter((c) => c.partnerId !== partner.id),
    ]);
    return;
  }
  emit(
    [
      { partnerId: partner.id, name: partner.name, avatar: partner.avatar_url, minimized: false, flash: 0 },
      ...chats,
    ].slice(0, MAX_OPEN)
  );
}

// An incoming message arrived from `partner`: open the window if it's closed, otherwise just
// flash it (whether expanded or minimised — don't disturb the minimised state).
export function notifyIncoming(partner: PartnerLike): void {
  const existing = chats.find((c) => c.partnerId === partner.id);
  if (!existing) {
    emit(
      [
        { partnerId: partner.id, name: partner.name, avatar: partner.avatar_url, minimized: false, flash: 1 },
        ...chats,
      ].slice(0, MAX_OPEN)
    );
    return;
  }
  emit(chats.map((c) => (c.partnerId === partner.id ? { ...c, flash: c.flash + 1 } : c)));
}

export function closeChat(partnerId: number): void {
  emit(chats.filter((c) => c.partnerId !== partnerId));
}

export function toggleMinimize(partnerId: number): void {
  emit(chats.map((c) => (c.partnerId === partnerId ? { ...c, minimized: !c.minimized } : c)));
}

export function useDockChats(): DockChat[] {
  return useSyncExternalStore(
    (cb) => {
      subs.add(cb);
      return () => subs.delete(cb);
    },
    () => chats,
    () => chats
  );
}
