/** SSE subscriber registry; bbox union drives OSINT feeds. */

import { getActiveEntitiesInBbox } from './cotStore.js'
import { isInBbox, matchesLayerFilter, unionBboxes } from './cotEntityUtils.js'

/** @typedef {{ west: number, south: number, east: number, north: number }} Bbox */
/** @typedef {(event: string, data: string) => Promise<void> | void} PushFn */

/** @type {Map<string, { bbox: Bbox | null, layers: Set<string>, push: PushFn }>} */
const subscribers = new Map()
let nextId = 1

/**
 * @param {{ bbox: Bbox | null, layers: Set<string>, push: PushFn }} sub
 * @returns {() => void} Unregister function.
 */
export function registerSubscriber(sub) {
  const id = String(nextId++)
  subscribers.set(id, sub)
  return () => subscribers.delete(id)
}

/** @returns {Bbox | null} Union of all subscriber bboxes. */
export function getSubscriberBboxUnion() {
  return unionBboxes([...subscribers.values()].map(s => s.bbox))
}

export function getSubscriberCount() {
  return subscribers.size
}

export function clearSubscribers() {
  subscribers.clear()
}

export async function notifySubscribersForEntity(event, payload, entity) {
  const data = JSON.stringify(payload)
  const tasks = []
  for (const sub of subscribers.values()) {
    if (sub.bbox && !isInBbox(entity, sub.bbox)) continue
    if (!matchesLayerFilter(sub.layers, entity)) continue
    tasks.push(Promise.resolve(sub.push(event, data)))
  }
  await Promise.all(tasks)
}

export async function notifySubscribersRemove(id) {
  const data = JSON.stringify({ id })
  await Promise.all(
    [...subscribers.values()].map(sub => Promise.resolve(sub.push('remove', data))),
  )
}

/**
 * Push a filtered snapshot to each active SSE subscriber.
 * @param {{ ttlMs?: number, osintTtlMs?: number, takFilterBbox?: boolean, maxEntities?: number }} snapshotOpts
 */
export async function broadcastSubscriberSnapshots(snapshotOpts) {
  const tasks = []
  for (const sub of subscribers.values()) {
    tasks.push((async () => {
      const entities = await getActiveEntitiesInBbox(sub.bbox, {
        ...snapshotOpts,
        layers: sub.layers,
      })
      await sub.push('snapshot', JSON.stringify({ entities }))
    })())
  }
  await Promise.all(tasks)
}
