'use client';

import * as React from 'react';
import { usePathname, useRouter } from 'next/navigation';
import {
  apiFetch,
  ApiError,
  AUTH_SESSION_INVALID_EVENT,
  AUTH_REFRESH_KEY,
  AUTH_SESSION_KEY,
  AUTH_TOKEN_KEY,
  AUTH_USER_CACHE_KEY,
  AuthSessionResponse,
  AuthTokens,
  fetchAuthSession,
  buildAuthDevicePayload,
  IdentifyResponse,
  getApiErrorMessage,
  ensureApiGatewayReady,
  invalidateApiGatewayReady,
  isApiGatewayReady,
  isGatewayUnavailableError,
  isPinRequiredError,
  OtpSendResponse,
  PasswordlessAuthResponse,
  PinVerificationResponse,
  PublicUser,
  refreshAuthSession,
  resetGatewayCircuit,
  resetPinRequiredNotification,
  scheduleFedcmSessionSync,
  syncFedcmSession,
  setPinRequiredHandler,
  subscribeApiReady,
  subscribeApiNotReady,
} from '@/lib/api';
import { resolvePostPinAuthRedirect } from '@/lib/auth-post-pin-redirect';
import { finalizeFedcmLogin, isFedcmLoginPopup, shouldFinalizeFedcmLogin } from '@/lib/fedcm-login-bridge';
import { FedcmApiLoginStatusBridge } from './fedcm-api-login-status';
import { usePublicSettings } from './public-settings-provider';
import { useToast } from './toast-provider';
import { PinLockModal } from './pin-lock-modal';
import { AppBootstrapScreen } from './app-bootstrap-screen';
import { EMBEDDED_AUTH_MESSAGE, type EmbeddedAuthPayload, useEmbeddedAuthFallback } from '@/lib/embedded-auth-bridge';
import { usePinIdleLock } from '@/hooks/use-pin-idle-lock';
import { clearPinIdleWatch, isPinIdleWatchActive, markPinIdleWatch } from '@/lib/pin-idle-storage';

interface AuthContextValue {
  user: PublicUser | null;
  token: string | null;
  isLoading: boolean;
  isApiReady: boolean;
  /** true после стабильного /health и успешной проверки сессии */
  isSessionReady: boolean;
  /** @deprecated используйте isSessionReady */
  isContentReady: boolean;
  isPinLocked: boolean;
  hasStoredSession: boolean;
  login: (login: string, password: string) => Promise<AuthTokens>;
  identifyLogin: (login: string) => Promise<IdentifyResponse>;
  sendLoginOtp: (recipient: string, channel?: string) => Promise<OtpSendResponse>;
  verifyLoginOtp: (recipient: string, code: string) => Promise<PasswordlessAuthResponse>;
  loginWithPassword: (login: string, password: string) => Promise<AuthTokens>;
  loginWithLdap: (username: string, password: string) => Promise<AuthTokens>;
  beginTotpLogin: (recipient: string) => Promise<string>;
  verifyTotpLogin: (totpChallengeToken: string, code: string) => Promise<AuthTokens>;
  completePin: (sessionId: string, pin: string) => Promise<void>;
  applyLoginAuth: (auth: AuthTokens) => Promise<AuthTokens>;
  applyUserPatch: (patch: Partial<PublicUser>) => void;
  register: (data: { displayName: string; login: string; password: string }) => Promise<void>;
  refreshProfile: () => Promise<void>;
  logout: () => void;
}

const AuthContext = React.createContext<AuthContextValue | null>(null);

function cacheUserProfile(user: PublicUser) {
  window.localStorage.setItem(AUTH_USER_CACHE_KEY, JSON.stringify(user));
}

function readCachedUserProfile(): PublicUser | null {
  const raw = window.localStorage.getItem(AUTH_USER_CACHE_KEY);
  if (!raw) return null;
  try {
    return JSON.parse(raw) as PublicUser;
  } catch {
    return null;
  }
}

function persistPartialAuth(auth: AuthTokens) {
  window.localStorage.setItem(AUTH_REFRESH_KEY, auth.refreshToken);
  window.localStorage.setItem(AUTH_SESSION_KEY, auth.sessionId);
  cacheUserProfile(auth.user);
}

function applySessionState(
  session: AuthSessionResponse,
  setters: {
    setUser: React.Dispatch<React.SetStateAction<PublicUser | null>>;
    setToken: React.Dispatch<React.SetStateAction<string | null>>;
    activatePinLock: (sessionId: string) => void;
    clearPinLock: () => void;
  }
) {
  setters.setUser(session.user);
  cacheUserProfile(session.user);
  if (session.sessionId) {
    window.localStorage.setItem(AUTH_SESSION_KEY, session.sessionId);
  }
  if (session.requiresPin) {
    setters.activatePinLock(session.sessionId ?? '');
  } else {
    setters.clearPinLock();
  }
}

function readInitialAuthState() {
  // Первичный render должен совпадать на сервере и в браузере. Токены из
  // localStorage читаются уже в refreshProfile(), иначе авторизованная вкладка
  // получает hydration mismatch (React #418) ещё до запуска эффектов.
  return {
    token: null as string | null,
    hasStoredSession: false
  };
}

export function AuthProvider({ children }: { children: React.ReactNode }) {
  const router = useRouter();
  const pathname = usePathname();
  const { showToast } = useToast();
  const { publicApiUrl, pinLockTimeoutMinutes } = usePublicSettings();
  const initialAuth = React.useMemo(() => readInitialAuthState(), []);
  const [token, setToken] = React.useState<string | null>(initialAuth.token);
  const [user, setUser] = React.useState<PublicUser | null>(null);
  const [isLoading, setIsLoading] = React.useState(true);
  const [isApiReady, setIsApiReady] = React.useState(false);
  const [isSessionReady, setIsSessionReady] = React.useState(false);
  const [isPinLocked, setIsPinLocked] = React.useState(false);
  const [hasStoredSession, setHasStoredSession] = React.useState(initialAuth.hasStoredSession);
  const [lockedSessionId, setLockedSessionId] = React.useState<string | null>(null);
  const [pinSubmitting, setPinSubmitting] = React.useState(false);
  const [pinError, setPinError] = React.useState<string | null>(null);
  const [bootstrapError, setBootstrapError] = React.useState<string | null>(null);
  const isPinLockedRef = React.useRef(false);
  const refreshInFlightRef = React.useRef<Promise<void> | null>(null);
  const initialBootstrapDoneRef = React.useRef(false);

  useEmbeddedAuthFallback();

  React.useEffect(() => {
    if (isApiGatewayReady()) {
      setIsApiReady(true);
    }
    const onReady = () => setIsApiReady(true);
    const onNotReady = () => {
      if (!initialBootstrapDoneRef.current) {
        setIsApiReady(false);
      }
    };
    const unsubReady = subscribeApiReady(onReady);
    const unsubNotReady = subscribeApiNotReady(onNotReady);
    return () => {
      unsubReady();
      unsubNotReady();
    };
  }, []);

  React.useEffect(() => {
    isPinLockedRef.current = isPinLocked;
  }, [isPinLocked]);

  const clearPinLock = React.useCallback(() => {
    setIsPinLocked(false);
    setLockedSessionId(null);
    setPinError(null);
    resetPinRequiredNotification();
  }, []);

  React.useEffect(() => {
    function handleEmbeddedAuth(event: Event) {
      const detail = (event as CustomEvent<EmbeddedAuthPayload>).detail;
      if (!detail?.token?.trim()) return;
      setToken(detail.token.trim());
      setHasStoredSession(true);
      clearPinLock();
    }

    window.addEventListener(EMBEDDED_AUTH_MESSAGE, handleEmbeddedAuth as EventListener);
    return () => window.removeEventListener(EMBEDDED_AUTH_MESSAGE, handleEmbeddedAuth as EventListener);
  }, [clearPinLock]);

  const activatePinLock = React.useCallback((sessionId: string) => {
    const resolvedSessionId = sessionId || window.localStorage.getItem(AUTH_SESSION_KEY);
    if (!resolvedSessionId) return;
    markPinIdleWatch(user?.id);
    setLockedSessionId(resolvedSessionId);
    setIsPinLocked(true);
    setPinError(null);
    setHasStoredSession(true);
    const cachedUser = readCachedUserProfile();
    if (cachedUser) {
      setUser(cachedUser);
    }
    const accessToken = window.localStorage.getItem(AUTH_TOKEN_KEY);
    if (accessToken) {
      void syncFedcmSession(accessToken);
    }
  }, [user?.id]);

  const warmUpAndApplySession = React.useCallback(
    async (accessToken: string) => {
      setIsSessionReady(false);
      setIsLoading(true);
      setBootstrapError(null);
      invalidateApiGatewayReady();
      setIsApiReady(false);

      try {
        await ensureApiGatewayReady();
        const session = await fetchAuthSession(accessToken);
        applySessionState(session, { setUser, setToken, activatePinLock, clearPinLock });
        initialBootstrapDoneRef.current = true;
        setIsApiReady(true);
        setIsSessionReady(true);
      } catch (error) {
        setBootstrapError(
          isGatewayUnavailableError(error)
            ? 'Не удалось подключиться к серверу API. Подождите несколько секунд и нажмите «Повторить».'
            : (getApiErrorMessage(error, 'Не удалось подключиться к серверу API') ?? 'Ошибка подключения')
        );
        throw error;
      } finally {
        setIsLoading(false);
      }
    },
    [activatePinLock, clearPinLock]
  );

  const establishSession = React.useCallback(
    async (auth: AuthTokens) => {
      window.localStorage.setItem(AUTH_TOKEN_KEY, auth.accessToken);
      window.localStorage.setItem(AUTH_REFRESH_KEY, auth.refreshToken);
      window.localStorage.setItem(AUTH_SESSION_KEY, auth.sessionId);
      setToken(auth.accessToken);
      setUser(auth.user);
      cacheUserProfile(auth.user);
      setHasStoredSession(true);
      clearPinLock();
      setBootstrapError(null);
      initialBootstrapDoneRef.current = true;
      setIsApiReady(true);
      setIsSessionReady(true);
      setIsLoading(false);

      void fetchAuthSession(auth.accessToken)
        .then((session) => {
          applySessionState(session, { setUser, setToken, activatePinLock, clearPinLock });
        })
        .catch((error) => {
          if (isPinRequiredError(error)) {
            activatePinLock(error.sessionId ?? auth.sessionId);
            return;
          }
          if (!isGatewayUnavailableError(error)) {
            const message = getApiErrorMessage(error, 'Не удалось обновить профиль после входа');
            if (message) showToast(message);
          }
        });

      if (auth.pinVerified !== false) {
        if (typeof window !== 'undefined' && shouldFinalizeFedcmLogin(window.location.pathname)) {
          void finalizeFedcmLogin(auth.accessToken);
        } else {
          void syncFedcmSession(auth.accessToken);
          scheduleFedcmSessionSync(auth.accessToken, 8000);
        }
      }
    },
    [activatePinLock, clearPinLock, showToast]
  );

  const logout = React.useCallback(() => {
    window.localStorage.removeItem(AUTH_TOKEN_KEY);
    window.localStorage.removeItem(AUTH_REFRESH_KEY);
    window.localStorage.removeItem(AUTH_SESSION_KEY);
    window.localStorage.removeItem(AUTH_USER_CACHE_KEY);
    clearPinIdleWatch();
    initialBootstrapDoneRef.current = false;
    setToken(null);
    setUser(null);
    setHasStoredSession(false);
    setIsSessionReady(false);
    setIsApiReady(false);
    invalidateApiGatewayReady();
    clearPinLock();
    router.push('/auth/login');
  }, [clearPinLock, router]);

  React.useEffect(() => {
    function handleInvalidSession() {
      if (!window.localStorage.getItem(AUTH_TOKEN_KEY) && !window.localStorage.getItem(AUTH_REFRESH_KEY)) return;
      logout();
    }

    window.addEventListener(AUTH_SESSION_INVALID_EVENT, handleInvalidSession);
    return () => window.removeEventListener(AUTH_SESSION_INVALID_EVENT, handleInvalidSession);
  }, [logout]);

  const applyUserPatch = React.useCallback((patch: Partial<PublicUser>) => {
    setUser((current) => {
      if (!current) return current;
      const next = { ...current, ...patch };
      cacheUserProfile(next);
      return next;
    });
  }, []);

  const refreshProfile = React.useCallback(async () => {
    if (refreshInFlightRef.current) {
      return refreshInFlightRef.current;
    }

    const task: Promise<void> = (async () => {
      const currentToken = window.localStorage.getItem(AUTH_TOKEN_KEY);
      const refreshToken = window.localStorage.getItem(AUTH_REFRESH_KEY);
      setHasStoredSession(Boolean(currentToken || refreshToken));

      if (!currentToken && !refreshToken) {
        window.localStorage.removeItem(AUTH_USER_CACHE_KEY);
        setUser(null);
        setToken(null);
        initialBootstrapDoneRef.current = true;
        setIsSessionReady(true);
        setIsApiReady(true);
        setIsLoading(false);
        return;
      }

      const isInitialBootstrap = !initialBootstrapDoneRef.current;
      if (isInitialBootstrap) {
        setIsLoading(true);
        setBootstrapError(null);
      }

      try {
        for (let bootstrapAttempt = 0; bootstrapAttempt < 4; bootstrapAttempt += 1) {
          if (isInitialBootstrap) {
            try {
              await ensureApiGatewayReady(bootstrapAttempt > 0);
            } catch {
              // цикл bootstrap повторит после паузы
            }
          }

          try {
            if (currentToken) {
              setToken(currentToken);
              try {
                const session = await fetchAuthSession(currentToken);
                applySessionState(session, { setUser, setToken, activatePinLock, clearPinLock });
                setIsApiReady(true);
                setIsSessionReady(true);
                return;
              } catch (error) {
                if (isPinRequiredError(error)) {
                  activatePinLock(error.sessionId ?? window.localStorage.getItem(AUTH_SESSION_KEY) ?? '');
                  setIsApiReady(true);
                  setIsSessionReady(true);
                  return;
                }
                if (!(error instanceof ApiError) || error.code !== 'TOKEN_EXPIRED') {
                  throw error;
                }
              }
            }

            if (refreshToken) {
              const refreshed = await refreshAuthSession();
              if (refreshed.accessToken) {
                window.localStorage.setItem(AUTH_TOKEN_KEY, refreshed.accessToken);
                setToken(refreshed.accessToken);
              }
              if (refreshed.sessionId) {
                window.localStorage.setItem(AUTH_SESSION_KEY, refreshed.sessionId);
              }
              if (refreshed.requiresPin) {
                if (refreshed.user) {
                  setUser(refreshed.user);
                  cacheUserProfile(refreshed.user);
                }
                activatePinLock(refreshed.sessionId ?? '');
                setIsApiReady(true);
                setIsSessionReady(true);
                return;
              }
              if (refreshed.accessToken) {
                const session = await fetchAuthSession(refreshed.accessToken);
                applySessionState(session, { setUser, setToken, activatePinLock, clearPinLock });
                setIsApiReady(true);
                setIsSessionReady(true);
                return;
              }
            }

            throw new ApiError('Сессия недействительна', 401);
          } catch (error) {
            if (isGatewayUnavailableError(error) && isInitialBootstrap && bootstrapAttempt + 1 < 4) {
              invalidateApiGatewayReady();
              resetGatewayCircuit();
              setBootstrapError(
                'Не удалось подключиться к серверу API (502). Подождите несколько секунд и нажмите «Повторить».'
              );
              await new Promise((resolve) => setTimeout(resolve, 1200));
              continue;
            }
            if (isPinRequiredError(error)) {
              activatePinLock(error.sessionId ?? window.localStorage.getItem(AUTH_SESSION_KEY) ?? '');
              setIsApiReady(true);
              setIsSessionReady(true);
              return;
            }
            if (refreshToken && error instanceof ApiError && error.status === 403) {
              activatePinLock(window.localStorage.getItem(AUTH_SESSION_KEY) ?? '');
              setIsApiReady(true);
              setIsSessionReady(true);
              return;
            }
            if (error instanceof ApiError && (error.code === 'NO_REFRESH' || error.status === 401)) {
              logout();
              return;
            }
            const message = getApiErrorMessage(error, 'Не удалось загрузить профиль');
            if (isGatewayUnavailableError(error) && isInitialBootstrap) {
              setBootstrapError('Не удалось подключиться к серверу API. Подождите несколько секунд и нажмите «Повторить».');
              setIsApiReady(false);
              setIsSessionReady(false);
              setIsLoading(false);
              return;
            }
            if (message) showToast(message);
            logout();
            return;
          }
        }
      } finally {
        if (isInitialBootstrap) {
          initialBootstrapDoneRef.current = true;
          setIsLoading(false);
        }
      }
    })();

    refreshInFlightRef.current = task;
    void task.finally(() => {
      refreshInFlightRef.current = null;
    });
    return task;
  }, [activatePinLock, clearPinLock, logout, showToast]);

  React.useEffect(() => {
    setPinRequiredHandler(activatePinLock);
    return () => setPinRequiredHandler(null);
  }, [activatePinLock]);

  React.useEffect(() => {
    void refreshProfile();
  }, [refreshProfile]);

  const pinIdleLockEnabled =
    isSessionReady &&
    !isPinLocked &&
    !pathname.startsWith('/auth/') &&
    Boolean(token ?? (typeof window !== 'undefined' ? window.localStorage.getItem(AUTH_TOKEN_KEY) : null)) &&
    isPinIdleWatchActive(user?.id);

  usePinIdleLock({
    enabled: pinIdleLockEnabled,
    timeoutMinutes: pinLockTimeoutMinutes,
    accessToken: token,
    onLockRequired: activatePinLock,
  });

  React.useEffect(() => {
    function handleVisibilityChange() {
      if (document.visibilityState !== 'visible') return;
      if (isPinLockedRef.current) return;
      if (!window.localStorage.getItem(AUTH_REFRESH_KEY)) return;
      void refreshProfile();
    }

    function handleAuthRefreshed(event: Event) {
      const detail = (event as CustomEvent<{ accessToken?: string; user?: PublicUser }>).detail;
      if (detail.accessToken) {
        setToken(detail.accessToken);
      }
      if (detail.user) {
        setUser(detail.user);
        cacheUserProfile(detail.user);
      }
    }

    document.addEventListener('visibilitychange', handleVisibilityChange);
    window.addEventListener('lendry:auth-refreshed', handleAuthRefreshed);

    return () => {
      document.removeEventListener('visibilitychange', handleVisibilityChange);
      window.removeEventListener('lendry:auth-refreshed', handleAuthRefreshed);
    };
  }, [refreshProfile]);

  const login = React.useCallback(
    async (loginValue: string, password: string) => {
      const auth = await apiFetch<AuthTokens>('/auth/login', {
        method: 'POST',
        body: JSON.stringify({
          login: loginValue,
          password,
          ...buildAuthDevicePayload(),
        })
      });
      if (auth.pinVerified) {
        await establishSession(auth);
      } else {
        persistPartialAuth(auth);
        markPinIdleWatch(auth.user.id);
        setHasStoredSession(true);
      }
      return auth;
    },
    [establishSession]
  );

  const identifyLogin = React.useCallback(async (loginValue: string) => {
    return apiFetch<IdentifyResponse>('/auth/identify', {
      method: 'POST',
      body: JSON.stringify({ login: loginValue })
    });
  }, []);

  const sendLoginOtp = React.useCallback(async (recipient: string, channel?: string) => {
    return apiFetch<OtpSendResponse>('/auth/otp/send', {
      method: 'POST',
      body: JSON.stringify({ recipient, channel })
    });
  }, []);

  const verifyLoginOtp = React.useCallback(
    async (recipient: string, code: string) => {
      const response = await apiFetch<PasswordlessAuthResponse>('/auth/otp/verify', {
        method: 'POST',
        body: JSON.stringify({
          recipient,
          code,
          ...buildAuthDevicePayload(),
        })
      });
      if (response.auth?.pinVerified) {
        await establishSession(response.auth);
      } else if (response.auth) {
        persistPartialAuth(response.auth);
        markPinIdleWatch(response.auth.user.id);
        setHasStoredSession(true);
      }
      return response;
    },
    [establishSession]
  );

  const loginWithPassword = React.useCallback(
    async (loginValue: string, passwordValue: string) => {
      const auth = await apiFetch<AuthTokens>('/auth/login/password', {
        method: 'POST',
        body: JSON.stringify({
          login: loginValue,
          password: passwordValue,
          ...buildAuthDevicePayload(),
        })
      });
      if (auth.pinVerified) {
        await establishSession(auth);
      } else {
        persistPartialAuth(auth);
        markPinIdleWatch(auth.user.id);
        setHasStoredSession(true);
      }
      return auth;
    },
    [establishSession]
  );

  const loginWithLdap = React.useCallback(
    async (username: string, passwordValue: string) => {
      const auth = await apiFetch<AuthTokens>('/auth/ldap/login', {
        method: 'POST',
        body: JSON.stringify({
          username,
          password: passwordValue,
          ...buildAuthDevicePayload(),
        })
      });
      if (auth.pinVerified) {
        await establishSession(auth);
      } else {
        persistPartialAuth(auth);
        markPinIdleWatch(auth.user.id);
        setHasStoredSession(true);
      }
      return auth;
    },
    [establishSession]
  );

  const beginTotpLogin = React.useCallback(async (recipient: string) => {
    const response = await apiFetch<{ totpChallengeToken: string }>('/auth/totp/begin', {
      method: 'POST',
      body: JSON.stringify({
        recipient,
        ...buildAuthDevicePayload(),
      })
    });
    return response.totpChallengeToken;
  }, []);

  const verifyTotpLogin = React.useCallback(
    async (totpChallengeToken: string, code: string) => {
      const auth = await apiFetch<AuthTokens>('/auth/totp/verify', {
        method: 'POST',
        body: JSON.stringify({ totpChallengeToken, code })
      });
      if (auth.pinVerified) {
        await establishSession(auth);
      } else {
        persistPartialAuth(auth);
        markPinIdleWatch(auth.user.id);
        setHasStoredSession(true);
      }
      return auth;
    },
    [establishSession]
  );

  const applyLoginAuth = React.useCallback(
    async (auth: AuthTokens) => {
      if (auth.pinVerified) {
        await establishSession(auth);
      } else {
        persistPartialAuth(auth);
        setHasStoredSession(true);
        activatePinLock(auth.sessionId);
      }
      return auth;
    },
    [activatePinLock, establishSession]
  );

  const completePin = React.useCallback(
    async (sessionId: string, pin: string) => {
      const response = await apiFetch<PinVerificationResponse>('/auth/pin/verify', {
        method: 'POST',
        body: JSON.stringify({ sessionId, pin })
      });
      window.localStorage.setItem(AUTH_TOKEN_KEY, response.accessToken);
      window.localStorage.setItem(AUTH_SESSION_KEY, sessionId);
      setToken(response.accessToken);
      markPinIdleWatch(user?.id);

      try {
        await warmUpAndApplySession(response.accessToken);
      } catch (error) {
        if (!isGatewayUnavailableError(error)) {
          throw error;
        }
        const cachedUser = readCachedUserProfile();
        if (cachedUser) {
          setUser(cachedUser);
        }
        clearPinLock();
        setBootstrapError(null);
        initialBootstrapDoneRef.current = true;
        setIsApiReady(true);
        setIsSessionReady(true);
        setIsLoading(false);
      }

      if (isFedcmLoginPopup()) {
        await finalizeFedcmLogin(response.accessToken);
        return;
      }

      const postPinRedirect =
        typeof window !== 'undefined'
          ? resolvePostPinAuthRedirect(pathname, window.location.search)
          : null;

      if (postPinRedirect !== null) {
        if (shouldFinalizeFedcmLogin(pathname)) {
          await finalizeFedcmLogin(response.accessToken);
        }
        router.replace(postPinRedirect);
        return;
      }

      if (shouldFinalizeFedcmLogin(pathname)) {
        await finalizeFedcmLogin(response.accessToken);
      }
    },
    [pathname, router, user?.id, warmUpAndApplySession]
  );

  const handlePinUnlock = React.useCallback(
    async (pin: string) => {
      const sessionId = lockedSessionId ?? window.localStorage.getItem(AUTH_SESSION_KEY);
      if (!sessionId) {
        setPinError('Сессия не найдена. Войдите снова.');
        return;
      }

      setPinSubmitting(true);
      setPinError(null);
      try {
        await completePin(sessionId, pin);
      } catch (error) {
        setPinError(error instanceof Error ? error.message : 'Не удалось подтвердить PIN-код');
      } finally {
        setPinSubmitting(false);
      }
    },
    [completePin, lockedSessionId]
  );

  const register = React.useCallback(
    async (data: { displayName: string; login: string; password: string }) => {
      const isEmail = data.login.includes('@');
      await apiFetch<PublicUser>('/auth/register', {
        method: 'POST',
        body: JSON.stringify({
          displayName: data.displayName,
          password: data.password,
          ...(isEmail ? { email: data.login } : { phone: data.login })
        })
      });
      await login(data.login, data.password);
    },
    [login]
  );

  const isBootstrapping =
    !isPinLocked &&
    Boolean(hasStoredSession || token) &&
    (isLoading || !isApiReady || !isSessionReady);

  React.useEffect(() => {
    if (!isBootstrapping || bootstrapError) return;
    const timer = window.setTimeout(() => {
      setBootstrapError(
        'Сервер API не отвечает. Если недавно перезапускали контейнеры — подождите 10–20 секунд и нажмите «Повторить».'
      );
    }, 18000);
    return () => window.clearTimeout(timer);
  }, [bootstrapError, isBootstrapping]);

  return (
    <AuthContext.Provider
      value={{
        user,
        token,
        isLoading,
        isApiReady,
        isSessionReady,
        isContentReady: isSessionReady && isApiReady,
        isPinLocked,
        hasStoredSession,
        login,
        identifyLogin,
        sendLoginOtp,
        verifyLoginOtp,
        loginWithPassword,
        loginWithLdap,
        beginTotpLogin,
        verifyTotpLogin,
        completePin,
        applyLoginAuth,
        applyUserPatch,
        register,
        refreshProfile,
        logout
      }}
    >
      {isBootstrapping ? (
        <AppBootstrapScreen
          error={bootstrapError}
          onRetry={() => {
            setBootstrapError(null);
            initialBootstrapDoneRef.current = false;
            invalidateApiGatewayReady();
            resetGatewayCircuit();
            setIsLoading(true);
            setIsApiReady(false);
            setIsSessionReady(false);
            void refreshProfile();
          }}
        />
      ) : (
        children
      )}
      <PinLockModal open={isPinLocked} isSubmitting={pinSubmitting} error={pinError} onSubmit={handlePinUnlock} />
      <FedcmApiLoginStatusBridge
        active={isSessionReady && hasStoredSession && Boolean(token)}
        accessToken={token}
        publicApiUrl={publicApiUrl}
      />
    </AuthContext.Provider>
  );
}

export function useAuth() {
  const context = React.useContext(AuthContext);
  if (!context) {
    throw new Error('useAuth должен использоваться внутри AuthProvider');
  }
  return context;
}
