/**
 * LocaleProvider + useLocale — system-i18n.
 *
 * Wraps the application root (or any sub-tree) with a locale context.
 * On locale change:
 *   1. Calls i18next.changeLanguage() for instant in-tree translation swap.
 *   2. Sets document.documentElement.lang and .dir imperatively.
 *   3. Respects `prefers-reduced-motion` — only adds the CSS direction transition
 *      class when the user has not requested reduced motion.
 *
 * No page reload is required — all three changes are synchronous DOM mutations.
 * Persists the user's choice to localStorage for the next session bootstrap.
 */
import {
  createContext,
  useCallback,
  useContext,
  useEffect,
  useState,
  type ReactNode,
} from 'react'
import i18next from 'i18next'
import type { Locale } from '@zync/types'
import { useDirection } from './useDirection'

const LOCALE_STORAGE_KEY = 'zync_locale'

interface LocaleContextValue {
  locale: Locale
  setLocale: (locale: Locale) => void
  dir: 'rtl' | 'ltr'
  timezone: string
  setTimezone: (timezone: string) => void
}

export const LocaleContext = createContext<LocaleContextValue | null>(null)

interface LocaleProviderProps {
  children: ReactNode
  initialLocale?: Locale
  /** IANA timezone for the current user, e.g. 'Asia/Jerusalem'. Defaults to 'Asia/Jerusalem'. */
  initialTimezone?: string
  /** Called after locale change so the parent can persist to user_preferences */
  onLocaleChange?: (locale: Locale) => void
}

/**
 * Apply `lang` + `dir` to the document root element.
 * Respects `prefers-reduced-motion` — no CSS transition on direction swap when
 * the user has opted out of motion.
 */
function applyDocumentLocale(locale: Locale, dir: 'rtl' | 'ltr'): void {
  if (typeof document === 'undefined') return

  const root = document.documentElement
  const reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches

  if (!reducedMotion) {
    root.classList.add('dir-transitioning')
  }

  root.lang = locale
  root.dir = dir

  if (!reducedMotion) {
    // Remove the transition class after a single animation frame so subsequent
    // content changes don't also animate.
    requestAnimationFrame(() => {
      root.classList.remove('dir-transitioning')
    })
  }
}

export function LocaleProvider({
  children,
  initialLocale = 'en',
  initialTimezone = 'Asia/Jerusalem',
  onLocaleChange,
}: LocaleProviderProps) {
  const [locale, setLocaleState] = useState<Locale>(() => {
    // Read from localStorage first (fastest — no async required)
    if (typeof localStorage !== 'undefined') {
      const stored = localStorage.getItem(LOCALE_STORAGE_KEY)
      if (stored === 'en' || stored === 'he') return stored
    }
    return initialLocale
  })
  const [timezone, setTimezone] = useState<string>(initialTimezone)

  const dir = useDirection(locale)

  useEffect(() => {
    setTimezone(initialTimezone)
  }, [initialTimezone])

  // Apply to DOM on initial mount and on each locale change
  useEffect(() => {
    applyDocumentLocale(locale, dir)
  }, [locale, dir])

  const setLocale = useCallback(
    (next: Locale) => {
      setLocaleState(next)
      // Persist to localStorage for next page-load bootstrap
      if (typeof localStorage !== 'undefined') {
        localStorage.setItem(LOCALE_STORAGE_KEY, next)
      }
      // Swap i18next language — no reload needed
      i18next.changeLanguage(next)
      // Notify parent (e.g. to persist to user_preferences via PATCH /api/preferences)
      onLocaleChange?.(next)
    },
    [onLocaleChange],
  )

  return (
    <LocaleContext.Provider value={{ locale, setLocale, dir, timezone, setTimezone }}>
      {children}
    </LocaleContext.Provider>
  )
}

/**
 * Returns the current locale context.
 * Must be called inside a `<LocaleProvider>` subtree.
 */
export function useLocale(): LocaleContextValue {
  const ctx = useContext(LocaleContext)
  if (!ctx) {
    throw new Error('useLocale must be used inside <LocaleProvider>')
  }
  return ctx
}
