"use client";

import type { ReactNode } from "react";

import { usePermissions } from "@/hooks/auth/usePermissions";
import { cn } from "@/lib/utils";
import type { AccessCheck } from "@/types/rbac";

type CanProps = AccessCheck & {
  children: ReactNode;
  /**
   * How to render when the user is NOT allowed:
   *  - "hide" (default) — render `fallback` (or nothing).
   *  - "disable" — still render `children`, but wrapped inert + dimmed so they
   *    can't be interacted with (pointer, keyboard, or focus).
   */
  mode?: "hide" | "disable";
  /** Shown instead of children when denied in "hide" mode. Ignored in "disable" mode. */
  fallback?: ReactNode;
  /** Wrapper element for "disable" mode. Use "div" for block content (default "span"). */
  as?: "span" | "div";
  /** Extra classes on the "disable"-mode wrapper (e.g. `block w-full` for block layouts). */
  className?: string;
};

/**
 * Gate any subtree behind a permission / role / panel check — the reusable
 * component-level access wrapper.
 *
 *   <Can permission="provider-verify"><VerifyButton /></Can>
 *   <Can permission="provider-verify" mode="disable"><VerifyButton /></Can>
 *   <Can anyOf={["catalog-update", "provider-update"]} fallback={<ReadOnlyBadge />}>…</Can>
 *   <Can panel="ADMIN"><AdminOnlyWidget /></Can>
 *
 * While the profile is still loading, access is treated as denied — nothing
 * flashes in, and "disable" renders disabled — until it resolves.
 *
 * NOTE: this hides/disables UI only; it is not a security boundary. The backend
 * must enforce the same permission on the endpoint (see the RBAC notes).
 */
export function Can({
  children,
  mode = "hide",
  fallback = null,
  as = "span",
  className,
  ...access
}: CanProps) {
  const { check, isLoading } = usePermissions();
  const allowed = !isLoading && check(access);

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

  if (mode === "disable") {
    // `inert` natively blocks focus/pointer/keyboard for the whole subtree; the
    // visual dim + not-allowed cursor communicate the disabled state.
    const wrapperClassName = cn(
      "pointer-events-none cursor-not-allowed select-none opacity-50",
      className,
    );
    return as === "div" ? (
      <div inert aria-disabled="true" className={wrapperClassName}>
        {children}
      </div>
    ) : (
      <span inert aria-disabled="true" className={wrapperClassName}>
        {children}
      </span>
    );
  }

  return <>{fallback}</>;
}
