'use client'

// ST-1115 / PBI-74: Client hook for the backlog 3-pane SSE stream.
// Mounts in BacklogHydrationWrapper so it survives Server Action refreshes.
// Dispatches pbi/story/task change events into useProductWorkspaceStore.
//
// T-861: stream blijft open op tab hidden. Per spec werkt EventSource gewoon
// door als de browser het toelaat — gemiste events worden opgehaald via
// resyncActiveScopes('visible') uit useWorkspaceResync.
// T-862: bij latere 'ready' events (post-reconnect) triggeren we
// resyncActiveScopes('reconnect') zodat events die tijdens disconnect zijn
// gemist, alsnog binnenkomen.

import { useEffect, useRef } from 'react'
import { useProductWorkspaceStore } from '@/stores/product-workspace/store'
import type { ProductRealtimeEvent } from '@/stores/product-workspace/types'

const BACKOFF_START_MS = 1_000
const BACKOFF_MAX_MS = 30_000

type EntityPayload = {
  op: 'I' | 'U' | 'D'
  entity: 'pbi' | 'story' | 'task'
  [key: string]: unknown
}

export function useBacklogRealtime(productId: string | null) {
  const sourceRef = useRef<EventSource | null>(null)
  const backoffRef = useRef<number>(BACKOFF_START_MS)
  const reconnectTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
  const readyCountRef = useRef<number>(0)

  useEffect(() => {
    if (!productId) return

    const close = () => {
      if (sourceRef.current) {
        sourceRef.current.close()
        sourceRef.current = null
      }
      if (reconnectTimerRef.current) {
        clearTimeout(reconnectTimerRef.current)
        reconnectTimerRef.current = null
      }
    }

    const connect = () => {
      close()
      const source = new EventSource(
        `/api/realtime/backlog?product_id=${encodeURIComponent(productId)}`,
      )
      sourceRef.current = source

      source.addEventListener('ready', () => {
        backoffRef.current = BACKOFF_START_MS
        readyCountRef.current += 1
        // T-862: eerste ready = initial connect; latere ready = reconnect.
        if (readyCountRef.current > 1) {
          void useProductWorkspaceStore
            .getState()
            .resyncActiveScopes('reconnect')
        }
      })

      source.onmessage = (e) => {
        if (!e.data) return
        try {
          const payload = JSON.parse(e.data) as EntityPayload
          useProductWorkspaceStore
            .getState()
            .applyRealtimeEvent(payload as unknown as ProductRealtimeEvent)
        } catch (err) {
          if (process.env.NODE_ENV !== 'production') {
            console.error('[realtime/backlog] failed to parse event', err, e.data)
          }
        }
      }

      source.onerror = () => {
        if (sourceRef.current !== source) return
        close()
        if (document.visibilityState === 'hidden') return
        const delay = backoffRef.current
        backoffRef.current = Math.min(backoffRef.current * 2, BACKOFF_MAX_MS)
        reconnectTimerRef.current = setTimeout(connect, delay)
      }
    }

    // T-861: stream blijft open op hidden. Reconnect alleen als source weg
    // is (b.v. na netwerkfout) en de tab visible is.
    const onVisibility = () => {
      if (document.visibilityState === 'visible' && sourceRef.current === null) {
        backoffRef.current = BACKOFF_START_MS
        connect()
      }
    }

    connect()
    document.addEventListener('visibilitychange', onVisibility)

    return () => {
      document.removeEventListener('visibilitychange', onVisibility)
      close()
      readyCountRef.current = 0
    }
  }, [productId])
}
