import { useCallback, useEffect, useMemo, useState, type ReactNode } from 'react'
import { getMe, login as apiLogin, logout as apiLogout, type CurrentUser } from '@/api/auth'
import { authStore } from '@/auth/authStore'
import { refreshAccessToken } from '@/api/client'
import { AuthContext, type AuthContextValue, type AuthStatus } from '@/auth/authContext'

/**
 * Задержки повторов восстановления сессии, если backend отвечает 5xx.
 *
 * Недоступность сервера — не повод объявлять пользователя неавторизованным:
 * refresh-cookie цела, и через несколько секунд сессия обычно поднимается
 * сама. Повторов ровно три (суммарно ~7 с) — дальше показываем страницу
 * входа, потому что бесконечный спиннер хуже честного «войдите заново»:
 * cookie при этом не стирается, и повторная попытка входа сработает, как
 * только backend оживёт.
 */
const BOOTSTRAP_RETRY_DELAYS_MS = [1000, 2000, 4000]

const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))

/**
 * Провайдер сессии пользователя.
 * При монтировании приложения пытается восстановить сессию через
 * silent-refresh (httpOnly refresh-cookie), т.к. access-токен в памяти
 * теряется при перезагрузке страницы.
 */
export function AuthProvider({ children }: { children: ReactNode }) {
  const [user, setUser] = useState<CurrentUser | null>(null)
  const [status, setStatus] = useState<AuthStatus>('loading')

  useEffect(() => {
    let cancelled = false

    async function bootstrap() {
      let outcome = await refreshAccessToken()
      for (const delay of BOOTSTRAP_RETRY_DELAYS_MS) {
        if (cancelled || outcome.result !== 'unavailable') break
        await sleep(delay)
        if (cancelled) return
        outcome = await refreshAccessToken()
      }
      if (cancelled) return
      if (outcome.result !== 'ok') {
        setStatus('unauthenticated')
        return
      }
      try {
        const me = await getMe()
        if (cancelled) return
        setUser(me)
        setStatus('authenticated')
      } catch {
        if (!cancelled) setStatus('unauthenticated')
      }
    }

    void bootstrap()
    return () => {
      cancelled = true
    }
  }, [])

  const login = useCallback(async (email: string, password: string) => {
    const token = await apiLogin(email, password)
    authStore.setAccessToken(token.access_token)
    const me = await getMe()
    setUser(me)
    setStatus('authenticated')
  }, [])

  const logout = useCallback(async () => {
    try {
      await apiLogout()
    } finally {
      authStore.setAccessToken(null)
      setUser(null)
      setStatus('unauthenticated')
    }
  }, [])

  const refreshUser = useCallback(async () => {
    const me = await getMe()
    setUser(me)
  }, [])

  const value = useMemo<AuthContextValue>(
    () => ({ user, status, login, logout, refreshUser }),
    [user, status, login, logout, refreshUser],
  )

  return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>
}
