"use client";

import { useState, useMemo } from "react";
import { SystemRole, PermissionKey, ROLE_DEFAULT_PERMISSIONS, type PermissionOverrides } from "@capakraken/shared";
import { trpc } from "~/lib/trpc/client.js";
import { AnimatedModal } from "~/components/ui/AnimatedModal.js";
import { InviteUserModal } from "./InviteUserModal.js";
import { SuccessToast } from "~/components/ui/SuccessToast.js";
import { FilterChips } from "~/components/ui/FilterChips.js";
import { InfoTooltip } from "~/components/ui/InfoTooltip.js";
import { SortableColumnHeader } from "~/components/ui/SortableColumnHeader.js";
import { useTableSort } from "~/hooks/useTableSort.js";
import { useViewPrefs } from "~/hooks/useViewPrefs.js";

const ALL_PERMISSION_KEYS = Object.values(PermissionKey);

const PERMISSION_LABELS: Record<string, string> = {
  viewPlanning: "View Planning",
  viewCosts: "View Costs",
  useAssistantAdvancedTools: "Assistant Advanced Tools",
  exportData: "Export Data",
  importData: "Import Data",
  approveVacations: "Approve Vacations",
  manageBlueprints: "Manage Blueprints",
  viewAllResources: "View All Resources",
  manageResources: "Manage Resources",
  manageProjects: "Manage Projects",
  manageAllocations: "Manage Allocations",
  manageRoles: "Manage Roles",
  manageUsers: "Manage Users",
  viewScores: "View Scores",
};

const SYSTEM_ROLE_LABELS: Record<SystemRole, string> = {
  [SystemRole.ADMIN]: "Admin",
  [SystemRole.MANAGER]: "Manager",
  [SystemRole.CONTROLLER]: "Controller",
  [SystemRole.USER]: "User",
  [SystemRole.VIEWER]: "Viewer",
};

const ROLE_BADGE_COLORS: Record<SystemRole, string> = {
  [SystemRole.ADMIN]: "bg-purple-100 text-purple-700 dark:bg-purple-900/40 dark:text-purple-400",
  [SystemRole.MANAGER]: "bg-blue-100 text-blue-700 dark:bg-blue-900/40 dark:text-blue-400",
  [SystemRole.CONTROLLER]: "bg-amber-100 text-amber-700 dark:bg-amber-900/40 dark:text-amber-400",
  [SystemRole.USER]: "bg-gray-100 text-gray-600 dark:bg-gray-800 dark:text-gray-400",
  [SystemRole.VIEWER]: "bg-gray-100 text-gray-500 dark:bg-gray-800 dark:text-gray-500",
};

// Lower = more privileged (sort asc = most privileged first)
const ROLE_ORDER: Record<string, number> = {
  ADMIN: 0,
  MANAGER: 1,
  CONTROLLER: 2,
  USER: 3,
  VIEWER: 4,
};

type UserRow = {
  id: string;
  name: string | null;
  email: string;
  systemRole: string;
  createdAt: Date;
  lastLoginAt: Date | null;
  lastActiveAt: Date | null;
  permissionOverrides: PermissionOverrides | null;
  totpEnabled: boolean;
  isActive: boolean;
};

type EditState = {
  userId: string;
  systemRole: SystemRole;
  granted: Set<string>;
  denied: Set<string>;
  chapterIds: string;
};

type CreateState = {
  name: string;
  email: string;
  password: string;
  systemRole: SystemRole;
};

const EMPTY_CREATE: CreateState = {
  name: "",
  email: "",
  password: "",
  systemRole: SystemRole.USER,
};

export function UsersClient() {
  const [selectedUserId, setSelectedUserId] = useState<string | null>(null);
  const [editState, setEditState] = useState<EditState | null>(null);
  const [createState, setCreateState] = useState<CreateState | null>(null);
  const [actionError, setActionError] = useState<string | null>(null);
  const [search, setSearch] = useState("");
  const [roleFilter, setRoleFilter] = useState<SystemRole | "">("");
  const [editingName, setEditingName] = useState<{ userId: string; name: string } | null>(null);
  const [passwordTarget, setPasswordTarget] = useState<{ userId: string; userName: string } | null>(null);
  const [newPassword, setNewPassword] = useState("");
  const [confirmPassword, setConfirmPassword] = useState("");
  const [passwordError, setPasswordError] = useState<string | null>(null);
  const [passwordSuccess, setPasswordSuccess] = useState(false);
  const [inviteOpen, setInviteOpen] = useState(false);
  const [deleteTarget, setDeleteTarget] = useState<{ userId: string; userName: string } | null>(null);

  const utils = trpc.useUtils();

  const { data: users, isLoading } = trpc.user.list.useQuery(undefined, {
    staleTime: 10_000,
  });

  const { data: roleConfigs } = trpc.systemRoleConfig.list.useQuery(undefined, {
    staleTime: 60_000,
  });

  // Build dynamic role defaults map from DB config (fallback to hardcoded)
  const roleDefaultsMap = useMemo(() => {
    if (!roleConfigs) return ROLE_DEFAULT_PERMISSIONS;
    const map: Record<string, string[]> = {};
    for (const c of roleConfigs) {
      map[c.role] = c.defaultPermissions as string[];
    }
    return map as Record<SystemRole, string[]>;
  }, [roleConfigs]);

  const { data: activeData } = trpc.user.activeCount.useQuery(undefined, {
    staleTime: 30_000,
    refetchInterval: 30_000,
  });

  const { data: effectivePerms } = trpc.user.getEffectivePermissions.useQuery(
    { userId: selectedUserId ?? "" },
    { enabled: !!selectedUserId },
  );

  const updateRoleMutation = trpc.user.updateRole.useMutation({
    onSuccess: async () => {
      await utils.user.list.invalidate();
      await utils.user.getEffectivePermissions.invalidate();
    },
    onError: (err) => setActionError(err.message),
  });

  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  // @ts-ignore TS2589: tRPC infers union type too deeply for nullable overrides schema
  const setPermissionsMutation = trpc.user.setPermissions.useMutation({
    onSuccess: async () => {
      await utils.user.list.invalidate();
      await utils.user.getEffectivePermissions.invalidate();
    },
    onError: (err) => setActionError(err.message),
  });

  const createUserMutation = trpc.user.create.useMutation({
    onSuccess: async () => {
      await utils.user.list.invalidate();
      setCreateState(null);
      setActionError(null);
    },
    onError: (err) => setActionError(err.message),
  });

  const autoLinkMutation = trpc.user.autoLinkAllByEmail.useMutation({
    onSuccess: async () => {
      await utils.user.list.invalidate();
    },
    onError: (err) => setActionError(err.message),
  });

  const resetPermissionsMutation = trpc.user.resetPermissions.useMutation({
    onSuccess: async () => {
      await utils.user.list.invalidate();
      await utils.user.getEffectivePermissions.invalidate();
      if (editState) {
        setEditState({ ...editState, granted: new Set(), denied: new Set(), chapterIds: "" });
      }
    },
    onError: (err) => setActionError(err.message),
  });

  const setPasswordMutation = trpc.user.setPassword.useMutation({
    onSuccess: () => {
      setPasswordSuccess(true);
      setNewPassword("");
      setConfirmPassword("");
      setPasswordError(null);
      setTimeout(() => {
        setPasswordTarget(null);
        setPasswordSuccess(false);
      }, 1500);
    },
    onError: (err) => setPasswordError(err.message),
  });

  const updateNameMutation = trpc.user.updateName.useMutation({
    onSuccess: async () => {
      await utils.user.list.invalidate();
      setEditingName(null);
    },
    onError: (err) => setActionError(err.message),
  });

  const disableTotpMutation = trpc.user.disableTotp.useMutation({
    onSuccess: async () => {
      await utils.user.list.invalidate();
      setActionError(null);
    },
    onError: (err) => setActionError(err.message),
  });

  const deactivateMutation = trpc.user.deactivate.useMutation({
    onSuccess: async () => {
      await utils.user.list.invalidate();
      setActionError(null);
    },
    onError: (err) => setActionError(err.message),
  });

  const reactivateMutation = trpc.user.reactivate.useMutation({
    onSuccess: async () => {
      await utils.user.list.invalidate();
      setActionError(null);
    },
    onError: (err) => setActionError(err.message),
  });

  const deleteMutation = trpc.user.delete.useMutation({
    onSuccess: async () => {
      await utils.user.list.invalidate();
      setDeleteTarget(null);
      setActionError(null);
    },
    onError: (err) => {
      setActionError(err.message);
      setDeleteTarget(null);
    },
  });

  function openSetPassword(user: UserRow) {
    setPasswordTarget({ userId: user.id, userName: user.name ?? user.email });
    setNewPassword("");
    setConfirmPassword("");
    setPasswordError(null);
    setPasswordSuccess(false);
  }

  function closeSetPassword() {
    setPasswordTarget(null);
    setNewPassword("");
    setConfirmPassword("");
    setPasswordError(null);
    setPasswordSuccess(false);
  }

  async function handleSetPassword() {
    if (!passwordTarget) return;
    if (newPassword.length < 8) {
      setPasswordError("Password must be at least 8 characters");
      return;
    }
    if (newPassword !== confirmPassword) {
      setPasswordError("Passwords do not match");
      return;
    }
    setPasswordError(null);
    await setPasswordMutation.mutateAsync({
      userId: passwordTarget.userId,
      password: newPassword,
    });
  }

  function openEdit(user: UserRow) {
    const role = (user.systemRole as SystemRole) ?? SystemRole.USER;
    const overrides = user.permissionOverrides as PermissionOverrides | null;
    setSelectedUserId(user.id);
    setEditState({
      userId: user.id,
      systemRole: role,
      granted: new Set(overrides?.granted ?? []),
      denied: new Set(overrides?.denied ?? []),
      chapterIds: (overrides?.chapterIds ?? []).join(", "),
    });
    setActionError(null);
  }

  function closeEdit() {
    setSelectedUserId(null);
    setEditState(null);
    setActionError(null);
  }

  function toggleGranted(key: string) {
    if (!editState) return;
    const next = new Set(editState.granted);
    const nextDenied = new Set(editState.denied);
    if (next.has(key)) {
      next.delete(key);
    } else {
      next.add(key);
      nextDenied.delete(key);
    }
    setEditState({ ...editState, granted: next, denied: nextDenied });
  }

  function toggleDenied(key: string) {
    if (!editState) return;
    const next = new Set(editState.denied);
    const nextGranted = new Set(editState.granted);
    if (next.has(key)) {
      next.delete(key);
    } else {
      next.add(key);
      nextGranted.delete(key);
    }
    setEditState({ ...editState, denied: next, granted: nextGranted });
  }

  async function handleSaveRole() {
    if (!editState) return;
    setActionError(null);
    await updateRoleMutation.mutateAsync({ id: editState.userId, systemRole: editState.systemRole });
  }

  async function handleSavePermissions() {
    if (!editState) return;
    setActionError(null);
    const granted = Array.from(editState.granted);
    const denied = Array.from(editState.denied);
    const chapterIds = editState.chapterIds
      .split(",")
      .map((s) => s.trim())
      .filter(Boolean);
    const overrides: PermissionOverrides = {
      ...(granted.length > 0 ? { granted: granted as unknown as PermissionKey[] } : {}),
      ...(denied.length > 0 ? { denied: denied as unknown as PermissionKey[] } : {}),
      ...(chapterIds.length > 0 ? { chapterIds } : {}),
    };
    const hasOverrides = granted.length > 0 || denied.length > 0 || chapterIds.length > 0;
    await setPermissionsMutation.mutateAsync({
      userId: editState.userId,
      overrides: hasOverrides ? overrides : null,
    });
  }

  async function handleReset() {
    if (!editState) return;
    setActionError(null);
    await resetPermissionsMutation.mutateAsync({ userId: editState.userId });
  }

  const allUsers = (users ?? []) as unknown as UserRow[];

  // Client-side filtering
  const filteredUsers = allUsers.filter((u) => {
    if (search) {
      const q = search.toLowerCase();
      if (!(u.name ?? "").toLowerCase().includes(q) && !u.email.toLowerCase().includes(q)) return false;
    }
    if (roleFilter && u.systemRole !== roleFilter) return false;
    return true;
  });

  const usersViewPrefs = useViewPrefs("users");
  const { sorted, sortField, sortDir, toggle } = useTableSort(filteredUsers, {
    initialField: usersViewPrefs.savedSort?.field ?? null,
    initialDir: usersViewPrefs.savedSort?.dir ?? null,
    onSortChange: (field, dir) => {
      usersViewPrefs.setSavedSort(field && dir ? { field, dir } : null);
    },
  });

  function handleSort(field: string) {
    if (field === "systemRole") {
      toggle("systemRole", (u) => ROLE_ORDER[u.systemRole] ?? 99);
    } else {
      toggle(field as keyof UserRow);
    }
  }

  const selectedUser = editState ? allUsers.find((u) => u.id === editState.userId) : null;

  async function handleCreateUser() {
    if (!createState) return;
    setActionError(null);
    await createUserMutation.mutateAsync({
      name: createState.name,
      email: createState.email,
      password: createState.password,
      systemRole: createState.systemRole,
    });
  }

  const isPending =
    updateRoleMutation.isPending ||
    setPermissionsMutation.isPending ||
    resetPermissionsMutation.isPending ||
    createUserMutation.isPending ||
    setPasswordMutation.isPending;

  function clearAll() {
    setSearch("");
    setRoleFilter("");
  }

  const chips = [
    ...(search ? [{ label: `Search: "${search}"`, onRemove: () => setSearch("") }] : []),
    ...(roleFilter ? [{ label: `Role: ${SYSTEM_ROLE_LABELS[roleFilter]}`, onRemove: () => setRoleFilter("") }] : []),
  ];

  function isOnline(user: UserRow) {
    if (!user.lastActiveAt) return false;
    return Date.now() - new Date(user.lastActiveAt).getTime() < 5 * 60 * 1000;
  }

  function formatRelativeTime(date: Date | null) {
    if (!date) return "Never";
    const d = new Date(date);
    const diff = Date.now() - d.getTime();
    if (diff < 60_000) return "Just now";
    if (diff < 3_600_000) return `${Math.floor(diff / 60_000)}m ago`;
    if (diff < 86_400_000) return `${Math.floor(diff / 3_600_000)}h ago`;
    return d.toLocaleDateString("en-GB", { day: "2-digit", month: "short", year: "numeric" });
  }

  return (
    <div className="app-page">
      <div className="app-page-header mb-6">
        <div>
          <h1 className="app-page-title">User Management</h1>
          <p className="app-page-subtitle mt-1">
            Manage user roles and permission overrides
          </p>
        </div>
        <div className="flex items-center gap-3">
          {activeData && (
            <div className="flex items-center gap-2 rounded-lg border border-green-200 dark:border-green-800 bg-green-50 dark:bg-green-900/20 px-3 py-2 text-sm">
              <span className="relative flex h-2.5 w-2.5">
                <span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75" />
                <span className="relative inline-flex rounded-full h-2.5 w-2.5 bg-green-500" />
              </span>
              <span className="font-medium text-green-700 dark:text-green-400">
                {activeData.count} online
              </span>
            </div>
          )}
          <button
            type="button"
            onClick={() => void autoLinkMutation.mutateAsync().then((r) => {
              setActionError(r.linked > 0 ? null : `No unlinked accounts found (checked ${r.checked})`);
              if (r.linked > 0) setActionError(null);
            })}
            disabled={autoLinkMutation.isPending}
            className="inline-flex items-center gap-2 rounded-lg border border-gray-300 dark:border-gray-600 px-3 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors disabled:opacity-50"
            title="Auto-link user accounts to resources by matching email addresses"
          >
            <svg className="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1" />
            </svg>
            {autoLinkMutation.isPending ? "Linking..." : "Auto-link Resources"}
          </button>
          <button
            type="button"
            onClick={() => setInviteOpen(true)}
            className="inline-flex items-center gap-2 rounded-lg border border-brand-300 dark:border-brand-600 px-4 py-2 text-sm font-medium text-brand-700 dark:text-brand-300 hover:bg-brand-50 dark:hover:bg-brand-900/20 transition-colors"
          >
            <svg className="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
            </svg>
            Invite User
          </button>
          <button
            type="button"
            onClick={() => { setCreateState({ ...EMPTY_CREATE }); setActionError(null); }}
            className="inline-flex items-center gap-2 rounded-lg bg-brand-600 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-brand-700 transition-colors"
          >
            <svg className="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
            </svg>
            Create User
          </button>
        </div>
      </div>

      {/* Filters */}
      <div className="flex flex-wrap items-center gap-3 mb-3">
        <input
          type="search"
          placeholder="Search by name or email…"
          value={search}
          onChange={(e) => setSearch(e.target.value)}
          className="border border-gray-300 dark:border-gray-600 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-brand-400 w-64 bg-white dark:bg-gray-900 dark:text-gray-100"
        />
        <select
          value={roleFilter}
          onChange={(e) => setRoleFilter(e.target.value as SystemRole | "")}
          className="border border-gray-300 dark:border-gray-600 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-brand-400 bg-white dark:bg-gray-900 dark:text-gray-100"
        >
          <option value="">All Roles</option>
          {Object.values(SystemRole).map((role) => (
            <option key={role} value={role}>{SYSTEM_ROLE_LABELS[role]}</option>
          ))}
        </select>
      </div>

      {chips.length > 0 && (
        <div className="mb-3">
          <FilterChips chips={chips} onClearAll={clearAll} />
        </div>
      )}

      {actionError && (
        <div className="mb-4 rounded-lg bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-700 px-4 py-3 text-sm text-red-700 dark:text-red-400 flex items-center justify-between">
          {actionError}
          <button
            type="button"
            onClick={() => setActionError(null)}
            className="text-red-400 hover:text-red-600 text-lg leading-none"
          >
            &times;
          </button>
        </div>
      )}

      {/* User Table */}
      <div className="bg-white dark:bg-gray-900 rounded-xl border border-gray-200 dark:border-gray-700 overflow-hidden">
        <table className="w-full text-sm">
          <thead>
            <tr className="border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800/50">
              <SortableColumnHeader label="Name" field="name" sortField={sortField} sortDir={sortDir} onSort={handleSort} tooltip="The user's display name. Shown in the UI and linked to a resource record if auto-linked." />
              <SortableColumnHeader label="Email" field="email" sortField={sortField} sortDir={sortDir} onSort={handleSort} tooltip="Login email address. Also used to auto-link user accounts to resource records by matching email." />
              <SortableColumnHeader label="Role" field="systemRole" sortField={sortField} sortDir={sortDir} onSort={handleSort} align="center" tooltip="System role determines default access level. ADMIN: full access · MANAGER: manage resources/projects · CONTROLLER: read + export costs · USER: standard access · VIEWER: read-only. Individual permissions can be overridden via the edit dialog." tooltipWidth="w-80" />
              <th className="px-4 py-3 font-medium text-gray-600 dark:text-gray-400 text-xs uppercase tracking-wider text-center">Status</th>
              <SortableColumnHeader label="Last Login" field="lastLoginAt" sortField={sortField} sortDir={sortDir} onSort={handleSort} tooltip="When the user last signed in." />
              <SortableColumnHeader label="Created" field="createdAt" sortField={sortField} sortDir={sortDir} onSort={handleSort} tooltip="Account creation date." />
              <th className="text-right px-4 py-3 font-medium text-gray-600 dark:text-gray-400 text-xs uppercase tracking-wider">Actions</th>
            </tr>
          </thead>
          <tbody>
            {isLoading && (
              <tr>
                <td colSpan={7} className="text-center py-8 text-gray-400">
                  Loading…
                </td>
              </tr>
            )}
            {!isLoading && sorted.length === 0 && (
              <tr>
                <td colSpan={7} className="text-center py-8 text-gray-400">
                  No users found.
                </td>
              </tr>
            )}
            {sorted.map((user) => (
              <tr
                key={user.id}
                className={`border-b border-gray-100 dark:border-gray-800 hover:bg-gray-50 dark:hover:bg-gray-800/30 transition-colors ${!user.isActive ? "opacity-60" : ""}`}
              >
                <td className="px-4 py-3 font-medium text-gray-900 dark:text-gray-100">
                  {user.name ?? <span className="italic text-gray-400">—</span>}
                </td>
                <td className="px-4 py-3 text-gray-500 dark:text-gray-400">{user.email}</td>
                <td className="px-4 py-3 text-center">
                  <span
                    className={`inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium ${
                      ROLE_BADGE_COLORS[user.systemRole as SystemRole] ?? ROLE_BADGE_COLORS[SystemRole.USER]
                    }`}
                  >
                    {SYSTEM_ROLE_LABELS[user.systemRole as SystemRole] ?? user.systemRole}
                  </span>
                </td>
                <td className="px-4 py-3 text-center">
                  <div className="flex items-center justify-center gap-1.5">
                    {!user.isActive ? (
                      <span className="inline-flex items-center gap-1.5 px-2 py-0.5 rounded-full text-xs font-medium bg-red-100 text-red-600 dark:bg-red-900/30 dark:text-red-400">
                        <span className="h-1.5 w-1.5 rounded-full bg-red-500" />
                        Inactive
                      </span>
                    ) : isOnline(user) ? (
                      <span className="inline-flex items-center gap-1.5 px-2 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-700 dark:bg-green-900/40 dark:text-green-400">
                        <span className="h-1.5 w-1.5 rounded-full bg-green-500" />
                        Online
                      </span>
                    ) : (
                      <span className="inline-flex items-center gap-1.5 px-2 py-0.5 rounded-full text-xs font-medium bg-gray-100 text-gray-500 dark:bg-gray-800 dark:text-gray-500">
                        <span className="h-1.5 w-1.5 rounded-full bg-gray-400 dark:bg-gray-600" />
                        Offline
                      </span>
                    )}
                    {user.totpEnabled && (
                      <span className="inline-flex items-center gap-1 px-1.5 py-0.5 rounded-full text-[10px] font-semibold bg-indigo-100 text-indigo-700 dark:bg-indigo-900/40 dark:text-indigo-400" title="TOTP MFA enabled">
                        MFA
                      </span>
                    )}
                  </div>
                </td>
                <td className="px-4 py-3 text-gray-500 dark:text-gray-400 text-xs">
                  {formatRelativeTime(user.lastLoginAt)}
                </td>
                <td className="px-4 py-3 text-gray-500 dark:text-gray-400 text-xs">
                  {new Date(user.createdAt).toLocaleDateString("en-GB")}
                </td>
                <td className="px-4 py-3 text-right">
                  <div className="flex items-center justify-end gap-2">
                    <button
                      type="button"
                      onClick={() => openSetPassword(user)}
                      className="inline-flex items-center gap-1 text-xs text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 font-medium"
                      title="Set password"
                    >
                      <svg className="h-3.5 w-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                        <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z" />
                      </svg>
                      Password
                    </button>
                    {user.totpEnabled && (
                      <button
                        type="button"
                        onClick={() => {
                          if (confirm(`Disable MFA for ${user.name ?? user.email}?`)) {
                            void disableTotpMutation.mutateAsync({ userId: user.id });
                          }
                        }}
                        disabled={disableTotpMutation.isPending}
                        className="inline-flex items-center gap-1 text-xs text-amber-600 hover:text-amber-800 dark:text-amber-400 dark:hover:text-amber-300 font-medium"
                        title="Disable TOTP MFA for this user"
                      >
                        <svg className="h-3.5 w-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                          <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
                        </svg>
                        Disable MFA
                      </button>
                    )}
                    <button
                      type="button"
                      onClick={() => openEdit(user)}
                      className="text-xs text-brand-600 hover:text-brand-800 font-medium"
                    >
                      Edit
                    </button>
                    {user.isActive ? (
                      <button
                        type="button"
                        onClick={() => {
                          if (confirm(`Deactivate ${user.name ?? user.email}? They will be logged out immediately and cannot log in until reactivated.`)) {
                            void deactivateMutation.mutateAsync({ userId: user.id });
                          }
                        }}
                        disabled={deactivateMutation.isPending}
                        className="text-xs text-amber-600 hover:text-amber-800 dark:text-amber-400 dark:hover:text-amber-300 font-medium"
                        title="Deactivate user — blocks login and revokes sessions"
                      >
                        Deactivate
                      </button>
                    ) : (
                      <button
                        type="button"
                        onClick={() => void reactivateMutation.mutateAsync({ userId: user.id })}
                        disabled={reactivateMutation.isPending}
                        className="text-xs text-green-600 hover:text-green-800 dark:text-green-400 dark:hover:text-green-300 font-medium"
                        title="Reactivate user — allows login again"
                      >
                        Reactivate
                      </button>
                    )}
                    <button
                      type="button"
                      onClick={() => setDeleteTarget({ userId: user.id, userName: user.name ?? user.email })}
                      className="text-xs text-red-500 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300 font-medium"
                      title="Permanently delete user"
                    >
                      Delete
                    </button>
                  </div>
                </td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>

      {/* Set Password Modal */}
      <AnimatedModal open={!!passwordTarget} onClose={closeSetPassword} maxWidth="max-w-md">
        <div className="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
          <h2 className="text-lg font-semibold text-gray-900 dark:text-gray-100">
            Set Password for {passwordTarget?.userName}
          </h2>
        </div>

        <div className="px-6 py-5 space-y-4">
          {passwordError && (
            <div className="rounded-lg bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-700 px-3 py-2 text-sm text-red-700 dark:text-red-400">
              {passwordError}
            </div>
          )}

          <div>
            <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
              New Password
            </label>
            <input
              type="password"
              value={newPassword}
              onChange={(e) => setNewPassword(e.target.value)}
              placeholder="Min. 8 characters"
              className="border border-gray-300 dark:border-gray-600 rounded-lg px-3 py-2 text-sm w-full bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-brand-400"
              autoComplete="new-password"
            />
            {newPassword.length > 0 && newPassword.length < 8 && (
              <p className="mt-1 text-xs text-amber-600 dark:text-amber-400">
                {8 - newPassword.length} more character{8 - newPassword.length !== 1 ? "s" : ""} needed
              </p>
            )}
          </div>

          <div>
            <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
              Confirm Password
            </label>
            <input
              type="password"
              value={confirmPassword}
              onChange={(e) => setConfirmPassword(e.target.value)}
              placeholder="Repeat password"
              className="border border-gray-300 dark:border-gray-600 rounded-lg px-3 py-2 text-sm w-full bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-brand-400"
              autoComplete="new-password"
            />
            {confirmPassword.length > 0 && newPassword !== confirmPassword && (
              <p className="mt-1 text-xs text-red-600 dark:text-red-400">
                Passwords do not match
              </p>
            )}
          </div>
        </div>

        <div className="flex items-center justify-end gap-3 px-6 py-4 border-t border-gray-200 dark:border-gray-700">
          <button
            type="button"
            onClick={closeSetPassword}
            className="px-4 py-2 text-sm text-gray-600 hover:text-gray-800 dark:text-gray-400 dark:hover:text-gray-200"
          >
            Cancel
          </button>
          <button
            type="button"
            onClick={() => void handleSetPassword()}
            disabled={setPasswordMutation.isPending || newPassword.length < 8 || newPassword !== confirmPassword}
            className="px-4 py-2 bg-brand-600 text-white rounded-lg hover:bg-brand-700 text-sm font-medium disabled:opacity-50 disabled:cursor-not-allowed"
          >
            {setPasswordMutation.isPending ? "Saving..." : "Set Password"}
          </button>
        </div>
      </AnimatedModal>

      <SuccessToast show={passwordSuccess} message="Password updated successfully" />

      <InviteUserModal open={inviteOpen} onClose={() => setInviteOpen(false)} />

      {/* Delete Confirmation Modal */}
      {deleteTarget && (
        <div className="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4">
          <div className="bg-white dark:bg-gray-900 rounded-xl shadow-2xl w-full max-w-md mx-4">
            <div className="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
              <h2 className="text-lg font-semibold text-gray-900 dark:text-gray-100">
                Delete User
              </h2>
            </div>
            <div className="px-6 py-5 space-y-3">
              <p className="text-sm text-gray-700 dark:text-gray-300">
                Are you sure you want to permanently delete{" "}
                <strong>{deleteTarget.userName}</strong>?
              </p>
              <p className="text-sm text-red-600 dark:text-red-400">
                This will permanently remove their account, sessions, vacation records, and notifications.
                Audit history entries will be retained but anonymised. This action cannot be undone.
              </p>
            </div>
            <div className="flex justify-end gap-3 px-6 py-4 border-t border-gray-200 dark:border-gray-700">
              <button
                type="button"
                onClick={() => setDeleteTarget(null)}
                disabled={deleteMutation.isPending}
                className="px-4 py-2 text-sm rounded-lg border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
              >
                Cancel
              </button>
              <button
                type="button"
                onClick={() => void deleteMutation.mutateAsync({ userId: deleteTarget.userId })}
                disabled={deleteMutation.isPending}
                className="px-4 py-2 text-sm rounded-lg bg-red-600 hover:bg-red-700 text-white font-medium transition-colors disabled:opacity-50"
              >
                {deleteMutation.isPending ? "Deleting…" : "Delete permanently"}
              </button>
            </div>
          </div>
        </div>
      )}

      {/* Create User Modal */}
      {createState && (
        <div className="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4">
          <div className="bg-white dark:bg-gray-900 rounded-xl shadow-2xl w-full max-w-md mx-4 flex flex-col">
            <div className="flex items-center justify-between px-6 py-4 border-b border-gray-200 dark:border-gray-700">
              <h2 className="text-lg font-semibold text-gray-900 dark:text-gray-100">
                Create User
              </h2>
              <button
                type="button"
                onClick={() => { setCreateState(null); setActionError(null); }}
                className="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 text-2xl leading-none"
              >
                &times;
              </button>
            </div>

            <div className="px-6 py-5 space-y-4">
              {actionError && (
                <div className="rounded-lg bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-700 px-3 py-2 text-sm text-red-700 dark:text-red-400">
                  {actionError}
                </div>
              )}

              <div>
                <label className="flex items-center text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
                  Name <InfoTooltip content="The display name for this user account." />
                </label>
                <input
                  type="text"
                  value={createState.name}
                  onChange={(e) => setCreateState({ ...createState, name: e.target.value })}
                  placeholder="Max Mustermann"
                  className="border border-gray-300 dark:border-gray-600 rounded-lg px-3 py-2 text-sm w-full bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-brand-400"
                />
              </div>

              <div>
                <label className="flex items-center text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
                  Email <InfoTooltip content="Login email address. Also used to auto-link the user to a resource record." />
                </label>
                <input
                  type="email"
                  value={createState.email}
                  onChange={(e) => setCreateState({ ...createState, email: e.target.value })}
                  placeholder="user@example.com"
                  className="border border-gray-300 dark:border-gray-600 rounded-lg px-3 py-2 text-sm w-full bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-brand-400"
                />
              </div>

              <div>
                <label className="flex items-center text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
                  Password <InfoTooltip content="Minimum 8 characters. Stored securely using Argon2 hashing." />
                </label>
                <input
                  type="password"
                  value={createState.password}
                  onChange={(e) => setCreateState({ ...createState, password: e.target.value })}
                  placeholder="Min. 8 characters"
                  className="border border-gray-300 dark:border-gray-600 rounded-lg px-3 py-2 text-sm w-full bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-brand-400"
                  autoComplete="new-password"
                />
              </div>

              <div>
                <label className="flex items-center text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
                  Role <InfoTooltip content="ADMIN: full system access. MANAGER: manage resources, projects, allocations. CONTROLLER: read + export financial data. USER: standard access. VIEWER: read-only." />
                </label>
                <select
                  value={createState.systemRole}
                  onChange={(e) => setCreateState({ ...createState, systemRole: e.target.value as SystemRole })}
                  className="border border-gray-300 dark:border-gray-600 rounded-lg px-3 py-2 text-sm w-full bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-brand-400"
                >
                  {Object.values(SystemRole).map((role) => (
                    <option key={role} value={role}>{SYSTEM_ROLE_LABELS[role]}</option>
                  ))}
                </select>
              </div>
            </div>

            <div className="flex items-center justify-end gap-3 px-6 py-4 border-t border-gray-200 dark:border-gray-700">
              <button
                type="button"
                onClick={() => { setCreateState(null); setActionError(null); }}
                className="px-4 py-2 text-sm text-gray-600 hover:text-gray-800 dark:text-gray-400 dark:hover:text-gray-200"
              >
                Cancel
              </button>
              <button
                type="button"
                onClick={() => void handleCreateUser()}
                disabled={isPending || !createState.name.trim() || !createState.email.trim() || createState.password.length < 8}
                className="px-4 py-2 bg-brand-600 text-white rounded-lg hover:bg-brand-700 text-sm font-medium disabled:opacity-50 disabled:cursor-not-allowed"
              >
                {createUserMutation.isPending ? "Creating..." : "Create User"}
              </button>
            </div>
          </div>
        </div>
      )}

      {/* Edit Modal */}
      {editState && selectedUser && (
        <div className="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4">
          <div className="bg-white dark:bg-gray-900 rounded-xl shadow-2xl w-full max-w-2xl mx-4 flex flex-col max-h-[90vh]">
            {/* Modal Header */}
            <div className="flex items-center justify-between px-6 py-4 border-b border-gray-200 dark:border-gray-700">
              <div>
                <h2 className="text-lg font-semibold text-gray-900 dark:text-gray-100">
                  Edit User
                </h2>
                <p className="text-sm text-gray-500 dark:text-gray-400 mt-0.5">
                  {selectedUser.name ?? selectedUser.email}
                </p>
              </div>
              <button
                type="button"
                onClick={closeEdit}
                className="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 text-2xl leading-none"
              >
                &times;
              </button>
            </div>

            {/* Modal Body */}
            <div className="overflow-y-auto flex-1 px-6 py-5 space-y-6">
              {/* User Name */}
              <section>
                <h3 className="text-sm font-semibold text-gray-700 dark:text-gray-300 mb-3">
                  Display Name
                </h3>
                {editingName?.userId === editState.userId ? (
                  <div className="flex items-center gap-2">
                    <input
                      type="text"
                      value={editingName.name}
                      onChange={(e) => setEditingName({ ...editingName, name: e.target.value })}
                      className="flex-1 border border-gray-300 dark:border-gray-600 rounded-lg px-3 py-2 text-sm bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-brand-400"
                      autoFocus
                      onKeyDown={(e) => {
                        if (e.key === "Enter" && editingName.name.trim()) {
                          updateNameMutation.mutate({ id: editingName.userId, name: editingName.name.trim() });
                        }
                        if (e.key === "Escape") setEditingName(null);
                      }}
                    />
                    <button
                      type="button"
                      onClick={() => updateNameMutation.mutate({ id: editingName.userId, name: editingName.name.trim() })}
                      disabled={!editingName.name.trim() || updateNameMutation.isPending}
                      className="px-3 py-2 bg-brand-600 text-white rounded-lg text-sm font-medium hover:bg-brand-700 disabled:opacity-50"
                    >
                      {updateNameMutation.isPending ? "..." : "Save"}
                    </button>
                    <button
                      type="button"
                      onClick={() => setEditingName(null)}
                      className="px-3 py-2 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 text-sm"
                    >
                      Cancel
                    </button>
                  </div>
                ) : (
                  <div className="flex items-center gap-2">
                    <span className="text-sm text-gray-900 dark:text-gray-100">
                      {(users as any)?.find((u: any) => u.id === editState.userId)?.name ?? "—"}
                    </span>
                    <button
                      type="button"
                      onClick={() => {
                        const user = (users as any)?.find((u: any) => u.id === editState.userId);
                        setEditingName({ userId: editState.userId, name: user?.name ?? "" });
                      }}
                      className="text-xs text-brand-600 hover:text-brand-700 dark:text-brand-400 dark:hover:text-brand-300"
                    >
                      Edit
                    </button>
                  </div>
                )}
              </section>

              {/* System Role */}
              <section>
                <h3 className="text-sm font-semibold text-gray-700 dark:text-gray-300 mb-3 flex items-center">
                  System Role <InfoTooltip content="The base role determines default permissions. Change the role and click 'Save Role' to apply. Permission overrides below can further customize access." />
                </h3>
                <div className="flex items-center gap-3">
                  <select
                    value={editState.systemRole}
                    onChange={(e) =>
                      setEditState({ ...editState, systemRole: e.target.value as SystemRole })
                    }
                    className="border border-gray-300 dark:border-gray-600 rounded-lg px-3 py-2 text-sm bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-brand-400"
                  >
                    {Object.values(SystemRole).map((role) => (
                      <option key={role} value={role}>
                        {SYSTEM_ROLE_LABELS[role]}
                      </option>
                    ))}
                  </select>
                  <button
                    type="button"
                    onClick={handleSaveRole}
                    disabled={isPending}
                    className="px-4 py-2 bg-brand-600 text-white rounded-lg hover:bg-brand-700 text-sm font-medium disabled:opacity-50"
                  >
                    {updateRoleMutation.isPending ? "Saving…" : "Save Role"}
                  </button>
                </div>
              </section>

              {/* Permissions */}
              <section>
                <h3 className="text-sm font-semibold text-gray-700 dark:text-gray-300 mb-2 flex items-center">
                  Permissions <InfoTooltip content="Permissions inherited from the role are shown with a filled checkbox. Click to override: grant additional permissions or deny role defaults." />
                </h3>
                <div className="flex gap-1.5 mb-3 text-[11px]">
                  <span className="inline-flex items-center gap-1 text-gray-500 dark:text-gray-400">
                    <span className="inline-block w-3 h-3 rounded border border-green-400 bg-green-100 dark:bg-green-900/40" /> Role default
                  </span>
                  <span className="inline-flex items-center gap-1 text-gray-500 dark:text-gray-400">
                    <span className="inline-block w-3 h-3 rounded border border-blue-400 bg-blue-100 dark:bg-blue-900/40" /> Extra grant
                  </span>
                  <span className="inline-flex items-center gap-1 text-gray-500 dark:text-gray-400">
                    <span className="inline-block w-3 h-3 rounded border border-red-400 bg-red-100 dark:bg-red-900/40 relative"><span className="absolute inset-0 flex items-center justify-center text-red-500 text-[9px] leading-none">&times;</span></span> Denied
                  </span>
                </div>
                <div className="space-y-1">
                  {ALL_PERMISSION_KEYS.map((key) => {
                    const roleDefaults = new Set(roleDefaultsMap[editState.systemRole] ?? []);
                    const isRoleDefault = roleDefaults.has(key as PermissionKey);
                    const isGranted = editState.granted.has(key);
                    const isDenied = editState.denied.has(key);

                    // Determine display state
                    let state: "default" | "granted" | "denied" | "off";
                    if (isDenied) state = "denied";
                    else if (isGranted) state = "granted";
                    else if (isRoleDefault) state = "default";
                    else state = "off";

                    function cycleState() {
                      if (!editState) return;
                      const nextGranted = new Set(editState.granted);
                      const nextDenied = new Set(editState.denied);

                      if (isRoleDefault) {
                        // Role default: off → denied → off
                        if (isDenied) {
                          nextDenied.delete(key);
                        } else {
                          nextDenied.add(key);
                          nextGranted.delete(key);
                        }
                      } else {
                        // Non-default: off → granted → off
                        if (isGranted) {
                          nextGranted.delete(key);
                        } else {
                          nextGranted.add(key);
                          nextDenied.delete(key);
                        }
                      }
                      setEditState({ ...editState, granted: nextGranted, denied: nextDenied });
                    }

                    const stateStyles = {
                      default: "bg-green-50 border-green-200 dark:bg-green-900/20 dark:border-green-800",
                      granted: "bg-blue-50 border-blue-200 dark:bg-blue-900/20 dark:border-blue-800",
                      denied: "bg-red-50 border-red-200 dark:bg-red-900/20 dark:border-red-800",
                      off: "bg-gray-50 border-gray-200 dark:bg-gray-800/50 dark:border-gray-700",
                    };

                    const checkStyles = {
                      default: "text-green-600 border-green-300 bg-green-100 dark:bg-green-900/40",
                      granted: "text-blue-600 border-blue-300 bg-blue-100 dark:bg-blue-900/40",
                      denied: "text-red-600 border-red-300 bg-red-100 dark:bg-red-900/40",
                      off: "text-gray-400 border-gray-300 dark:border-gray-600",
                    };

                    return (
                      <button
                        key={key}
                        type="button"
                        onClick={cycleState}
                        className={`flex items-center gap-2.5 w-full px-3 py-1.5 rounded-lg border text-sm text-left transition-colors ${stateStyles[state]} hover:opacity-80`}
                      >
                        <span className={`flex-shrink-0 w-4 h-4 rounded border flex items-center justify-center ${checkStyles[state]}`}>
                          {state === "default" && (
                            <svg className="w-3 h-3" fill="currentColor" viewBox="0 0 20 20"><path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" /></svg>
                          )}
                          {state === "granted" && (
                            <svg className="w-3 h-3" fill="currentColor" viewBox="0 0 20 20"><path fillRule="evenodd" d="M10 3a1 1 0 011 1v5h5a1 1 0 110 2h-5v5a1 1 0 11-2 0v-5H4a1 1 0 110-2h5V4a1 1 0 011-1z" clipRule="evenodd" /></svg>
                          )}
                          {state === "denied" && (
                            <span className="text-xs font-bold leading-none">&times;</span>
                          )}
                        </span>
                        <span className={`flex-1 ${state === "denied" ? "line-through text-red-500 dark:text-red-400" : state === "off" ? "text-gray-500 dark:text-gray-400" : "text-gray-900 dark:text-gray-100"}`}>
                          {PERMISSION_LABELS[key] ?? key}
                        </span>
                        {state === "default" && (
                          <span className="text-[10px] text-green-600 dark:text-green-400 font-medium uppercase tracking-wide">Role</span>
                        )}
                        {state === "granted" && (
                          <span className="text-[10px] text-blue-600 dark:text-blue-400 font-medium uppercase tracking-wide">Extra</span>
                        )}
                        {state === "denied" && (
                          <span className="text-[10px] text-red-600 dark:text-red-400 font-medium uppercase tracking-wide">Denied</span>
                        )}
                      </button>
                    );
                  })}
                </div>

                {/* Chapter Scope */}
                <div className="mt-4">
                  <label className="flex items-center text-xs font-medium text-gray-600 dark:text-gray-400 mb-1.5">
                    Chapter Scope (comma-separated IDs, leave blank for all) <InfoTooltip content="Restrict this user's access to specific chapters/disciplines only. Leave blank to allow access to all chapters." />
                  </label>
                  <input
                    type="text"
                    value={editState.chapterIds}
                    onChange={(e) => setEditState({ ...editState, chapterIds: e.target.value })}
                    placeholder="e.g. chapter-1, chapter-2"
                    className="border border-gray-300 dark:border-gray-600 rounded-lg px-3 py-2 text-sm w-full bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-brand-400"
                  />
                </div>
              </section>
            </div>

            {/* Modal Footer */}
            <div className="flex items-center justify-between px-6 py-4 border-t border-gray-200 dark:border-gray-700 gap-3">
              <button
                type="button"
                onClick={handleReset}
                disabled={isPending}
                className="px-4 py-2 text-sm text-red-600 dark:text-red-400 hover:text-red-800 dark:hover:text-red-300 border border-red-200 dark:border-red-700 hover:border-red-300 dark:hover:border-red-600 rounded-lg disabled:opacity-50"
              >
                {resetPermissionsMutation.isPending ? "Resetting…" : "Reset to Defaults"}
              </button>
              <div className="flex gap-3">
                <button
                  type="button"
                  onClick={closeEdit}
                  className="px-4 py-2 text-sm text-gray-600 hover:text-gray-800 dark:text-gray-400 dark:hover:text-gray-200"
                >
                  Close
                </button>
                <button
                  type="button"
                  onClick={handleSavePermissions}
                  disabled={isPending}
                  className="px-4 py-2 bg-brand-600 text-white rounded-lg hover:bg-brand-700 text-sm font-medium disabled:opacity-50"
                >
                  {setPermissionsMutation.isPending ? "Saving…" : "Save Permissions"}
                </button>
              </div>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}
