"use client";

/**
 * MIRROR of main frontend `frontend/src/components/auth/RouteGuard.tsx` —
 * keep in sync manually.
 *
 * Особенность tradein-mvp: Next.js basePath=/trade-in (см. `next.config.ts`).
 * `usePathname()` возвращает путь БЕЗ basePath — например на странице
 * `gendsgn.ru/trade-in/scrapers/avito` хук вернёт `/scrapers/avito`.
 * RBAC config (`auth/roles.yaml`) использует абсолютные пути сайта
 * (`/trade-in/**`, `/trade-in/api/v1/admin/**`), поэтому перед проверкой
 * isPathAllowed мы префиксим pathname через NEXT_PUBLIC_BASE_PATH.
 *
 * #2555 login redirect: `router.push()` (как и `usePathname()`) работает в
 * пространстве путей БЕЗ basePath — Next сам префиксит basePath на навигации
 * (см. `next.config.ts` комментарий `basePath`). Поэтому `next=` в query
 * строится из `rawPath` (БЕЗ basePath), а не `absolutePath` — иначе
 * `/login/page.tsx` сделал бы `router.push("/trade-in/history")`, и Next
 * задвоил бы префикс в `/trade-in/trade-in/history`.
 */

import { useRouter, usePathname } from "next/navigation";
import { useEffect } from "react";

import { NoAccessScreen } from "@/components/auth/NoAccessScreen";
import { HTTPError } from "@/lib/api";
import { isPathAllowed } from "@/lib/isPathAllowed";
import { useMe } from "@/lib/useMe";

const BASE_PATH = process.env.NEXT_PUBLIC_BASE_PATH ?? "";
// #801: dev/CI-only preview-маршрут (a11y/lighthouse) рендерится оффлайн без RBAC.
const ENABLE_PREVIEW = process.env.NEXT_PUBLIC_ENABLE_PREVIEW === "1";

interface RouteGuardProps {
  children: React.ReactNode;
}

export function RouteGuard({ children }: RouteGuardProps) {
  const rawPath = usePathname() ?? "/";
  const router = useRouter();
  // Абсолютный путь сайта: BASE_PATH + rawPath. Аккуратно с двойным слэшем
  // на `/`: `BASE_PATH = "/trade-in"` + `"/"` → `/trade-in/` (ок).
  const absolutePath = BASE_PATH
    ? `${BASE_PATH}${rawPath === "/" ? "/" : rawPath}`
    : rawPath;
  const { data, isLoading, error } = useMe();

  // #2555: /login сам себя не гейтит — иначе редирект-петля (401 на /me →
  // редирект на /login → RouteGuard на /login опять видит 401 → редирект…).
  const isLoginPage = rawPath === "/login";

  // Prod-only: сессия истекла/отсутствует → уводим на логин вместо старого
  // NoAccessScreen variant="session". Редирект — побочный эффект (нельзя
  // router.push во время рендера), поэтому useEffect; пока он не сработал,
  // рендерим null (см. return ниже), чтобы не мигал старый contents.
  const shouldRedirectToLogin =
    !isLoginPage &&
    process.env.NODE_ENV === "production" &&
    error instanceof HTTPError &&
    error.status === 401;

  useEffect(() => {
    if (!shouldRedirectToLogin) return;
    // PR #2562 review finding 3: deep-links carry их state в query (`/v2?id=
    // <uuid>` — см. next.config.ts redirect comment про restore-by-id). Без
    // `window.location.search` юзер, чья сессия истекла mid-session на такой
    // ссылке, после логина попадал бы на голый `/v2` и терял отчёт. Effect
    // — гарантированно client-side (useEffect тело никогда не бежит на SSR),
    // поэтому `window` тут безопасен без typeof-guard.
    const next = `${rawPath}${window.location.search}`;
    router.push(`/login?next=${encodeURIComponent(next)}`);
  }, [shouldRedirectToLogin, rawPath, router]);

  // #801: preview-страница самодостаточна (свой QueryClient с фейковым me),
  // RBAC к ней не применяем. Только под флагом — в проде по умолчанию выключено.
  if (ENABLE_PREVIEW && rawPath.startsWith("/ui-preview")) {
    return <>{children}</>;
  }

  if (isLoginPage) {
    return <>{children}</>;
  }

  if (isLoading) return null;

  if (error instanceof HTTPError && error.status === 401) {
    // Dev without Caddy: 401 is normal, mount the app so local dev works.
    if (process.env.NODE_ENV !== "production") return <>{children}</>;
    // Prod: редирект уже запущен эффектом выше — ничего не рендерим, пока
    // навигация не завершится (mounting children on 401 causes TanStack
    // Query re-subscribe storm, см. историю до #2555 в git blame).
    return null;
  }

  if (error instanceof HTTPError && error.status === 403) {
    return <NoAccessScreen variant="user" />;
  }

  if (error) {
    // Не 401/403 — 500 / сетевой сбой / таймаут. НЕ переиспользуем variant="user"
    // (текст «аккаунт не привязан к роли» вводит в заблуждение при техническом
    // сбое) — отдельный экран с честной формулировкой + reload CTA.
    return <NoAccessScreen variant="error" />;
  }

  if (!data) return null;

  // Пробный доступ закончился (#praktika): role=expired → спец-экран, а не generic path-deny.
  if (data.role === "expired") {
    return <NoAccessScreen variant="trial" />;
  }

  if (!isPathAllowed(data.allowed_paths, data.deny_paths, absolutePath)) {
    return <NoAccessScreen variant="path" path={absolutePath} />;
  }

  return <>{children}</>;
}
