import { describe, it, expect, beforeEach, vi } from 'vitest'
import {
  registerSubscriber,
  getSubscriberBboxUnion,
  clearSubscribers,
  notifySubscribersForEntity,
  notifySubscribersRemove,
  broadcastSubscriberSnapshots,
} from '../../../server/utils/cotSubscribers.js'
import { updateFromCot, clearCotStore } from '../../../server/utils/cotStore.js'

describe('cotSubscribers', () => {
  beforeEach(() => {
    clearSubscribers()
  })

  it('unions subscriber bboxes', () => {
    registerSubscriber({
      bbox: { west: -123, south: 37, east: -122, north: 38 },
      layers: new Set(['air']),
      push: vi.fn(),
    })
    registerSubscriber({
      bbox: { west: -124, south: 36, east: -121, north: 39 },
      layers: new Set(['surface']),
      push: vi.fn(),
    })
    expect(getSubscriberBboxUnion()).toEqual({ west: -124, south: 36, east: -121, north: 39 })
  })

  it('notifies subscribers inside bbox and matching layer', async () => {
    const push = vi.fn()
    registerSubscriber({
      bbox: { west: -123, south: 37, east: -122, north: 38 },
      layers: new Set(['air']),
      push,
    })
    await notifySubscribersForEntity('update', { entity: { id: 'ICAO.x' } }, {
      id: 'ICAO.x',
      lat: 37.5,
      lng: -122.5,
      type: 'a-f-A-C-F',
    })
    expect(push).toHaveBeenCalledWith('update', expect.any(String))
  })

  it('skips subscribers when entity outside bbox', async () => {
    const push = vi.fn()
    registerSubscriber({
      bbox: { west: -123, south: 37, east: -122, north: 38 },
      layers: new Set(['air']),
      push,
    })
    await notifySubscribersForEntity('update', { entity: { id: 'ICAO.x' } }, {
      id: 'ICAO.x',
      lat: 40,
      lng: -122.5,
      type: 'a-f-A-C-F',
    })
    expect(push).not.toHaveBeenCalled()
  })

  it('notifySubscribersRemove pushes to all subscribers', async () => {
    const pushA = vi.fn()
    const pushB = vi.fn()
    registerSubscriber({
      bbox: { west: -123, south: 37, east: -122, north: 38 },
      layers: new Set(['air']),
      push: pushA,
    })
    registerSubscriber({
      bbox: { west: -125, south: 35, east: -120, north: 40 },
      layers: new Set(['surface']),
      push: pushB,
    })
    await notifySubscribersRemove('ICAO.removed')
    expect(pushA).toHaveBeenCalledWith('remove', JSON.stringify({ id: 'ICAO.removed' }))
    expect(pushB).toHaveBeenCalledWith('remove', JSON.stringify({ id: 'ICAO.removed' }))
  })

  it('broadcastSubscriberSnapshots sends per-subscriber filtered snapshot', async () => {
    clearCotStore()
    await updateFromCot({ id: 'ICAO.in', lat: 37.5, lng: -122.5, type: 'a-f-A-C-F' })
    await updateFromCot({ id: 'ICAO.out', lat: 40, lng: -100, type: 'a-f-A-C-F' })
    const push = vi.fn()
    registerSubscriber({
      bbox: { west: -123, south: 37, east: -122, north: 38 },
      layers: new Set(['air']),
      push,
    })
    await broadcastSubscriberSnapshots({ ttlMs: 90_000, osintTtlMs: 30_000, takFilterBbox: false })
    expect(push).toHaveBeenCalledWith('snapshot', expect.any(String))
    const payload = JSON.parse(push.mock.calls[0][1])
    expect(payload.entities.map(e => e.id)).toEqual(['ICAO.in'])
  })
})
