// src/utils/singleTabAudioLeader.js
// Ensures only one tab ("leader") plays sounds per bodyshop.
//
// Storage key:  localStorage["imex:sound:leader:<bodyshopId>"] = { id, ts }
// Channel:      new BroadcastChannel("imex:sound:<bodyshopId>")

const STORAGE_PREFIX = "imex:sound:leader:";
const CHANNEL_PREFIX = "imex:sound:";

const TTL_MS = 60_000; // leader expires after 60s without heartbeat
const HEARTBEAT_MS = 20_000; // leader refresh interval
const WATCHDOG_MS = 10_000; // how often non-leaders check for stale leader

const TAB_ID =
  typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).slice(2);

function channelSupported() {
  try {
    return "BroadcastChannel" in window;
  } catch {
    return false;
  }
}

function getChannel(bodyshopId) {
  if (!channelSupported() || !bodyshopId) return null;
  try {
    return new BroadcastChannel(CHANNEL_PREFIX + String(bodyshopId));
  } catch {
    return null;
  }
}

function lsKey(bodyshopId) {
  return STORAGE_PREFIX + String(bodyshopId);
}

function readLeader(bodyshopId) {
  if (!bodyshopId) return null;
  try {
    const raw = localStorage.getItem(lsKey(bodyshopId));
    if (!raw) return null;
    return JSON.parse(raw);
  } catch {
    return null;
  }
}

function writeLeader(record, bodyshopId) {
  if (!bodyshopId) return;
  try {
    localStorage.setItem(lsKey(bodyshopId), JSON.stringify(record));
    const bc = getChannel(bodyshopId);
    if (bc) {
      bc.postMessage({ type: "leader-update", payload: { ...record, bodyshopId } });
      bc.close();
    }
  } catch {
    // ignore
  }
}

function removeLeader(bodyshopId) {
  if (!bodyshopId) return;
  try {
    const cur = readLeader(bodyshopId);
    if (cur?.id === TAB_ID) {
      localStorage.removeItem(lsKey(bodyshopId));
      const bc = getChannel(bodyshopId);
      if (bc) {
        bc.postMessage({ type: "leader-removed", payload: { id: TAB_ID, bodyshopId } });
        bc.close();
      }
    }
  } catch {
    // ignore
  }
}

function now() {
  return Date.now();
}

function isStale(rec) {
  return !rec || now() - rec.ts > TTL_MS;
}

function claimLeadership(bodyshopId) {
  const rec = { id: TAB_ID, ts: now() };
  writeLeader(rec, bodyshopId);
  return rec;
}

/** Is THIS tab currently the leader (and not stale)? */
export function isLeaderTab(bodyshopId) {
  const rec = readLeader(bodyshopId);
  return !!rec && rec.id === TAB_ID && !isStale(rec);
}

/** Force this tab to become the leader right now. */
export function claimLeadershipNow(bodyshopId) {
  return claimLeadership(bodyshopId);
}

/**
 * Initialize leader election/heartbeat for this tab (scoped by bodyshopId).
 * Call once (e.g., in SoundWrapper). Returns a cleanup function.
 */
export function initSingleTabAudioLeader(bodyshopId) {
  if (!bodyshopId)
    return () => {
      //
    };

  // If no leader or stale, try to claim after a tiny delay (reduce startup contention)
  if (isStale(readLeader(bodyshopId))) {
    setTimeout(() => claimLeadership(bodyshopId), 100);
  }

  // If this tab becomes focused/visible, it can claim leadership
  const onFocus = () => claimLeadership(bodyshopId);
  const onVis = () => {
    if (document.visibilityState === "visible") claimLeadership(bodyshopId);
  };
  window.addEventListener("focus", onFocus);
  document.addEventListener("visibilitychange", onVis);

  // Heartbeat from the leader to keep record fresh
  const heartbeat = setInterval(() => {
    if (!isLeaderTab(bodyshopId)) return;
    writeLeader({ id: TAB_ID, ts: now() }, bodyshopId);
  }, HEARTBEAT_MS);

  // Watchdog: if leader is stale, try to claim (even if we're not focused)
  const watchdog = setInterval(() => {
    const cur = readLeader(bodyshopId);
    if (isStale(cur)) claimLeadership(bodyshopId);
  }, WATCHDOG_MS);

  // If this tab was the leader, clean up on unload
  const onUnload = () => removeLeader(bodyshopId);
  window.addEventListener("beforeunload", onUnload);

  // Per-bodyshop BroadcastChannel listener (optional/no-op)
  const bc = getChannel(bodyshopId);
  const onBC = bc
    ? () => {
        // No state kept here; localStorage read is the source of truth.
      }
    : null;
  if (bc && onBC) bc.addEventListener("message", onBC);

  return () => {
    window.removeEventListener("focus", onFocus);
    document.removeEventListener("visibilitychange", onVis);
    window.removeEventListener("beforeunload", onUnload);
    clearInterval(heartbeat);
    clearInterval(watchdog);
    if (bc && onBC) {
      bc.removeEventListener("message", onBC);
      bc.close();
    }
  };
}
