"use client";

import * as React from "react";
import { Search, Plus, ShieldCheck, KeyRound, X, Info } from "lucide-react";
import { toast } from "sonner";
import { useSession } from "next-auth/react";
import {
  useGetUserQuery,
  useGetPermissionsQuery,
  useSetUserPermissionsMutation,
} from "@/api/rtk";
import { getApiEntityId } from "@/api/permissions/types";
import type { Permission } from "@/api/permissions/types";
import type { AddPermissionDialogProps } from "@/components/users/types";
import {
  Dialog,
  DialogContent,
  DialogHeader,
  DialogTitle,
  DialogDescription,
} from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Loading } from "@/components/ui/loading";
import { Checkbox } from "@/components/ui/checkbox";
import { cn } from "@/lib/utils";
import { filterSalesTargetCatalogPermissions } from "@/lib/sales-target-catalog";

const ACTION_COLORS: Record<string, string> = {
  create: "bg-emerald-50 text-emerald-700 border-emerald-200",
  read: "bg-blue-50 text-blue-700 border-blue-200",
  update: "bg-amber-50 text-amber-700 border-amber-200",
  delete: "bg-red-50 text-red-700 border-red-200",
  manage: "bg-purple-50 text-purple-700 border-purple-200",
};

function actionBadgeClass(action: string): string {
  return ACTION_COLORS[action.toLowerCase()] ?? "bg-gray-50 text-gray-700 border-gray-200";
}

function formatResourceTitle(resource: string): string {
  return resource
    .split("_")
    .filter(Boolean)
    .map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase())
    .join(" ");
}

/** Align legacy QA_COMPLIANCE role rows with catalog CLIENT_MEETING_VERIFICATION. */
function normalizeResourceKey(resource: string): string {
  const r = String(resource).trim().toUpperCase();
  return r === "QA_COMPLIANCE" ? "CLIENT_MEETING_VERIFICATION" : r;
}

/** Stable key for matching catalog permissions to role grants (resource + action). */
function permissionKey(p: Pick<Permission, "resource" | "action">): string {
  return `${normalizeResourceKey(String(p.resource))}.${String(p.action).toUpperCase()}`;
}

export const AddPermissionDialog = React.memo(({
  userId,
  isOpen,
  onOpenChange,
  onBusyChange,
}: AddPermissionDialogProps) => {
  const { update: updateSession } = useSession();
  const [search, setSearch] = React.useState("");
  const [tempSelectedIds, setTempSelectedIds] = React.useState<Set<string>>(new Set());

  // 1. Data fetching
  const { data: user, isLoading: userLoading } = useGetUserQuery(userId, { skip: !isOpen });
  const { data: permissionsRaw = [], isLoading: permsLoading } = useGetPermissionsQuery(undefined, { skip: !isOpen });
  const allPermissions = React.useMemo(
    () => filterSalesTargetCatalogPermissions(permissionsRaw),
    [permissionsRaw],
  );
  const [setUserPermissions, { isLoading: isUpdating }] = useSetUserPermissionsMutation();

  // 2. Compute inherited and existing custom permissions
  const { rolePermissionKeys, savedCustomIds } = React.useMemo(() => {
    const rKeys = new Set<string>();
    const cIds = new Set<string>();

    if (user) {
      // Role permissions (inherited)
      const role = user.role;
      if (role && typeof role !== "string" && Array.isArray(role.permissions)) {
        role.permissions.forEach((p) => {
          if (typeof p === "object" && p !== null && "action" in p && "resource" in p) {
            rKeys.add(permissionKey(p as Permission));
          }
        });
      }

      // Custom permissions (already granted)
      if (Array.isArray(user.customPermissions)) {
        user.customPermissions.forEach((p) => {
          const pid = getApiEntityId(p);
          if (pid) cIds.add(pid);
        });
      }
    }

    return { rolePermissionKeys: rKeys, savedCustomIds: cIds };
  }, [user]);

  const permissionMatchesSearch = React.useCallback(
    (p: Permission, q: string) => {
      if (!q) return true;
      const haystack = [
        p.resource,
        p.action,
        `${p.resource}.${p.action}`,
        formatResourceTitle(p.resource),
        p.description ?? "",
      ]
        .join(" ")
        .toLowerCase();
      return haystack.includes(q);
    },
    [],
  );

  // 3. Filter and group available permissions
  const availablePermissions = React.useMemo(() => {
    const q = search.trim().toLowerCase();
    return allPermissions.filter((p) => {
      const pid = getApiEntityId(p);
      if (!pid) return false;
      if (savedCustomIds.has(pid)) return false;
      if (rolePermissionKeys.has(permissionKey(p))) return false;
      return permissionMatchesSearch(p, q);
    });
  }, [
    allPermissions,
    savedCustomIds,
    rolePermissionKeys,
    search,
    permissionMatchesSearch,
  ]);

  const inheritedMatchesSearch = React.useMemo(() => {
    const q = search.trim().toLowerCase();
    if (!q) return [];
    return allPermissions.filter((p) => {
      const pid = getApiEntityId(p);
      if (!pid) return false;
      if (savedCustomIds.has(pid)) return false;
      if (!rolePermissionKeys.has(permissionKey(p))) return false;
      return permissionMatchesSearch(p, q);
    });
  }, [
    allPermissions,
    savedCustomIds,
    rolePermissionKeys,
    search,
    permissionMatchesSearch,
  ]);

  const groupedAvailable = React.useMemo(() => {
    const map = new Map<string, Permission[]>();
    for (const p of availablePermissions) {
      const key = p.resource || "OTHER";
      const list = map.get(key) || [];
      list.push(p);
      map.set(key, list);
    }
    // Sort resources and actions
    const sortedKeys = Array.from(map.keys()).sort((a, b) => a.localeCompare(b));
    const sortedMap = new Map<string, Permission[]>();
    for (const key of sortedKeys) {
      sortedMap.set(key, map.get(key)!.sort((a, b) => a.action.localeCompare(b.action)));
    }
    return sortedMap;
  }, [availablePermissions]);

  // Reset selection when dialog opens/closes
  React.useEffect(() => {
    if (!isOpen) {
      setTempSelectedIds(new Set());
      setSearch("");
    }
  }, [isOpen]);

  // 4. Handlers
  const togglePermission = React.useCallback((pid: string) => {
    setTempSelectedIds((prev) => {
      const next = new Set(prev);
      if (next.has(pid)) next.delete(pid);
      else next.add(pid);
      return next;
    });
  }, []);

  const toggleResourceGroup = React.useCallback((resource: string, perms: Permission[]) => {
    const pids = perms.map(p => getApiEntityId(p)).filter(Boolean) as string[];
    const allInGroupSelected = pids.every(id => tempSelectedIds.has(id));

    setTempSelectedIds((prev) => {
      const next = new Set(prev);
      if (allInGroupSelected) {
        pids.forEach(id => next.delete(id));
      } else {
        pids.forEach(id => next.add(id));
      }
      return next;
    });
  }, [tempSelectedIds]);

  const handleBatchGrant = React.useCallback(async () => {
    if (!user || tempSelectedIds.size === 0) return;
    const nextIds = [...Array.from(savedCustomIds), ...Array.from(tempSelectedIds)];
    onBusyChange?.(true);
    try {
      await setUserPermissions({ id: userId, permissionIds: nextIds }).unwrap();
      toast.success(`${tempSelectedIds.size} permissions granted successfully`);
      await updateSession();
      onOpenChange(false);
    } catch {
      toast.error("Failed to grant permissions");
    } finally {
      onBusyChange?.(false);
    }
  }, [
    userId,
    user,
    savedCustomIds,
    tempSelectedIds,
    setUserPermissions,
    updateSession,
    onOpenChange,
    onBusyChange,
  ]);

  const handleClose = React.useCallback(() => {
    if (isUpdating) return;
    onOpenChange(false);
  }, [isUpdating, onOpenChange]);

  const handleOpenChange = React.useCallback(
    (open: boolean) => {
      if (!open) handleClose();
    },
    [handleClose],
  );

  return (
    <Dialog open={isOpen} onOpenChange={handleOpenChange}>
      <DialogContent className="sm:max-w-[700px] p-0 overflow-hidden rounded-3xl border-none shadow-2xl bg-white/95 backdrop-blur-xl">
        <div className="absolute inset-0 bg-linear-to-br from-accent/5 via-transparent to-accent/5 pointer-events-none" />

        <DialogHeader className="px-6 pt-6 pb-4 border-b border-[#eaecf0] relative z-10">
          <div className="flex items-center gap-3 mb-1">
            <div className="flex h-10 w-10 items-center justify-center rounded-xl bg-accent/10 text-accent">
              <Plus size={20} />
            </div>
            <div>
              <DialogTitle className="text-xl font-bold font-['Lexend'] text-[#101828]">
                Add Custom Permissions
              </DialogTitle>
              <DialogDescription className="text-sm text-[#667085]">
                Select one or more permissions to grant directly to this user.
              </DialogDescription>
            </div>
          </div>
        </DialogHeader>

        <div className="p-6 space-y-5 relative z-10">
          {isUpdating && (
            <div
              className="absolute inset-0 z-20 flex flex-col items-center justify-center gap-3 rounded-b-3xl bg-white/85 backdrop-blur-sm"
              aria-busy="true"
              aria-live="polite"
            >
              <Loading className="h-9 w-9 text-accent" />
              <p className="text-sm font-medium text-[#344054]">Saving permissions…</p>
            </div>
          )}
          {/* Search bar */}
          <div className="relative">
            <Search size={18} className="absolute left-3.5 top-1/2 -translate-y-1/2 text-[#667085]" />
            <Input
              type="search"
              placeholder="Search by module or action (e.g. 'lead update')..."
              className="pl-11 h-12 rounded-2xl border-[#d0d5dd] bg-white/50 focus:bg-white transition-all text-sm"
              value={search}
              onChange={(e) => setSearch(e.target.value)}
              autoFocus
            />
          </div>

          {/* List display */}
          <div className="max-h-[200px] overflow-y-auto scrollbar-themed pr-1 -mr-1">
            {(userLoading || permsLoading) ? (
              <div className="py-12 flex flex-col items-center justify-center gap-3">
                <Loading className="h-8 w-8 text-accent" />
                <p className="text-sm text-[#667085]">Fetching catalog...</p>
              </div>
            ) : isUpdating ? null : availablePermissions.length === 0 ? (
              <div className="py-16 text-center space-y-3">
                <div className="mx-auto w-12 h-12 rounded-full bg-gray-50 flex items-center justify-center border border-gray-100">
                  <Info size={24} className="text-gray-400" />
                </div>
                <div>
                  <p className="text-[15px] font-semibold text-[#101828]">No permissions available</p>
                  <p className="text-sm text-[#667085] max-w-[320px] mx-auto mt-1">
                    {inheritedMatchesSearch.length > 0
                      ? "Matching permissions are already granted through this user's role (hidden here). Assign them on the role instead, or pick another permission."
                      : search
                        ? allPermissions.length === 0
                          ? "Permission catalog is empty. Create UPDATE + CLIENT_MEETING_VERIFICATION under Settings → Permissions first."
                          : "Try a different search term"
                        : "This user already has all catalog permissions via their role or direct grants."}
                  </p>
                </div>
                {inheritedMatchesSearch.length > 0 ? (
                  <div className="mx-auto max-w-md rounded-xl border border-[#eaecf0] bg-[#f9fafb] px-4 py-3 text-left">
                    <p className="text-[10px] font-bold uppercase tracking-wide text-[#667085] mb-2">
                      Already on role
                    </p>
                    <ul className="space-y-1">
                      {inheritedMatchesSearch.map((p) => {
                        const pid = getApiEntityId(p);
                        return (
                          <li
                            key={pid ?? permissionKey(p)}
                            className="font-mono text-[11px] text-[#344054]"
                          >
                            {p.action} · {formatResourceTitle(p.resource)}
                          </li>
                        );
                      })}
                    </ul>
                  </div>
                ) : null}
                {search && (
                  <Button variant="ghostAccent" size="sm" onClick={() => setSearch("")}>
                    Clear search
                  </Button>
                )}
              </div>
            ) : (
              <div className="space-y-6">
                {Array.from(groupedAvailable.entries()).map(([resource, perms]) => {
                  const pids = perms.map(p => getApiEntityId(p)).filter(Boolean) as string[];
                  const allSelected = pids.every(id => tempSelectedIds.has(id));

                  return (
                    <div key={resource} className="space-y-3">
                      <div className="flex items-center justify-between px-1">
                        <div className="flex items-center gap-2">
                          <span className="text-[11px] font-bold text-[#667085] uppercase tracking-widest font-mono">
                            {formatResourceTitle(resource)}
                          </span>
                          <div className="h-px w-24 bg-gradient-to-r from-[#eaecf0] to-transparent" />
                        </div>
                        <button
                          type="button"
                          onClick={() => toggleResourceGroup(resource, perms)}
                          className="text-[10px] font-semibold text-accent hover:underline"
                        >
                          {allSelected ? "Deselect Group" : "Select Group"}
                        </button>
                      </div>
                      <div className="grid grid-cols-1 sm:grid-cols-2 gap-2">
                        {perms.map((p) => {
                          const pid = getApiEntityId(p);
                          if (!pid) return null;
                          const isSelected = tempSelectedIds.has(pid);
                          return (
                            <div
                              key={pid}
                              className={cn(
                                "group flex items-center gap-3 p-3 rounded-2xl border transition-all cursor-pointer",
                                isSelected
                                  ? "border-accent bg-accent/5 shadow-sm"
                                  : "border-[#eaecf0] bg-white hover:border-accent/30"
                              )}
                              onClick={() => togglePermission(pid)}
                            >
                              <Checkbox
                                checked={isSelected}
                                onCheckedChange={() => togglePermission(pid)}
                                className="pointer-events-none select-none"
                              />
                              <div className="flex items-center gap-2 min-w-0">
                                <span className={cn(
                                  "inline-flex items-center rounded-full border px-2 py-0.5 text-[10px] font-bold capitalize shrink-0",
                                  actionBadgeClass(p.action)
                                )}>
                                  {p.action}
                                </span>
                                <span className="font-mono text-[11px] text-[#344054] truncate">
                                  {p.resource}.{p.action}
                                </span>
                              </div>
                            </div>
                          );
                        })}
                      </div>
                    </div>
                  );
                })}
              </div>
            )}
          </div>
        </div>

        <div className="p-6 bg-gray-50/50 border-t border-[#eaecf0] flex items-center justify-between relative z-10">
          <div className="flex flex-col gap-0.5">
            <div className="flex items-center gap-2 text-[11px] text-[#667085]">
              <ShieldCheck size={14} className="text-[#065f46]" />
              <span>{tempSelectedIds.size} permissions selected to grant.</span>
            </div>
            <p className="text-[10px] text-[#98a2b3]">Inherited role permissions are hidden.</p>
          </div>
          <div className="flex items-center gap-3">
            <Button variant="ghostMuted" size="md" onClick={handleClose}>
              Cancel
            </Button>
            <Button
              variant="accentGrantBatch"
              onClick={handleBatchGrant}
              disabled={isUpdating || tempSelectedIds.size === 0}
            >
              {isUpdating ? <Loading className="h-4 w-4" /> : <KeyRound size={16} />}
              Grant {tempSelectedIds.size > 0 ? `(${tempSelectedIds.size})` : "Selected"}
            </Button>
          </div>
        </div>
      </DialogContent>
    </Dialog>
  );
});

AddPermissionDialog.displayName = "AddPermissionDialog";
