'use client';
import { FunctionComponent, ReactNode, useEffect } from 'react';
import { useRouter } from 'next/navigation';
import { useLocale, useTranslations } from 'next-intl';
import { useSnackbar } from 'notistack';

import { type AppRole } from '@/constants';
import { useRoleHydration } from '@/services/auth';
import { resolveRoleDestination } from '@/services/auth/routing';
import AuthSplash from './AuthSplash';
import AuthAccountError from './AuthAccountError';

interface RoleGuardProps {
  /**
   * The actor shell being entered. When set, a session that lacks the role is **redirected** to its real
   * destination (chrome UX, not security — the server still authorizes every endpoint). Omit for a scope
   * not keyed on `AppRole` (the partner portal, which self-gates via `useMyPartnerCenter`); the guard then
   * only hardens hydration — a neutral loading/error state instead of the raw shell while `/me` resolves.
   */
  expected?: AppRole;
  children: ReactNode;
}

/**
 * The client-side role-aware navigation guard for the private shells (refinement-phase-2). It gates a shell
 * on the resolved-vs-pending role state (`useRoleHydration`) so the wrong actor app never renders:
 *  - **loading** — a brand splash while `/me` is in flight (never the customer shell as a stand-in);
 *  - **error** — an explicit account-error recovery when `/me` failed (never a silent customer fallback);
 *  - **role mismatch** — redirect to the caller's real app (`resolveRoleDestination`, the single source of
 *    "which app") with a toast, rather than rendering a shell they lack the role for.
 *
 * A dual customer+nurse session holds both roles, so it passes either shell's guard and can move freely
 * between the family and nurse apps.
 * @component RoleGuard
 */
const RoleGuard: FunctionComponent<RoleGuardProps> = ({ expected, children }) => {
  const t = useTranslations('auth');
  const router = useRouter();
  const locale = useLocale();
  const { enqueueSnackbar } = useSnackbar();
  const hydration = useRoleHydration();

  const me = hydration.status === 'ready' ? hydration.me : null;
  const appRoles = hydration.status === 'ready' ? hydration.appRoles : null;
  const allowed = !expected || (appRoles?.includes(expected) ?? false);
  // Stable across renders for a given identity (a string), so the redirect effect fires once, not per render.
  const redirectTo = me && !allowed ? `/${locale}${resolveRoleDestination(me)}` : null;

  useEffect(() => {
    if (!redirectTo) return;
    enqueueSnackbar(t('guard_denied'), { variant: 'warning' });
    router.replace(redirectTo);
  }, [redirectTo, enqueueSnackbar, t, router]);

  if (hydration.status === 'loading') return <AuthSplash message={t('routing_title')} />;
  if (hydration.status === 'error')
    return <AuthAccountError onRetry={hydration.retry} isRetrying={hydration.isRetrying} />;
  // Role mismatch — hold the neutral splash while the redirect above navigates away.
  if (!allowed) return <AuthSplash message={t('routing_title')} />;
  return <>{children}</>;
};

export default RoleGuard;
