// SSE stand-in for the fast profile. MSW 2.x does not have first-class
// EventSource support (see AZ-456 Risk 3); jsdom does not ship one either.
// `simulateSseStream` returns a fake EventSource that tests inject in place
// of the global where production code allows it (e2e Playwright covers
// SSE end-to-end where the real EventSource is exercised against the suite).

export interface SseEvent<T = unknown> {
  event?: string
  data: T
  id?: string
}

export interface FakeEventSource extends EventTarget {
  readyState: 0 | 1 | 2
  url: string
  close(): void
  emit<T>(e: SseEvent<T>): void
  emitError(err?: Event): void
}

const READY_CONNECTING = 0 as const
const READY_OPEN = 1 as const
const READY_CLOSED = 2 as const

export function createFakeEventSource(url = 'about:blank'): FakeEventSource {
  const target = new EventTarget() as FakeEventSource
  ;(target as { readyState: 0 | 1 | 2 }).readyState = READY_CONNECTING
  ;(target as { url: string }).url = url

  // Move to "open" on the next microtask so listeners attached synchronously
  // after construction still see the open event (matches the production
  // EventSource handshake behavior closely enough for assertions).
  queueMicrotask(() => {
    ;(target as { readyState: 0 | 1 | 2 }).readyState = READY_OPEN
    target.dispatchEvent(new Event('open'))
  })

  target.close = () => {
    ;(target as { readyState: 0 | 1 | 2 }).readyState = READY_CLOSED
  }
  target.emit = <T>(e: SseEvent<T>) => {
    if (target.readyState !== READY_OPEN) return
    const payload = typeof e.data === 'string' ? e.data : JSON.stringify(e.data)
    const message = new MessageEvent(e.event ?? 'message', { data: payload, lastEventId: e.id })
    target.dispatchEvent(message)
  }
  target.emitError = (err?: Event) => {
    target.dispatchEvent(err ?? new Event('error'))
  }
  return target
}

export function simulateSseStream<T = unknown>(events: Array<SseEvent<T>>): FakeEventSource {
  const source = createFakeEventSource()
  queueMicrotask(() => {
    for (const e of events) source.emit(e)
  })
  return source
}
