import { useEffect, useRef } from 'react'
import { useAuth } from 'react-oidc-context'
import { setAccessToken, setUnauthorizedHandler } from '@/api/client'
import { LEGACY_TOKEN_STORAGE_KEY } from './oidcConfig'

/**
 * Синхронизирует {@code access_token} из react-oidc-context в API client
 * (in-memory holder) и подключает 401 handler на silent renew / redirect.
 *
 * <p>Mount внутри {@code <AuthProvider>}, рендерит null. Должен быть один
 * экземпляр на app — handler ставится глобально.
 *
 * <p><b>Single source of truth для 401:</b> раньше был дубликат interceptor'а
 * в {@code api/client.ts} (просто удалял token из localStorage) + ещё один
 * здесь (signinSilent/Redirect). Оба срабатывали на одном response — race и
 * потенциальный redirect loop. Теперь handler один, через
 * {@link setUnauthorizedHandler}.
 *
 * <p><b>Loop guard:</b> {@code redirecting} ref предотвращает burst
 * параллельных signinRedirect'ов при N concurrent 401 (типичная картина
 * после истечения токена — все pending requests возвращают 401 одновременно).
 */
export function TokenSync() {
  const auth = useAuth()
  const redirecting = useRef(false)

  // 1) Token sync — single update path к accessToken holder в client.ts.
  // ВАЖНО: пропускаем токен ТОЛЬКО когда user реально authenticated AND token
  // не expired. Раньше передавали raw {@code auth.user?.access_token} — это
  // включало случаи когда oidc-client-ts вернул user object с уже expired
  // token'ом (silent renew failed но user object всё ещё в state). axios
  // отправлял Bearer expired_token → backend (с permissive=false для guest
  // anyway re-validates JWT через oauth2ResourceServer) → 401 → catalog UI
  // показывал «Не удалось загрузить данные» вместо PUBLIC dicts.
  //
  // user.expired property из oidc-client-ts: true если access_token expiry
  // прошла. Дополнительно проверяем isAuthenticated — react-oidc-context
  // обновляет это в реальном времени.
  useEffect(() => {
    const liveToken =
      auth.isAuthenticated && auth.user && !auth.user.expired
        ? auth.user.access_token
        : null
    setAccessToken(liveToken ?? null)
    // Cleanup legacy localStorage key (was used до fix #12 + #2/3/13 cascade).
    // Идемпотентный no-op если ключа нет.
    if (typeof window !== 'undefined') {
      window.localStorage.removeItem(LEGACY_TOKEN_STORAGE_KEY)
    }
  }, [auth.isAuthenticated, auth.user, auth.user?.access_token, auth.user?.expired])

  // 2) 401 handler — registered once на app mount, делегирует silent renew /
  //    redirect на Keycloak. Guard'ы:
  //    - auth.isLoading / activeNavigator: OIDC bootstrap или redirect уже в
  //      progress — не дублируем.
  //    - URL содержит ?error=: вернулись из Keycloak с провалом — пусть UI
  //      покажет RequireAuth error screen, не зацикливаемся.
  //    - redirecting ref: при burst 401 (N concurrent requests) первый
  //      handler инициирует redirect, остальные no-op до его завершения.
  useEffect(() => {
    setUnauthorizedHandler(() => {
      if (auth.isLoading || auth.activeNavigator) return
      if (typeof window !== 'undefined' && window.location.search.includes('error=')) {
        return
      }
      if (redirecting.current) return
      redirecting.current = true
      const reset = () => {
        redirecting.current = false
      }
      if (auth.isAuthenticated) {
        // Authenticated user hit 401 → token истёк. Silent renew, иначе full
        // redirect для re-login.
        auth
          .signinSilent()
          .then(reset)
          .catch(() => {
            // Silent renew не удался — full redirect. reset не нужен:
            // page reload сбросит state. Но catch всё равно нужен на случай
            // если redirect promise отвергается (Keycloak unreachable).
            auth.signinRedirect().catch(reset)
          })
      } else {
        // Anonymous user попытался hit auth-required endpoint (mutation,
        // INTERNAL/RESTRICTED scope dictionary). Не редиректим автоматически —
        // UI должен сам показать «Войдите для редактирования» CTA, а юзер
        // сознательно кликнет «Войти». Авто-redirect ломает guest mode:
        // случайный 401 от любого API (например INTERNAL fetch) выкинул бы
        // в Keycloak без явного намерения.
        reset()
      }
    })
    return () => setUnauthorizedHandler(null)
  }, [auth])

  return null
}
