"use client";

import * as React from "react";
import { measureElement, useVirtualizer } from "@tanstack/react-virtual";
import {
  getApiEntityId,
  type Permission as ApiPermission,
} from "@/api/permissions/types";
import { Can } from "@/components/providers/ability-provider";
import { Switch } from "@/components/ui/switch";
import { cn } from "@/lib/utils";

/** Max columns at xl+; must match grid `xl:grid-cols-*`. */
const PERMISSIONS_GRID_CHUNK = 4;

function permissionCode(p: ApiPermission): string {
  return `${p.resource}.${p.action}`;
}

type ResourceKey = ApiPermission["resource"];

export type FlatRow =
  | { kind: "header"; key: string; resource: ResourceKey; count: number }
  | { kind: "grid"; key: string; perms: ApiPermission[] };

export function flattenPermissionsForVirtual(
  groups: [ResourceKey, ApiPermission[]][],
): FlatRow[] {
  const out: FlatRow[] = [];
  for (const [resource, perms] of groups) {
    out.push({
      kind: "header",
      key: `h-${resource}`,
      resource,
      count: perms.length,
    });
    for (let i = 0; i < perms.length; i += PERMISSIONS_GRID_CHUNK) {
      out.push({
        kind: "grid",
        key: `g-${resource}-${i}`,
        perms: perms.slice(i, i + PERMISSIONS_GRID_CHUNK),
      });
    }
  }
  return out;
}

/** Space above each resource block after the first (matches header row `pt-*`). */
const BETWEEN_GROUPS_PT = 24; // pt-6

function estimateRowHeight(row: FlatRow | undefined, index: number): number {
  if (!row) return 40;
  if (row.kind === "header") {
    return 34 + (index > 0 ? BETWEEN_GROUPS_PT : 0);
  }
  // Up to 4 cells; may wrap at smaller breakpoints. Initial guess; `measureElement` refines.
  const n = row.perms.length;
  const rowsGuess = Math.ceil(n / 2);
  return Math.min(280, 12 + rowsGuess * 76);
}

export type VirtualizedPermissionsListProps = {
  permissionsByResource: [ResourceKey, ApiPermission[]][];
  selectedPermIds: string[];
  /** Role id for permission updates; toggles no-op if empty. */
  effectiveSelectedRoleId: string;
  selectedRoleName: string;
  updating: boolean;
  onToggle: (roleId: string, permId: string) => void;
  className?: string;
};

export function VirtualizedPermissionsList({
  permissionsByResource,
  selectedPermIds,
  effectiveSelectedRoleId,
  selectedRoleName,
  updating,
  onToggle,
  className,
}: VirtualizedPermissionsListProps) {
  const parentRef = React.useRef<HTMLDivElement>(null);

  const flatRows = React.useMemo(
    () => flattenPermissionsForVirtual(permissionsByResource),
    [permissionsByResource],
  );

  // eslint-disable-next-line react-hooks/incompatible-library -- TanStack Virtual
  const virtualizer = useVirtualizer({
    count: flatRows.length,
    getScrollElement: () => parentRef.current,
    estimateSize: (index) => estimateRowHeight(flatRows[index], index),
    overscan: 6,
    getItemKey: (index) => flatRows[index]?.key ?? index,
    measureElement,
  });

  React.useLayoutEffect(() => {
    virtualizer.measure();
    const id = requestAnimationFrame(() => virtualizer.measure());
    return () => cancelAnimationFrame(id);
  }, [flatRows.length, virtualizer]);

  return (
    <div
      ref={parentRef}
      className={cn(
        "flex-1 min-h-0 overflow-y-auto overflow-x-hidden scrollbar-themed",
        className,
      )}
    >
      <div
        className="relative w-full max-w-full"
        style={{ height: `${virtualizer.getTotalSize()}px` }}
      >
        {virtualizer.getVirtualItems().map((vi) => {
          const row = flatRows[vi.index];
          if (!row) return null;

          return (
            <div
              key={row.key}
              data-index={vi.index}
              ref={virtualizer.measureElement}
              className={cn(
                "left-0 w-full",
                row.kind === "header" && vi.index > 0 && "pt-6",
              )}
              style={{
                position: "absolute",
                top: 0,
                transform: `translateY(${vi.start}px)`,
              }}
            >
              {row.kind === "header" ? (
                <div className="flex flex-wrap items-baseline gap-2 border-b border-border/60 py-1">
                  <h3 className="text-[11px] font-extrabold text-accent uppercase tracking-wide">
                    {row.resource}
                  </h3>
                  <span className="text-[11px] font-medium text-muted-foreground">
                    {row.count} permission{row.count === 1 ? "" : "s"}
                  </span>
                </div>
              ) : (
                <div className="grid grid-cols-1 gap-x-2 gap-y-4 pb-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
                  {row.perms.map((perm) => {
                    const permissionId = getApiEntityId(perm);
                    if (!permissionId) return null;
                    const isEnabled = selectedPermIds.includes(permissionId);
                    const code = permissionCode(perm);

                    return (
                      <div
                        key={permissionId}
                        className={cn(
                          "flex min-w-0 items-center justify-between gap-2 rounded-xl border px-2.5 py-2",
                          isEnabled
                            ? "border-accent/20 bg-accent/[0.04]"
                            : "border-border bg-white",
                        )}
                      >
                        <div className="min-w-0 flex-1">
                          <p className="text-[13px] font-semibold text-text">{perm.action}</p>
                          <p
                            className="truncate font-mono text-[11px] text-muted-foreground"
                            title={code}
                          >
                            {code}
                          </p>
                        </div>
                        <Can
                          action="update"
                          subject="role"
                          fallback={
                            <Switch
                              checked={isEnabled}
                              disabled
                              className="shrink-0"
                              aria-label={code}
                            />
                          }
                        >
                          <Switch
                            className="shrink-0"
                            checked={isEnabled}
                            disabled={updating}
                            onCheckedChange={() => {
                              if (!effectiveSelectedRoleId) return;
                              void onToggle(effectiveSelectedRoleId, permissionId);
                            }}
                            aria-label={`${code} for ${selectedRoleName}`}
                          />
                        </Can>
                      </div>
                    );
                  })}
                </div>
              )}
            </div>
          );
        })}
      </div>
    </div>
  );
}
