import { useEffect, useRef, useSyncExternalStore } from 'react'

// Global inflight coordination for long-running user-facing actions. A
// ref-counting external store (same shape as `scrollStore` in src/components/
// menu/scroll.ts) so any surface can `begin` an operation and the subscribed
// <InflightBar /> inside MenuSurfacePill lights up while at least one is live.
//
// Declarative wiring: `useInflight(isPending, label?)` flips a handle on/off
// alongside a React Query mutation result. `useCacheMutation` calls this
// internally so every mutation ported onto the factory lights the bar with
// zero per-screen plumbing.
//
// Imperative wiring: `inflightStore.begin(label?) → handle` / `end(handle)` /
// `setProgress(handle, 0..1)`. Used by the composer submit flow where the
// duration outlives a single React render.

export type InflightHandle = number

type InternalHandle = {
  id: InflightHandle
  label?: string
  progress?: number
}

export type InflightSnapshot = {
  count: number
  /** Latest-begun handle's label. Undefined when idle or the active handle was
   *  started without a label. */
  label: string | undefined
  /** Max reported determinate progress across active handles, or undefined if
   *  no active handle reported progress (bar renders indeterminate). */
  progress: number | undefined
  /** Monotonic counter so `useSyncExternalStore` sees a fresh reference on
   *  every change — handles and snapshot are otherwise mutated in place. */
  version: number
}

const IDLE: InflightSnapshot = {
  count: 0,
  label: undefined,
  progress: undefined,
  version: 0,
}

const listeners = new Set<() => void>()
let handles: InternalHandle[] = []
let nextId = 1
let snapshot: InflightSnapshot = IDLE

function computeSnapshot(): InflightSnapshot {
  if (handles.length === 0) {
    return { ...IDLE, version: snapshot.version + 1 }
  }
  const last = handles[handles.length - 1]
  let progress: number | undefined
  for (const h of handles) {
    if (h.progress !== undefined) {
      progress = progress === undefined ? h.progress : Math.max(progress, h.progress)
    }
  }
  return {
    count: handles.length,
    label: last?.label,
    progress,
    version: snapshot.version + 1,
  }
}

function emit() {
  snapshot = computeSnapshot()
  for (const l of listeners) l()
}

export const inflightStore = {
  begin(label?: string): InflightHandle {
    const id = nextId++
    handles.push({ id, label })
    emit()
    return id
  },
  end(id: InflightHandle) {
    const before = handles.length
    handles = handles.filter((h) => h.id !== id)
    if (handles.length !== before) emit()
  },
  setProgress(id: InflightHandle, progress: number) {
    const h = handles.find((x) => x.id === id)
    if (!h) return
    const clamped = progress < 0 ? 0 : progress > 1 ? 1 : progress
    if (h.progress === clamped) return
    h.progress = clamped
    emit()
  },
  updateLabel(id: InflightHandle, label?: string) {
    const h = handles.find((x) => x.id === id)
    if (!h) return
    if (h.label === label) return
    h.label = label
    emit()
  },
  subscribe(l: () => void) {
    listeners.add(l)
    return () => {
      listeners.delete(l)
    }
  },
  getSnapshot() {
    return snapshot
  },
}

export function useInflightSnapshot(): InflightSnapshot {
  return useSyncExternalStore(
    inflightStore.subscribe,
    inflightStore.getSnapshot,
    inflightStore.getSnapshot
  )
}

/**
 * Mirror a boolean `active` flag into a handle on the global inflight store.
 * When `active` flips true we begin; when it flips false or the component
 * unmounts we end. Label changes while active are reflected live.
 */
export function useInflight(active: boolean, label?: string) {
  const idRef = useRef<InflightHandle | null>(null)
  useEffect(() => {
    if (active) {
      if (idRef.current === null) {
        idRef.current = inflightStore.begin(label)
      } else {
        inflightStore.updateLabel(idRef.current, label)
      }
    } else if (idRef.current !== null) {
      inflightStore.end(idRef.current)
      idRef.current = null
    }
  }, [active, label])
  useEffect(() => {
    return () => {
      if (idRef.current !== null) {
        inflightStore.end(idRef.current)
        idRef.current = null
      }
    }
  }, [])
}
