"use client"

import { useRouter } from "next/navigation"
import * as React from "react"

/**
 * The tab's ear. It draws nothing.
 *
 * ## Why it refreshes rather than renders
 *
 * The obvious client would hold the count in state and the rows in a list, and
 * it would be wrong twice over. The badge and the feed are already rendered on
 * the server, in the reader's language, with Buddhist-era dates and a sentence
 * chosen from a key at the moment somebody looks; a second renderer in the
 * browser would be a second set of those decisions to keep in step. And a count
 * held in client state next to a count arriving as a prop is two answers to one
 * question, which is the bug that outlives the feature.
 *
 * So the stream says only "there is something new" and `router.refresh()` asks
 * the server what. Nothing reloads, no scroll position moves, no form loses
 * what was typed in it: the RSC payload for the current route comes back and
 * React reconciles it, so the sidebar badge and the list both change in place
 * from the one source that was ever right about them.
 *
 * ## Why it lives in the root layout
 *
 * A layout survives navigation and a page does not. Mounted in the app shell it
 * would tear the connection down and open a new one on every click, which is
 * 400 people opening a connection per page view for no gain. Mounted once, a
 * tab holds one connection for as long as it is open.
 *
 * Only the two Web APIs, so nothing follows this into the browser bundle.
 */

/**
 * How long a burst is allowed to settle before asking the server.
 *
 * A reconnect after a long absence can send a morning's notifications in one
 * go, and each of them is the same request for the same fresh page. Two hundred
 * milliseconds is under what anybody perceives as a delay and is long enough
 * for a replay to arrive whole.
 */
const SETTLE_MS = 200

export function NotificationStream() {
  const router = useRouter()

  React.useEffect(() => {
    const source = new EventSource("/api/notifications/stream")
    let settling: ReturnType<typeof setTimeout> | undefined

    function arrived() {
      clearTimeout(settling)
      settling = setTimeout(() => router.refresh(), SETTLE_MS)
    }

    source.addEventListener("notification", arrived)

    /*
      No error handler. `EventSource` reconnects by itself, with the interval
      the stream asked for, and hands back the last id it saw so nothing that
      happened in between is lost. The one failure it does not retry is a
      refused connection, which is what a signed-out tab gets and is what it
      should get.
    */
    return () => {
      clearTimeout(settling)
      source.close()
    }
  }, [router])

  return null
}
