"use client";

import * as React from "react";
import { useQueryState, parseAsString } from "nuqs";
import {
  useGetRolesQuery,
  useGetPermissionsQuery,
  useCreateRoleMutation,
  useUpdateRoleMutation,
  useDeleteRoleMutation,
} from "@/api/rtk";
import {
  getApiEntityId,
  type Role as ApiRole,
  type Permission as ApiPermission,
} from "@/api/permissions/types";
import { Can } from "@/components/providers/ability-provider";
import { useAuthToken } from "@/hooks/use-auth-token"
import { Shield, CheckCircle2, Pencil, Trash2, Search, Filter } from "lucide-react";
import { Loading } from "@/components/ui/loading";
import { NoDataFound } from "@/components/ui/no-data-found";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
  Dialog,
  DialogContent,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from "@/components/ui/dialog";
import {
  AlertDialog,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogTitle,
} from "@/components/ui/alert-dialog";
import { toast } from "sonner";
import { cn } from "@/lib/utils";
import { Switch } from "@/components/ui/switch";

function getPermissionId(p: ApiPermission | string): string {
  return typeof p === "string" ? p : getApiEntityId(p) ?? "";
}

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

/** ACTIVITY, DASHBOARD, and LEAD_HISTORY catalog rows are READ-only (view / stats / timeline API). */
function isReadOnlyCatalogNonReadPermission(
  p: Pick<ApiPermission, "action" | "resource">,
): boolean {
  const r = String(p.resource ?? "")
    .trim()
    .toUpperCase();
  if (r !== "ACTIVITY" && r !== "DASHBOARD" && r !== "LEAD_HISTORY") return false;
  return String(p.action ?? "")
    .trim()
    .toUpperCase() !== "READ";
}

/** EXCEL_IMPORT catalog is CREATE-only (bulk import). */
function isExcelImportCatalogNonCreatePermission(
  p: Pick<ApiPermission, "action" | "resource">,
): boolean {
  const r = String(p.resource ?? "")
    .trim()
    .toUpperCase();
  if (r !== "EXCEL_IMPORT") return false;
  return String(p.action ?? "")
    .trim()
    .toUpperCase() !== "CREATE";
}

/** CONTACT was removed from the catalog — hide stale rows from cached GET /permissions. */
function isContactCatalogRemoved(
  p: Pick<ApiPermission, "resource">,
): boolean {
  return String(p.resource ?? "").trim().toUpperCase() === "CONTACT";
}

/** COMPANY was removed from the Resource enum — hide stale rows from cache. */
function isCompanyCatalogRemoved(
  p: Pick<ApiPermission, "resource">,
): boolean {
  return String(p.resource ?? "").trim().toUpperCase() === "COMPANY";
}

function isSalesTargetLegacyCatalogPermission(
  p: Pick<ApiPermission, "resource" | "action">,
): boolean {
  if (String(p.resource ?? "").trim().toUpperCase() !== "SALES_TARGET") {
    return false;
  }
  const action = String(p.action ?? "").trim().toUpperCase();
  return action !== "READ" && action !== "MANAGE";
}

function isPermissionHiddenFromRolesGrid(p: ApiPermission): boolean {
  return (
    isReadOnlyCatalogNonReadPermission(p) ||
    isExcelImportCatalogNonCreatePermission(p) ||
    isContactCatalogRemoved(p) ||
    isCompanyCatalogRemoved(p) ||
    isSalesTargetLegacyCatalogPermission(p)
  );
}

/** Sort actions in a predictable CRUD-ish order within each resource group. */
const ACTION_ORDER = ["CREATE", "READ", "UPDATE", "DELETE", "MANAGE"] as const;

function actionSortIndex(action: string): number {
  const u = action.toUpperCase();
  const idx = ACTION_ORDER.indexOf(u as (typeof ACTION_ORDER)[number]);
  return idx === -1 ? 999 : idx;
}

/** Human-readable heading for a permission resource (e.g. DEALS → Deals). */
function formatResourceGroupTitle(resource: string): string {
  if (resource.toUpperCase() === "DEALS") return "Deals";
  return resource
    .split("_")
    .filter(Boolean)
    .map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase())
    .join(" ");
}

function arePermissionIdSetsEqual(a: string[], b: string[]): boolean {
  if (a.length !== b.length) return false;
  const sa = [...a].sort();
  const sb = [...b].sort();
  return sa.every((v, i) => v === sb[i]);
}

function groupPermissionsByResource(perms: ApiPermission[]): {
  resource: string;
  permissions: ApiPermission[];
}[] {
  const map = new Map<string, ApiPermission[]>();
  for (const p of perms) {
    const key = p.resource || "OTHER";
    const list = map.get(key);
    if (list) list.push(p);
    else map.set(key, [p]);
  }
  const resources = Array.from(map.keys()).sort((a, b) =>
    a.localeCompare(b, undefined, { sensitivity: "base" })
  );
  return resources.map((resource) => {
    const permissions = (map.get(resource) ?? []).slice().sort((a, b) => {
      const da = actionSortIndex(a.action);
      const db = actionSortIndex(b.action);
      if (da !== db) return da - db;
      return a.action.localeCompare(b.action, undefined, { sensitivity: "base" });
    });
    return { resource, permissions };
  });
}

type RoleDialogMode = "create" | "edit" | null;

interface RoleItemProps {
  role: ApiRole;
  isActive: boolean;
  onClick: (id: string | null) => void;
  onEdit: (role: ApiRole) => void;
  onDelete: (id: string) => void;
}

const RoleItem = React.memo(({ role, isActive, onClick, onEdit, onDelete }: RoleItemProps) => {
  const roleId = getApiEntityId(role);
  return (
    <div
      onClick={() => onClick(roleId ?? null)}
      className={cn(
        "p-4 rounded-[18px] border transition-all cursor-pointer group relative",
        isActive
          ? "bg-white border-accent shadow-premium"
          : "bg-white/50 border-border hover:border-gray-300"
      )}
    >
      <div className="flex items-start justify-between mb-2">
        <div className="flex items-center gap-3">
          <div className="size-10 rounded-xl flex items-center justify-center text-white bg-accent">
            <Shield size={20} />
          </div>
          <div className="flex flex-col">
            <span className="text-[14px] font-bold text-text font-['Lexend']">
              {role.name}
            </span>
            <span className="text-[11px] text-gray-700">
              {(role.permissions?.length ?? 0)} permissions
            </span>
          </div>
        </div>
        <Can action="update" subject="role">
          <div className="flex items-center gap-1 opacity-100" onClick={(e) => e.stopPropagation()}>
            <Button
              variant="ghost"
              size="icon"
              className="h-8 w-8"
              onClick={(e) => {
                e.stopPropagation();
                onEdit(role);
              }}
            >
              <Pencil size={14} />
            </Button>
            <Button
              variant="ghost"
              size="icon"
              className="h-8 w-8 text-destructive hover:text-destructive"
              onClick={(e) => {
                e.stopPropagation();
                if (roleId) onDelete(roleId);
              }}
            >
              <Trash2 size={14} />
            </Button>
          </div>
        </Can>
      </div>
      <p className="text-[11px] text-gray-700 leading-relaxed mb-3">
        {role.description ?? "—"}
      </p>
    </div>
  );
});

RoleItem.displayName = "RoleItem";

interface PermissionItemProps {
  permission: ApiPermission;
  isIncluded: boolean;
  onToggle: (id: string) => void;
  disabled?: boolean;
}

const PermissionItem = React.memo(({ permission, isIncluded, onToggle, disabled }: PermissionItemProps) => {
  const permissionId = getApiEntityId(permission);
  if (!permissionId) return null;

  return (
    <div
      onClick={() => !disabled && onToggle(permissionId)}
      className={cn(
        "flex items-center justify-between p-4 rounded-2xl border transition-all select-none group/card",
        disabled ? "cursor-wait opacity-70" : "cursor-pointer",
        isIncluded
          ? "bg-accent/5 border-accent/20"
          : "bg-white/60 border-border hover:bg-white"
      )}
    >
      <div className="flex items-center gap-3 min-w-0">
        <div className="flex items-center shrink-0">
          <div
            className={cn(
              "size-5 rounded-md border flex items-center justify-center transition-all",
              isIncluded
                ? "bg-accent border-accent text-white"
                : "border-gray-300 group-hover/card:border-accent/40 bg-white"
            )}
          >
            {isIncluded && (
              <CheckCircle2 size={12} strokeWidth={3} />
            )}
          </div>
        </div>
        <div className="flex flex-col gap-0.5 min-w-0">
          <h4 className="text-[13px] font-bold text-text truncate">
            {permission.action}
          </h4>
          <p className="text-[11px] text-gray-700 font-mono truncate">
            {permissionCode(permission)}
          </p>
        </div>
      </div>
      <div
        className={cn(
          "size-6 rounded-full flex items-center justify-center transition-all shrink-0",
          isIncluded
            ? "bg-accent text-white"
            : "bg-bg border border-border text-transparent"
        )}
      >
        <CheckCircle2 size={16} />
      </div>
    </div>
  );
});

PermissionItem.displayName = "PermissionItem";

export default function RolesPermissionsPage() {
  const { token: accessToken } = useAuthToken();
  const { data: roles = [], isLoading: rolesLoading } = useGetRolesQuery(
    undefined,
    { skip: !accessToken }
  );
  const { data: allPermissions = [], isLoading: permsLoading } = useGetPermissionsQuery(
    undefined,
    { skip: !accessToken }
  );

  const permissionsCatalogForGrid = React.useMemo(
    () => allPermissions.filter((p) => !isPermissionHiddenFromRolesGrid(p)),
    [allPermissions],
  );
  const [createRole, { isLoading: creating }] = useCreateRoleMutation();
  const [updateRole, { isLoading: updating }] = useUpdateRoleMutation();
  const [deleteRole, { isLoading: deleting }] = useDeleteRoleMutation();

  /** Active role — synced to `?role=<id>` for shareable links and browser history. */
  const [roleParam, setRoleParam] = useQueryState(
    "role",
    parseAsString.withOptions({ shallow: true }),
  );

  /** Permission grid search — synced to `?q=` (replace + throttle avoids history spam). */
  const [permissionSearch, setPermissionSearch] = useQueryState(
    "q",
    parseAsString.withDefault("").withOptions({ shallow: true, throttleMs: 250 }),
  );

  /** Roles list search — synced to `?roleQ=`. */
  const [roleSearch, setRoleSearch] = useQueryState(
    "roleQ",
    parseAsString.withDefault("").withOptions({ shallow: true, throttleMs: 250 }),
  );

  const filteredRoles = React.useMemo(() => {
    const q = roleSearch.trim().toLowerCase();
    if (!q) return roles;
    return roles.filter(
      (role) =>
        role.name.toLowerCase().includes(q) ||
        (role.description && role.description.toLowerCase().includes(q))
    );
  }, [roles, roleSearch]);

  const selectedRoleId = React.useMemo(() => {
    if (!roles.length) return null;
    if (roleParam && roles.some((r) => getApiEntityId(r) === roleParam)) {
      return roleParam;
    }
    return getApiEntityId(roles[0]) ?? null;
  }, [roles, roleParam]);

  const selectedRole = React.useMemo(
    () =>
      roles.find((r) => getApiEntityId(r) === selectedRoleId) ?? roles[0] ?? null,
    [roles, selectedRoleId],
  );
  const [roleDialogOpen, setRoleDialogOpen] = React.useState(false);
  const [roleDialogMode, setRoleDialogMode] = React.useState<RoleDialogMode>(null);
  const [roleFormName, setRoleFormName] = React.useState("");
  const [roleFormDescription, setRoleFormDescription] = React.useState("");
  const [deleteRoleId, setDeleteRoleId] = React.useState<string | null>(null);
  const [showOnlySelected, setShowOnlySelected] = React.useState(false);
  const [selectedPermGridIds, setSelectedPermGridIds] = React.useState<string[]>([]);

  const waitingForToken = !accessToken;
  const isLoading = rolesLoading || permsLoading;

  const effectiveSelectedRoleId = getApiEntityId(selectedRole) ?? null;

  const savedPermissionIds = React.useMemo(() => {
    if (!selectedRole) return [];
    return (selectedRole.permissions ?? [])
      .map((p) => getPermissionId(p))
      .filter(Boolean);
  }, [selectedRole]);

  const permissionsDirty = React.useMemo(
    () => !arePermissionIdSetsEqual(selectedPermGridIds, savedPermissionIds),
    [selectedPermGridIds, savedPermissionIds]
  );

  const filteredPermissions = React.useMemo(() => {
    let filtered = permissionsCatalogForGrid;

    // Filter by search query
    const q = permissionSearch.trim().toLowerCase();
    if (q) {
      filtered = filtered.filter(
        (perm) =>
          perm.resource.toLowerCase().includes(q) ||
          perm.action.toLowerCase().includes(q) ||
          permissionCode(perm).toLowerCase().includes(q)
      );
    }

    // Filter by selected permissions if toggle is on
    if (showOnlySelected) {
      filtered = filtered.filter((perm) => {
        const permId = getApiEntityId(perm);
        return permId && selectedPermGridIds.includes(permId);
      });
    }

    return filtered;
  }, [permissionsCatalogForGrid, permissionSearch, showOnlySelected, selectedPermGridIds]);

  const permissionGroups = React.useMemo(
    () => groupPermissionsByResource(filteredPermissions),
    [filteredPermissions]
  );

  const handleSubmitRolePermissions = async () => {
    if (!effectiveSelectedRoleId || !permissionsDirty) return;
    try {
      await updateRole({
        id: effectiveSelectedRoleId,
        body: {
          permissions: selectedPermGridIds
            .map((id) => String(id))
            .filter((id) => {
              const row = allPermissions.find((x) => getApiEntityId(x) === id);
              if (!row) return true;
              return !isPermissionHiddenFromRolesGrid(row);
            }),
        },
      }).unwrap();
      toast.success("Permissions saved");
    } catch (e) {
      toast.error(
        e instanceof Error ? e.message : "Failed to save permissions"
      );
    }
  };

  const allFilteredPermissionIds = React.useMemo(
    () =>
      filteredPermissions
        .map((p) => getApiEntityId(p))
        .filter((id): id is string => Boolean(id)),
    [filteredPermissions]
  );

  const allFilteredIncludedInDraft = React.useMemo(() => {
    if (allFilteredPermissionIds.length === 0) return false;
    return allFilteredPermissionIds.every((id) =>
      selectedPermGridIds.includes(id)
    );
  }, [allFilteredPermissionIds, selectedPermGridIds]);

  const handleSelectAllFilteredPerms = () => {
    if (allFilteredPermissionIds.length === 0) return;
    if (allFilteredIncludedInDraft) {
      setSelectedPermGridIds((prev) =>
        prev.filter((id) => !allFilteredPermissionIds.includes(id))
      );
    } else {
      setSelectedPermGridIds((prev) => [
        ...new Set([...prev, ...allFilteredPermissionIds]),
      ]);
    }
  };

  const openCreateDialog = React.useCallback(() => {
    setRoleFormName("");
    setRoleFormDescription("");
    setRoleDialogMode("create");
    setRoleDialogOpen(true);
  }, []);

  const openEditDialog = React.useCallback(
    (role?: ApiRole) => {
      const target = role ?? selectedRole;
      if (!target) return;
      setRoleFormName(target.name);
      setRoleFormDescription(target.description ?? "");
      setRoleDialogMode("edit");
      setRoleDialogOpen(true);
      if (role) {
        const id = getApiEntityId(role);
        if (id) void setRoleParam(id, { shallow: true, history: "push" });
      }
    },
    [selectedRole, setRoleParam],
  );

  const handleRoleClick = React.useCallback(
    (id: string | null) => {
      const nextId = id ?? (roles[0] ? getApiEntityId(roles[0]) : null);
      if (nextId) void setRoleParam(nextId, { shallow: true, history: "push" });
    },
    [roles, setRoleParam],
  );

  const confirmDelete = React.useCallback((roleId: string) => setDeleteRoleId(roleId), []);
  const cancelDelete = React.useCallback(() => setDeleteRoleId(null), []);

  const handleTogglePermission = React.useCallback((id: string) => {
    setSelectedPermGridIds((prev) =>
      prev.includes(id)
        ? prev.filter((prevId) => prevId !== id)
        : [...prev, id]
    );
  }, []);

  const handleRoleDialogSubmit = async () => {
    const name = roleFormName.trim();
    if (!name) {
      toast.error("Role name is required");
      return;
    }
    if (roleDialogMode === "create") {
      try {
        const created = await createRole({ name, description: roleFormDescription.trim() || undefined }).unwrap();
        setRoleDialogOpen(false);
        const newId = getApiEntityId(created);
        if (newId) void setRoleParam(newId, { shallow: true, history: "push" });
        toast.success("Role created");
      } catch (e) {
        toast.error(e instanceof Error ? e.message : "Failed to create role");
      }
    } else if (roleDialogMode === "edit" && effectiveSelectedRoleId) {
      try {
        await updateRole({
          id: effectiveSelectedRoleId,
          body: { name, description: roleFormDescription.trim() || undefined },
        }).unwrap();
        setRoleDialogOpen(false);
        toast.success("Role updated");
      } catch (e) {
        toast.error(e instanceof Error ? e.message : "Failed to update role");
      }
    }
  };

  const handleDeleteConfirm = async () => {
    if (!deleteRoleId) return;
    try {
      await deleteRole(deleteRoleId).unwrap();
      if (effectiveSelectedRoleId === deleteRoleId) {
        const next = roles.find((r) => getApiEntityId(r) !== deleteRoleId);
        const nextId = getApiEntityId(next) ?? null;
        void setRoleParam(nextId, { shallow: true, history: "replace" });
      }
      setDeleteRoleId(null);
      toast.success("Role deleted");
    } catch (e) {
      toast.error(e instanceof Error ? e.message : "Failed to delete role");
      throw e;
    }
  };

  // Keep `?role=` valid: default to first role, or recover from stale / unknown ids (replace only).
  React.useEffect(() => {
    if (!roles.length) return;
    const firstId = getApiEntityId(roles[0]) ?? null;
    if (!roleParam) {
      if (firstId) void setRoleParam(firstId, { shallow: true, history: "replace" });
      return;
    }
    const ok = roles.some((r) => getApiEntityId(r) === roleParam);
    if (!ok && firstId) {
      void setRoleParam(firstId, { shallow: true, history: "replace" });
    }
  }, [roles, roleParam, setRoleParam]);

  // Draft permission ids: reset from server when switching role or when role data refreshes
  React.useEffect(() => {
    if (selectedRole) {
      const currentIds = (selectedRole.permissions ?? [])
        .map((p) => getPermissionId(p))
        .filter(Boolean)
        .filter((id) => {
          const row = allPermissions.find((x) => getApiEntityId(x) === id);
          if (!row) return true;
          return !isPermissionHiddenFromRolesGrid(row);
        });
      setSelectedPermGridIds(currentIds);
    } else {
      setSelectedPermGridIds([]);
    }
  }, [selectedRoleId, selectedRole, allPermissions]);

  const roleToDelete = deleteRoleId
    ? roles.find((r) => getApiEntityId(r) === deleteRoleId)
    : null;

  return (
    <div className="flex flex-col h-full min-h-0 gap-5 p-1 sm:p-2 md:p-4 animate-in fade-in duration-500 overflow-hidden">
      <div className="flex flex-col lg:flex-row justify-between items-start lg:items-center gap-4 border-b border-border/60 pb-6 shrink-0">
        <div className="flex flex-col">
          <h1 className="text-[21px] font-extrabold text-text font-['Lexend'] tracking-tight">
            Roles & Permissions
          </h1>
          <p className="text-[12px] text-gray-700 font-normal mt-0.5">
            Define access levels and assign permissions to roles.
          </p>
        </div>
        <div className="flex items-center gap-2">
          <Can action="update" subject="role">
            <Button size="sm" onClick={openCreateDialog}>
              <Shield size={16} className="mr-2" /> New Role
            </Button>
          </Can>
        </div>
      </div>

      {waitingForToken ? (
        <Loading message="Loading..." className="py-16" />
      ) : isLoading ? (
        <Loading message="Loading roles and permissions..." className="py-16" />
      ) : (
        <div className="grid grid-cols-1 xl:grid-cols-[400px_1fr] gap-6 flex-1 min-h-0 overflow-hidden">
          <div className="flex flex-col gap-3 min-h-0 overflow-hidden">
            <div className="relative shrink-0">
              <Search size={14} className="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground" />
              <Input
                type="search"
                placeholder="Search roles..."
                className="pl-9 pr-3 h-10 rounded-[18px] bg-white border-border/60"
                value={roleSearch}
                onChange={(e) => setRoleSearch(e.target.value)}
              />
            </div>
            <div className="flex flex-col gap-3 overflow-y-auto overflow-x-hidden pr-1 min-h-0 scrollbar-themed">
              {filteredRoles.length === 0 ? (
                <div className="py-8 text-center text-muted-foreground text-[12px]">
                  No roles match your search
                </div>
              ) : (
                filteredRoles.map((role) => (
                  <RoleItem
                    key={getApiEntityId(role) ?? role.name}
                    role={role}
                    isActive={effectiveSelectedRoleId === getApiEntityId(role)}
                    onClick={handleRoleClick}
                    onEdit={openEditDialog}
                    onDelete={confirmDelete}
                  />
                ))
              )}
            </div>
          </div>

          <div className="bg-white rounded-[24px] border border-border shadow-sm flex flex-col min-h-0 overflow-hidden">
            {selectedRole ? (
              <>
                <div className="p-6 border-b border-border bg-bg/30 shrink-0">
                  <div className="flex items-center justify-between">
                    <div className="flex flex-col">
                      <h2 className="text-[16px] font-bold text-text font-['Lexend'] flex items-center gap-2">
                        Permissions for {selectedRole.name}
                      </h2>
                      <p className="text-[11px] text-gray-700 mt-0.5">
                        Enable or disable capabilities for this role.
                      </p>
                    </div>
                    <Can action="update" subject="role">
                      <Button variant="outline" size="sm" onClick={() => openEditDialog()}>
                        <Pencil size={14} className="mr-2" /> Edit role
                      </Button>
                    </Can>
                  </div>
                  <div className="flex flex-col gap-3 mt-4">
                    <div className="flex flex-col sm:flex-row gap-3">
                      <div className="relative flex-1">
                        <Search size={14} className="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground" />
                        <Input
                          type="search"
                          placeholder="Search permissions (e.g. deal, create, user.read)..."
                          className="pl-9 pr-3"
                          value={permissionSearch}
                          onChange={(e) => setPermissionSearch(e.target.value)}
                        />
                      </div>
                      <div className="flex items-center gap-2 px-4 py-2 rounded-xl border border-border bg-white shrink-0">
                        <Filter size={14} className="text-muted-foreground" />
                        <span className="text-[12px] font-medium text-text">Selected only</span>
                        <Switch
                          checked={showOnlySelected}
                          onCheckedChange={setShowOnlySelected}
                        />
                      </div>
                      <Button
                        variant="outline"
                        size="sm"
                        className="h-9 px-4 rounded-xl text-[12px] font-bold gap-2"
                        onClick={handleSelectAllFilteredPerms}
                      >
                        <CheckCircle2 size={14} />
                        {allFilteredIncludedInDraft && allFilteredPermissionIds.length > 0
                          ? "Deselect filtered"
                          : "Select all filtered"}
                      </Button>
                    </div>
                  </div>
                </div>

                <div className="flex-1 min-h-0 overflow-y-auto overflow-x-hidden p-6 scrollbar-themed">
                  {filteredPermissions.length === 0 ? (
                    <NoDataFound
                      message="No permissions found"
                      description={showOnlySelected
                        ? "No selected permissions match your search"
                        : "Try adjusting your search query"}
                    />
                  ) : (
                    <div className="flex flex-col gap-8">
                      {permissionGroups.map(({ resource, permissions }) => (
                        <section
                          key={resource}
                          className="rounded-2xl border border-border/80 bg-bg/40 overflow-hidden"
                        >
                          <div className="flex flex-wrap items-baseline justify-between gap-2 px-4 py-3 border-b border-border/60 bg-white/80">
                            <div className="flex flex-col gap-0.5 min-w-0">
                              <h3 className="text-[13px] font-bold text-text font-['Lexend'] tracking-tight">
                                {formatResourceGroupTitle(resource)}
                              </h3>
                              <p className="text-[10px] font-mono text-muted-foreground uppercase tracking-wide truncate">
                                {resource}
                              </p>
                            </div>
                            <span className="text-[11px] font-medium text-muted-foreground tabular-nums shrink-0">
                              {permissions.length}{" "}
                              {permissions.length === 1 ? "permission" : "permissions"}
                            </span>
                          </div>
                          <div className="p-4 grid grid-cols-1 md:grid-cols-2 gap-3">
                            {permissions.map((perm) => (
                              <PermissionItem
                                key={getApiEntityId(perm)}
                                permission={perm}
                                isIncluded={selectedPermGridIds.includes(getApiEntityId(perm) ?? "")}
                                onToggle={handleTogglePermission}
                                disabled={updating}
                              />
                            ))}
                          </div>
                        </section>
                      ))}
                    </div>
                  )}
                </div>

                {permissionsDirty && (
                  <div className="shrink-0 border-t border-border bg-muted/20 dark:bg-muted/10 px-4 py-3 animate-in fade-in slide-in-from-bottom-2 duration-200">
                    <div className="flex flex-col sm:flex-row items-stretch sm:items-center gap-4 sm:gap-6">
                      <div className="flex items-center gap-3 sm:border-r border-border sm:pr-6 min-w-0">
                        <div className="size-9 rounded-xl bg-accent/10 flex items-center justify-center text-accent shrink-0">
                          <Shield size={20} />
                        </div>
                        <div className="flex flex-col min-w-0">
                          <span className="text-[13px] font-bold text-foreground">
                            Unsaved changes
                          </span>
                          <span className="text-[11px] text-muted-foreground font-medium line-clamp-2">
                            {selectedPermGridIds.length} permission
                            {selectedPermGridIds.length === 1 ? "" : "s"} for{" "}
                            {selectedRole.name} — click Submit to save.
                          </span>
                        </div>
                      </div>

                      <div className="flex flex-wrap items-center gap-2 sm:ml-auto">
                        <Can action="update" subject="role">
                          <Button
                            size="sm"
                            className="h-9 px-5 rounded-xl text-[12px] font-bold gap-2 shadow-sm"
                            onClick={() => void handleSubmitRolePermissions()}
                            disabled={updating}
                          >
                            <CheckCircle2 size={14} />
                            {updating ? "Saving…" : "Submit"}
                          </Button>
                        </Can>
                      </div>
                    </div>
                  </div>
                )}
              </>
            ) : (
              <div className="flex-1 flex items-center justify-center text-muted-foreground">
                Select a role or create one
              </div>
            )}
          </div>
        </div>
      )}

      {/* Create / Edit Role Dialog */}
      <Dialog open={roleDialogOpen} onOpenChange={setRoleDialogOpen}>
        <DialogContent>
          <DialogHeader>
            <DialogTitle>
              {roleDialogMode === "create" ? "New Role" : "Edit Role"}
            </DialogTitle>
          </DialogHeader>
          <div className="grid gap-4 py-4">
            <div className="grid gap-2">
              <Label htmlFor="role-name">Name</Label>
              <Input
                id="role-name"
                value={roleFormName}
                onChange={(e) => setRoleFormName(e.target.value)}
                placeholder="e.g. Sales Manager"
              />
            </div>
            <div className="grid gap-2">
              <Label htmlFor="role-desc">Description (optional)</Label>
              <Input
                id="role-desc"
                value={roleFormDescription}
                onChange={(e) => setRoleFormDescription(e.target.value)}
                placeholder="Brief description of this role"
              />
            </div>
          </div>
          <DialogFooter>
            <Button variant="outline" onClick={() => setRoleDialogOpen(false)}>
              Cancel
            </Button>
            <Button
              onClick={handleRoleDialogSubmit}
              disabled={creating || (roleDialogMode === "edit" && updating)}
            >
              {roleDialogMode === "create"
                ? creating ? "Creating..." : "Create"
                : updating ? "Saving..." : "Save"}
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>

      {/* Delete confirmation */}
      <AlertDialog
        open={!!deleteRoleId}
        onOpenChange={(open) => {
          if (!open && deleting) return;
          if (!open) cancelDelete();
        }}
      >
        <AlertDialogContent>
          <AlertDialogHeader>
            <AlertDialogTitle>Delete role</AlertDialogTitle>
            <AlertDialogDescription>
              Are you sure you want to delete the role &quot;{roleToDelete?.name}&quot;? This cannot be undone.
              Users with this role will need to be assigned a different role.
            </AlertDialogDescription>
          </AlertDialogHeader>
          <AlertDialogFooter>
            <AlertDialogCancel onClick={cancelDelete} disabled={deleting}>
              Cancel
            </AlertDialogCancel>
            <Button
              type="button"
              variant="destructive"
              onClick={() => void handleDeleteConfirm()}
              disabled={deleting}
            >
              {deleting ? "Deleting..." : "Delete"}
            </Button>
          </AlertDialogFooter>
        </AlertDialogContent>
      </AlertDialog>
    </div>
  );
}
