"use client";

import * as React from "react";
import { useParams, useRouter } from "next/navigation";
import Link from "next/link";
import { useSession } from "next-auth/react";
import {
  ArrowLeft, KeyRound, ShieldCheck, ShieldOff, Layers,
  Plus, X, UserCog, Search,
} from "lucide-react";
import { toast } from "sonner";
import equal from "fast-deep-equal";
import { useGetUserQuery, useGetPermissionsQuery, useSetUserPermissionsMutation } from "@/api/rtk";
import { filterSalesTargetCatalogPermissions } from "@/lib/sales-target-catalog";
import { getApiEntityId } from "@/api/permissions/types";
import type { ApiUser } from "@/api/users/types";
import type { Permission } from "@/api/permissions/types";
import { useAuthToken } from "@/hooks/use-auth-token";
import { useAbility } from "@/components/providers/ability-provider";
import { useAppDispatch, useAppSelector } from "@/store/hooks";
import { selectUserFromCache, setUserInCache } from "@/store";
import { Button } from "@/components/ui/button";
import { Loading } from "@/components/ui/loading";
import { NoDataFound } from "@/components/ui/no-data-found";
import { KPICard } from "@/components/ui/kpi-card";
import { Checkbox } from "@/components/ui/checkbox";
import { cn } from "@/lib/utils";
import { AddPermissionDialog } from "./add-permission-dialog";

// ─── helpers ────────────────────────────────────────────────────────────────

const EMPTY_ARRAY: any[] = [];

function getRoleName(role: ApiUser["role"]): string {
  if (!role) return "—";
  return typeof role === "string" ? role : role.name;
}

function getRolePermissions(user: ApiUser | undefined): Permission[] {
  const role = user?.role;
  if (!role || typeof role === "string") return [];
  const raw = role.permissions ?? [];
  if (!Array.isArray(raw)) return [];
  return raw.filter(
    (p): p is Permission =>
      typeof p === "object" && p != null && "action" in p && "resource" in p
  );
}

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

function groupByResource(perms: Permission[]): Map<string, Permission[]> {
  const map = new Map<string, Permission[]>();
  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]);
  }
  for (const [, list] of map) {
    list.sort((a, b) => a.action.localeCompare(b.action, undefined, { sensitivity: "base" }));
  }
  return map;
}

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

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";
}

// ─── sub-components ──────────────────────────────────────────────────────────

function PermissionCard({
  resource,
  perms,
  badge,
  selectedIds,
  onToggle,
}: {
  resource: string;
  perms: Permission[];
  badge?: React.ReactNode;
  selectedIds?: Set<string>;
  onToggle?: (pid: string) => void;
}) {
  return (
    <div className="rounded-[20px] border border-[#eaecf0] overflow-hidden bg-white/70 backdrop-blur-md shadow-sm flex flex-col">
      <div className="flex items-center justify-between gap-3 px-5 py-3.5 border-b border-[#eaecf0] bg-[#f9fafb]/80 shrink-0">
        <div className="min-w-0">
          <h2 className="font-['Lexend'] text-[13px] font-bold text-[#101828] uppercase tracking-wider truncate">
            {formatResourceTitle(resource)}
          </h2>
          <p className="font-mono text-[10px] text-[#667085] tracking-wide mt-0.5">{resource}</p>
        </div>
        <div className="flex items-center gap-2 shrink-0">
          {badge}
          <span className="text-[11px] font-semibold text-[#667085] bg-[#f2f4f7] border border-[#eaecf0] rounded-full px-2.5 py-0.5">
            {perms.length} {perms.length === 1 ? "action" : "actions"}
          </span>
        </div>
      </div>
      <ul className="divide-y divide-[#eaecf0]">
        {perms.map((p, idx) => {
          const pid = getApiEntityId(p);
          const isSelected = !!(pid && selectedIds?.has(pid));
          return (
            <li
              key={`${resource}-${p.action}-${idx}`}
              className={cn(
                "flex items-center justify-between gap-4 px-5 py-3 transition-colors",
                isSelected ? "bg-red-50/50" : "hover:bg-[#f9fafb]"
              )}
            >
              <div className="flex items-center gap-3">
                {onToggle && pid && (
                  <Checkbox
                    checked={isSelected}
                    onCheckedChange={() => onToggle(pid)}
                    className="border-[#d0d5dd]"
                  />
                )}
                <span className={cn(
                  "inline-flex items-center rounded-full border px-2.5 py-0.5 text-[11px] font-semibold capitalize shrink-0",
                  actionBadgeClass(p.action)
                )}>
                  {p.action}
                </span>
              </div>
              <span className="font-mono text-[11px] text-[#667085] shrink-0">
                {p.resource}.{p.action}
              </span>
            </li>
          );
        })}
      </ul>
    </div>
  );
}

// ─── main page ───────────────────────────────────────────────────────────────

export default function UserPermissionsPage() {
  const params = useParams();
  const router = useRouter();
  const ability = useAbility();
  const { token } = useAuthToken();
  const { update: updateSession, status: sessionStatus } = useSession();
  const dispatch = useAppDispatch();
  const id = typeof params.id === "string" ? params.id : "";

  const canRead = ability.can("read", "user");
  const canManageUser = ability.can("manage", "user");
  const canCreateUser = ability.can("create", "user");
  const canUpdateUser = ability.can("update", "user");
  /** Add / remove direct grants — same idea as Users list (create/manage/update on USER). */
  const canEditDirectGrants =
    canManageUser || canCreateUser || canUpdateUser;

  // Cache-first: read from persisted slice
  const cachedUser = useAppSelector((state) => selectUserFromCache(state, id));

  // Background fetch
  const { data: latestUser, isLoading, isError, error } = useGetUserQuery(id, {
    skip: !token || !id || !canRead,
  });

  // Use latest data if available, otherwise fallback to cache
  const user = latestUser || cachedUser;

  // Background validation & cache update
  React.useEffect(() => {
    if (latestUser) {
      if (!equal(cachedUser, latestUser)) {
        dispatch(setUserInCache(latestUser));
      }
    }
  }, [latestUser, cachedUser, dispatch]);

  const { data: allPermissionsData } = useGetPermissionsQuery(undefined, {
    skip: !token || !canEditDirectGrants,
  });
  const allPermissions = React.useMemo(
    () => filterSalesTargetCatalogPermissions(allPermissionsData ?? []),
    [allPermissionsData],
  );
  const [setUserPermissions, { isLoading: isSavingPermissions }] =
    useSetUserPermissionsMutation();
  const [permissionActionBusy, setPermissionActionBusy] = React.useState(false);
  const isPermissionBusy = isSavingPermissions || permissionActionBusy;

  // ── role permissions (read-only) ──
  const rolePerms = React.useMemo(() => {
    const fromObjects = getRolePermissions(user);
    if (fromObjects.length > 0) return fromObjects;
    const role = user?.role;
    if (!role || typeof role === "string") return [];
    const raw = role.permissions ?? [];
    if (!Array.isArray(raw)) return [];
    const ids = raw.filter((x): x is string => typeof x === "string");
    if (ids.length === 0 || allPermissions.length === 0) return [];
    const byId = new Map<string, Permission>();
    for (const p of allPermissions) {
      const pid = getApiEntityId(p);
      if (pid) byId.set(pid, p);
    }
    const out: Permission[] = [];
    for (const rid of ids) {
      const p = byId.get(rid);
      if (p) out.push(p);
    }
    return out;
  }, [user, allPermissions]);
  const roleGrouped = React.useMemo(() => groupByResource(rolePerms), [rolePerms]);
  const roleKeys    = React.useMemo(
    () => Array.from(roleGrouped.keys()).sort((a, b) => a.localeCompare(b, undefined, { sensitivity: "base" })),
    [roleGrouped]
  );

  const rolePermissionKeys = React.useMemo(() => {
    const keys = new Set<string>();
    for (const p of rolePerms) {
      if (p.resource && p.action) keys.add(permissionKey(p));
    }
    return keys;
  }, [rolePerms]);

  // ── custom permissions (editable) ──
  const savedCustomIds = React.useMemo(
    () => new Set((user?.customPermissions ?? []).map((p) => getApiEntityId(p)).filter(Boolean) as string[]),
    [user]
  );
  const savedCustomIdsStr = Array.from(savedCustomIds).sort().join(",");

  const [selectedIds, setSelectedIds] = React.useState<Set<string>>(new Set());
  const [toDeleteIds, setToDeleteIds] = React.useState<Set<string>>(new Set());

  // sync when user loads
  React.useEffect(() => {
    setSelectedIds(new Set(savedCustomIds));
    setToDeleteIds(new Set());
  }, [savedCustomIdsStr]); // Stabilize dependency

  const customPerms = React.useMemo(
    () => allPermissions.filter((p) => {
      const pid = getApiEntityId(p);
      return pid && selectedIds.has(pid);
    }),
    [allPermissions, selectedIds]
  );
  const customGrouped = React.useMemo(() => groupByResource(customPerms), [customPerms]);
  const customKeys    = React.useMemo(
    () => Array.from(customGrouped.keys()).sort((a, b) => a.localeCompare(b, undefined, { sensitivity: "base" })),
    [customGrouped]
  );

  // permissions not already granted (custom id or same resource+action on role)
  const availableToAdd = React.useMemo(
    () => allPermissions.filter((p) => {
      const pid = getApiEntityId(p);
      if (!pid || selectedIds.has(pid)) return false;
      if (rolePermissionKeys.has(permissionKey(p))) return false;
      return true;
    }),
    [allPermissions, selectedIds, rolePermissionKeys]
  );

  const fullCatalogNothingToAdd =
    allPermissions.length > 0 && availableToAdd.length === 0;

  const coverageNotice = React.useMemo(() => {
    if (!fullCatalogNothingToAdd) return null;
    if (rolePerms.length > 0 && customPerms.length === 0) {
      return "All permissions are already included through this user's role. Nothing else to add.";
    }
    if (rolePerms.length > 0 && customPerms.length > 0) {
      return "All permissions are already covered by this user's role and direct grants. Nothing else to add.";
    }
    return "You have already added all direct permissions.";
  }, [fullCatalogNothingToAdd, rolePerms.length, customPerms.length]);
  const availableGrouped = React.useMemo(() => groupByResource(availableToAdd), [availableToAdd]);
  const availableKeys    = React.useMemo(
    () => Array.from(availableGrouped.keys()).sort((a, b) => a.localeCompare(b, undefined, { sensitivity: "base" })),
    [availableGrouped]
  );

  const [addOpen, setAddOpen] = React.useState(false);

  const handleToggleDelete = (pid: string) => {
    setToDeleteIds((prev) => {
      const next = new Set(prev);
      if (next.has(pid)) next.delete(pid);
      else next.add(pid);
      return next;
    });
  };

  async function removeSelectedPermissions() {
    if (toDeleteIds.size === 0) return;
    const nextIds = Array.from(selectedIds).filter(id => !toDeleteIds.has(id));

    setPermissionActionBusy(true);
    try {
      await setUserPermissions({ id, permissionIds: nextIds }).unwrap();
      toast.success(`${toDeleteIds.size} permissions removed`);
      setToDeleteIds(new Set());
      await updateSession();
    } catch {
      toast.error("Failed to remove permissions");
    } finally {
      setPermissionActionBusy(false);
    }
  }

  // ── guards ──
  if (!token) {
    return (
      <div className="flex min-h-[40vh] items-center justify-center p-8">
        <Loading message="Loading…" />
      </div>
    );
  }
  if (!canRead) {
    if (isPermissionBusy || sessionStatus === "loading") {
      return (
        <div className="flex min-h-[40vh] items-center justify-center p-8">
          <Loading
            message={
              isPermissionBusy ? "Saving permissions…" : "Loading session…"
            }
          />
        </div>
      );
    }
    return (
      <div className="mx-auto max-w-lg p-8">
        <NoDataFound message="Access denied" description="You do not have permission to view user details." />
        <div className="mt-6 flex justify-center">
          <Button variant="outline" asChild><Link href="/users">Back to users</Link></Button>
        </div>
      </div>
    );
  }

  return (
    <div className="relative flex flex-col h-full gap-5 p-4 sm:p-6 md:p-8 animate-in fade-in duration-500 overflow-y-auto scrollbar-themed">
      {isPermissionBusy && (
        <div
          className="absolute inset-0 z-50 flex items-center justify-center bg-background/70 backdrop-blur-[2px]"
          aria-busy="true"
          aria-live="polite"
        >
          <Loading message="Saving permissions…" />
        </div>
      )}

      {/* ── Page header ── */}
      <div className="flex flex-col md:flex-row justify-between items-start md:items-center gap-4 border-b border-border/60 pb-8 shrink-0">
        <div className="flex items-center gap-3 min-w-0">
          <Button
            type="button"
            variant="toolbar"
            tone="default"
            onClick={() => router.push("/users")}
            aria-label="Back to users"
          >
            <ArrowLeft size={18} />
          </Button>
          <div className="flex items-center gap-2 min-w-0">
            <div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-accent/10 text-accent">
              <KeyRound size={20} />
            </div>
            <div className="min-w-0">
              <h1 className="text-2xl sm:text-3xl font-extrabold text-[#101828] font-['Lexend'] tracking-tight">
                User Permissions
              </h1>
              {user && (
                <p className="text-sm text-[#475467] font-normal truncate">
                  {user.name} · {user.email}
                </p>
              )}
            </div>
          </div>
        </div>
      </div>

      {/* ── Loading / error ── */}
      {isLoading && <Loading message="Loading user…" className="py-24" />}
      {isError && (
        <div className="rounded-2xl border border-destructive/20 bg-destructive/5 p-4 text-[13px] text-destructive">
          {error && typeof error === "object" && "data" in error
            ? String((error as { data?: { message?: unknown } }).data?.message ?? "Failed to load user")
            : "Failed to load user"}
        </div>
      )}

      {!isLoading && !isError && user && (
        <div className="space-y-10 pb-12">

          {/* ── KPI cards ── */}
          <div className="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-4 gap-6 shrink-0">
            <KPICard label="Role" value={getRoleName(user.role)} subtext="assigned role"
              icon={<ShieldCheck size={20} className="text-accent" />}
              className="bg-white/40 backdrop-blur-sm border-white/60 shadow-sm rounded-2xl" />
            <KPICard label="Role Permissions" value={rolePerms.length.toString()} subtext="from role"
              icon={<KeyRound size={20} className="text-accent" />}
              className="bg-white/40 backdrop-blur-sm border-white/60 shadow-sm rounded-2xl" />
            <KPICard label="Custom Permissions" value={customPerms.length.toString()} subtext="direct grants"
              icon={<UserCog size={20} className="text-accent" />}
              className="bg-white/40 backdrop-blur-sm border-white/60 shadow-sm rounded-2xl" />
            <KPICard label="Resources" value={(new Set([...roleKeys, ...customKeys])).size.toString()} subtext="covered modules"
              icon={<Layers size={20} className="text-accent" />}
              className="bg-white/40 backdrop-blur-sm border-white/60 shadow-sm rounded-2xl" />
          </div>

          {/* ══════════════════════════════════════════════
              SECTION 1 — Role permissions (read-only)
          ══════════════════════════════════════════════ */}
          <section>
            <div className="flex items-center gap-2 mb-4">
              <ShieldCheck size={16} className="text-accent shrink-0" />
              <h2 className="font-['Lexend'] text-[15px] font-bold text-[#101828]">Role Permissions</h2>
              <span className="text-[11px] font-semibold text-[#667085] bg-[#f2f4f7] border border-[#eaecf0] rounded-full px-2.5 py-0.5 ml-1">
                {rolePerms.length} total
              </span>
              <span className="text-[12px] text-[#667085] ml-1">— inherited from role, read-only</span>
            </div>

            {rolePerms.length === 0 ? (
              <div className="rounded-[20px] border border-[#eaecf0] overflow-hidden bg-white/70 backdrop-blur-md shadow-sm">
                <NoDataFound
                  icon={<ShieldOff size={32} className="text-muted-foreground/50" />}
                  message="No role permissions"
                  description="This user's role has no permission records attached."
                />
              </div>
            ) : (
              <div className="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-5">
                {roleKeys.map((resource) => (
                  <PermissionCard
                    key={resource}
                    resource={resource}
                    perms={roleGrouped.get(resource) ?? []}
                  />
                ))}
              </div>
            )}
          </section>

          {/* ══════════════════════════════════════════════
              SECTION 2 — Custom permissions (editable)
          ══════════════════════════════════════════════ */}
          <section>
            <div className="flex flex-wrap items-center gap-3 mb-4">
              <div className="flex items-center gap-2">
                <UserCog size={16} className="text-accent shrink-0" />
                <h2 className="font-['Lexend'] text-[15px] font-bold text-[#101828]">Custom Permissions</h2>
                <span className="text-[11px] font-semibold text-[#667085] bg-[#f2f4f7] border border-[#eaecf0] rounded-full px-2.5 py-0.5 ml-1">
                  {customPerms.length} total
                </span>
              </div>
              <span className="text-[12px] text-[#667085]">— granted directly to this user</span>

              {canEditDirectGrants && (
                <div className="flex items-center gap-2 ml-auto">
                  {toDeleteIds.size > 0 && (
                    <Button
                      type="button"
                      variant="destructiveModalSm"
                      onClick={removeSelectedPermissions}
                      disabled={isPermissionBusy}
                    >
                      <X size={14} /> Remove Selected ({toDeleteIds.size})
                    </Button>
                  )}
                  {availableToAdd.length > 0 && (
                    <Button
                      type="button"
                      variant="accentToolbarAdd"
                      onClick={() => setAddOpen(true)}
                      disabled={isPermissionBusy}
                    >
                      <Plus size={14} /> Add Permission
                    </Button>
                  )}
                </div>
              )}
            </div>

            {fullCatalogNothingToAdd && coverageNotice && (
              <div
                className="mb-4 flex items-start gap-3 rounded-[20px] border border-emerald-200/80 bg-emerald-50/60 px-4 py-3.5 text-[13px] text-[#065f46] shadow-sm"
                role="status"
              >
                <ShieldCheck size={18} className="mt-0.5 shrink-0 text-emerald-600" aria-hidden />
                <p className="leading-snug">{coverageNotice}</p>
              </div>
            )}

            {customPerms.length === 0 ? (
              <div className="rounded-[20px] border border-[#eaecf0] overflow-hidden bg-white/70 backdrop-blur-md shadow-sm">
                <NoDataFound
                  icon={<UserCog size={32} className="text-muted-foreground/50" />}
                  message="No custom permissions"
                  description={
                    canEditDirectGrants
                      ? fullCatalogNothingToAdd && coverageNotice
                        ? coverageNotice
                        : 'Use "Add Permission" to grant direct access to this user.'
                      : "No custom permissions have been assigned to this user."
                  }
                />
              </div>
            ) : (
              <div className="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-5">
                {customKeys.map((resource) => (
                  <PermissionCard
                    key={resource}
                    resource={resource}
                    perms={customGrouped.get(resource) ?? []}
                    selectedIds={toDeleteIds}
                    onToggle={canEditDirectGrants ? handleToggleDelete : undefined}
                  />
                ))}
              </div>
            )}
          </section>

        </div>
      )}
      <AddPermissionDialog
        userId={id}
        isOpen={addOpen}
        onOpenChange={setAddOpen}
        onBusyChange={setPermissionActionBusy}
      />
    </div>
  );
}
