// frontend/src/hooks/usePreferences.js
//
// Per-staff-member UI preference persistence backed by Firestore via the
// /staff/me/preferences API. Each page registers with a unique page key,
// and reads/writes are transparently debounced (400 ms) so rapid state
// changes don't flood the backend.
//
// Usage:
//   const { prefs, setPref, setPrefBatch, ready } = usePreferences('crm_customers')
//
//   const [pageSize, setPageSize] = useState(() => prefs.page_size ?? 25)
//   // after user changes:
//   setPref('page_size', newSize)          // single key
//   setPrefBatch({ sort_key: 'name', sort_dir: 'asc' })  // multiple keys at once
//
// The hook returns `ready: false` until the initial load from the backend
// completes, so pages can delay rendering preference-sensitive state until
// they have the real values (avoids a flicker from stale defaults).
//
// Fallback: if the API is unreachable (network error, not logged in), the
// hook silently falls back to localStorage so the page keeps working.

import { useState, useEffect, useRef, useCallback } from 'react'
import api from '@/lib/api'

const DEBOUNCE_MS = 400

// Module-level cache: avoids re-fetching all preferences on every mount
// within the same session. The full prefs object is cached once and shared.
let _cachedAllPrefs = null       // null = not loaded yet
let _loadPromise    = null       // in-flight fetch deduplicated

function getLocalFallbackKey(pageKey) {
  // Attempt to get the uid from the stored user object for per-user isolation
  try {
    const u = JSON.parse(localStorage.getItem('user') || '{}')
    const uid = u.uid || u.sub || 'anon'
    return `ui_prefs_${uid}_${pageKey}`
  } catch {
    return `ui_prefs_anon_${pageKey}`
  }
}

function readLocalFallback(pageKey) {
  try {
    const raw = localStorage.getItem(getLocalFallbackKey(pageKey))
    return raw ? JSON.parse(raw) : {}
  } catch {
    return {}
  }
}

function writeLocalFallback(pageKey, prefs) {
  try {
    localStorage.setItem(getLocalFallbackKey(pageKey), JSON.stringify(prefs))
  } catch { /* quota exceeded — silently ignore */ }
}

async function loadAllPrefs() {
  if (_cachedAllPrefs !== null) return _cachedAllPrefs
  if (_loadPromise) return _loadPromise

  _loadPromise = api.get('/staff/me/preferences')
    .then((data) => {
      _cachedAllPrefs = data || {}
      _loadPromise = null
      return _cachedAllPrefs
    })
    .catch(() => {
      _loadPromise = null
      return null  // null = backend unreachable, use localStorage
    })

  return _loadPromise
}

// Call this on logout to clear the session cache
export function clearPreferencesCache() {
  _cachedAllPrefs = null
  _loadPromise    = null
}

export default function usePreferences(pageKey) {
  const [prefs, setPrefs] = useState({})
  const [ready, setReady] = useState(false)
  const pendingRef = useRef({})    // accumulated changes not yet flushed
  const timerRef   = useRef(null)

  // ── Initial load ──────────────────────────────────────────────────────────
  useEffect(() => {
    let cancelled = false
    loadAllPrefs().then((allPrefs) => {
      if (cancelled) return
      if (allPrefs !== null) {
        const page = allPrefs[pageKey] || {}
        setPrefs(page)
      } else {
        // Backend unreachable — use localStorage fallback
        setPrefs(readLocalFallback(pageKey))
      }
      setReady(true)
    })
    return () => { cancelled = true }
  }, [pageKey])

  // ── Flush pending changes to backend ─────────────────────────────────────
  const flush = useCallback((updates) => {
    api.patch(`/staff/me/preferences/${pageKey}`, { prefs: updates })
      .then((allPrefs) => {
        if (_cachedAllPrefs !== null) {
          _cachedAllPrefs = allPrefs
        }
        // Also update localStorage as fallback
        const pagePrefs = allPrefs[pageKey] || {}
        writeLocalFallback(pageKey, pagePrefs)
      })
      .catch(() => {
        // Best-effort — only write to localStorage on failure
        writeLocalFallback(pageKey, { ...readLocalFallback(pageKey), ...updates })
      })
  }, [pageKey])

  // ── Schedule debounced flush ──────────────────────────────────────────────
  const scheduleFlush = useCallback(() => {
    if (timerRef.current) clearTimeout(timerRef.current)
    timerRef.current = setTimeout(() => {
      const toFlush = { ...pendingRef.current }
      pendingRef.current = {}
      if (Object.keys(toFlush).length > 0) flush(toFlush)
    }, DEBOUNCE_MS)
  }, [flush])

  // ── Public API ────────────────────────────────────────────────────────────

  const setPref = useCallback((key, value) => {
    setPrefs((prev) => {
      const next = { ...prev, [key]: value }
      // Update cache immediately so other hook instances see it
      if (_cachedAllPrefs !== null) {
        _cachedAllPrefs = { ..._cachedAllPrefs, [pageKey]: next }
      }
      return next
    })
    pendingRef.current = { ...pendingRef.current, [key]: value }
    scheduleFlush()
  }, [pageKey, scheduleFlush])

  const setPrefBatch = useCallback((updates) => {
    setPrefs((prev) => {
      const next = { ...prev, ...updates }
      if (_cachedAllPrefs !== null) {
        _cachedAllPrefs = { ..._cachedAllPrefs, [pageKey]: next }
      }
      return next
    })
    pendingRef.current = { ...pendingRef.current, ...updates }
    scheduleFlush()
  }, [pageKey, scheduleFlush])

  // Flush immediately on unmount if there are pending changes
  useEffect(() => {
    return () => {
      if (timerRef.current) clearTimeout(timerRef.current)
      const toFlush = { ...pendingRef.current }
      if (Object.keys(toFlush).length > 0) flush(toFlush)
    }
  }, [flush])

  return { prefs, setPref, setPrefBatch, ready }
}
