import type { ToesEvent, ToesEventType } from '../shared/events'

export type { ToesEvent, ToesEventType }

type EventCallback = (event: ToesEvent) => void

interface Listener {
  types: ToesEventType[]
  callback: EventCallback
}

const _listeners = new Set<Listener>()

let _source: EventSource | undefined

function ensureConnection() {
  if (_source) return
  const url = `${process.env.TOES_URL}/api/events/stream`
  _source = new EventSource(url)

  _source.onerror = () => {
    if (_source?.readyState === EventSource.CLOSED) {
      console.warn('[toes] Event stream closed unexpectedly')
      _source = undefined
    }
  }

  _source.onmessage = (e) => {
    try {
      const event: ToesEvent = JSON.parse(e.data)
      _listeners.forEach(l => {
        if (l.types.includes(event.type)) l.callback(event)
      })
    } catch {
      // Ignore malformed events
    }
  }
}

function closeConnection() {
  if (_source) {
    _source.close()
    _source = undefined
  }
}

export function on(type: ToesEventType | ToesEventType[], callback: EventCallback): () => void {
  const listener: Listener = {
    types: Array.isArray(type) ? type : [type],
    callback,
  }
  _listeners.add(listener)
  ensureConnection()

  return () => {
    _listeners.delete(listener)
    if (_listeners.size === 0) closeConnection()
  }
}
