"use client";

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

import { EmptyState } from "@/components/ui/empty-state";
import { Skeleton } from "@/components/ui/skeleton";
import { usePermissions } from "@/hooks/auth/usePermissions";
import { useTranslation } from "@/hooks/useTranslation";
import { getDashboardPath } from "@/lib/constants/nav";
import { ROUTES } from "@/lib/constants/routes";
import type { AccessCheck } from "@/types/rbac";

type RouteGuardProps = AccessCheck & {
  children: ReactNode;
  /**
   * What to do when the user lacks access:
   *  - "redirect" (default) — send them to `redirectTo` (their dashboard by default).
   *  - "fallback" — stay on the route and render the no-access `fallback`.
   */
  onDenied?: "redirect" | "fallback";
  /** Redirect target for `onDenied="redirect"`. Defaults to the user's dashboard. */
  redirectTo?: string;
  /** Rendered for `onDenied="fallback"`. Defaults to a no-access EmptyState. */
  fallback?: ReactNode;
};

/**
 * Route/section-level access gate. Wrap a page's content (inside the panel
 * layout) to require a finer permission than the coarse admin-vs-provider split
 * that `proxy.ts` already enforces.
 *
 *   <RouteGuard permission="provider-verify"><ProviderReviewPage /></RouteGuard>
 *   <RouteGuard anyOf={["catalog-update"]} onDenied="fallback">…</RouteGuard>
 *
 * `proxy.ts` remains the first line for the panel split (it runs on the edge from
 * the JWT role, which has no permissions); this adds per-page permission checks
 * on the client. Like `<Can>`, it gates UI only — the backend must still enforce.
 */
export function RouteGuard({
  children,
  onDenied = "redirect",
  redirectTo,
  fallback,
  ...access
}: RouteGuardProps) {
  const router = useRouter();
  const { t } = useTranslation();
  const { check, isLoading, role } = usePermissions();
  const allowed = check(access);

  const target =
    redirectTo ?? (role ? getDashboardPath(role) : ROUTES.AUTH.LOGIN);

  useEffect(() => {
    if (!isLoading && !allowed && onDenied === "redirect") {
      router.replace(target);
    }
  }, [isLoading, allowed, onDenied, target, router]);

  // Hold the frame while resolving so neither the gated content nor the
  // fallback flashes before we know the answer.
  if (isLoading) {
    return <Skeleton className="h-40 w-full" />;
  }

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

  if (onDenied === "fallback") {
    return (
      <>
        {fallback ?? (
          <EmptyState
            title={t("rbac.noAccess.title")}
            description={t("rbac.noAccess.description")}
          />
        )}
      </>
    );
  }

  // redirect in flight → render nothing to avoid flashing the gated content
  return null;
}
