"use client";

import type { AccessScope } from "@/lib/features/types";
import type { AdminRole } from "@/lib/features/users/roles/types";
import {
  Badge,
  Button,
  Card,
  Group,
  Stack,
  Text,
  TextInput,
  Title,
  Tooltip,
} from "@mantine/core";
import { useDebouncedValue } from "@mantine/hooks";
import { IconPlus, IconSearch, IconUsers, IconX } from "@tabler/icons-react";
import moment from "moment";
import { useEffect, useState } from "react";
import { useNavigate, useSearchParams } from "react-router";
import {
  MemberDataTable,
  type MemberColumn,
} from "@/routes/members/_components/MemberDataTable";

/** Privilege letters, in the order they read: C R U D. */
const PRIVILEGES = [
  { key: "create", label: "C", title: "Create" },
  { key: "read", label: "R", title: "Read" },
  { key: "update", label: "U", title: "Update" },
  { key: "delete", label: "D", title: "Delete" },
] as const;

const RolesClientPage: React.FC<{
  tableProps: { data: AdminRole[]; totalRows: number };
  accessScope: AccessScope;
}> = ({ tableProps, accessScope }) => {
  const navigate = useNavigate();
  const [searchParams, setSearchParams] = useSearchParams();
  const [searchDraft, setSearchDraft] = useState(
    searchParams.get("search") ?? "",
  );
  const [debouncedSearch] = useDebouncedValue(searchDraft, 350);

  // Applied as you type; debounced because each apply is a server round trip.
  useEffect(() => {
    const next = debouncedSearch.trim();
    if (next === (searchParams.get("search") ?? "")) return;
    const params = new URLSearchParams(searchParams);
    if (next) params.set("search", next);
    else params.delete("search");
    params.set("page", "1");
    setSearchParams(params);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [debouncedSearch]);

  const columns: MemberColumn<AdminRole>[] = [
    {
      key: "name",
      header: "Role",
      render: (role) => (
        <Stack gap={0}>
          <Text fz="sm" fw={600}>
            {role.name}
          </Text>
          {role.description ? (
            <Text fz={11} c="dimmed" lineClamp={1}>
              {role.description}
            </Text>
          ) : null}
        </Stack>
      ),
    },
    {
      key: "users",
      header: "Users",
      numeric: true,
      render: (role) => (
        <Group gap={4} wrap="nowrap" justify="flex-end">
          <IconUsers size={13} opacity={0.5} />
          <Text fz="sm" c={role.userCount ? undefined : "dimmed"}>
            {role.userCount ?? 0}
          </Text>
        </Group>
      ),
    },
    {
      key: "modules",
      header: "Modules",
      render: (role) => {
        const granted = role.privileges ?? [];
        if (granted.length === 0) {
          return (
            <Text fz="sm" c="dimmed">
              None — this role grants nothing
            </Text>
          );
        }
        // First few by name, the rest as a count: a role with fifteen modules
        // would otherwise wrap into a wall of badges.
        const shown = granted.slice(0, 4);
        const rest = granted.length - shown.length;
        return (
          <Group gap={4} wrap="wrap">
            {shown.map((privilege) => (
              <Tooltip
                key={privilege.slug}
                label={PRIVILEGES.filter(
                  (entry) => privilege[entry.key] === true,
                )
                  .map((entry) => entry.title)
                  .join(", ")}
              >
                <Badge variant="light" size="sm" radius={4}>
                  {privilege.name}
                </Badge>
              </Tooltip>
            ))}
            {rest > 0 ? (
              <Tooltip
                label={granted
                  .slice(4)
                  .map((privilege) => privilege.name)
                  .join(", ")}
              >
                <Badge variant="light" color="gray" size="sm" radius={4}>
                  +{rest}
                </Badge>
              </Tooltip>
            ) : null}
          </Group>
        );
      },
    },
    {
      key: "privileges",
      header: "Privileges",
      render: (role) => {
        const granted = role.privileges ?? [];
        if (granted.length === 0) {
          return (
            <Text fz="sm" c="dimmed">
              —
            </Text>
          );
        }
        // Rolled up across modules: whether the role can write at all is the
        // question worth answering at a glance.
        return (
          <Group gap={4} wrap="nowrap">
            {PRIVILEGES.map((entry) => {
              const count = granted.filter(
                (privilege) => privilege[entry.key] === true,
              ).length;
              return (
                <Tooltip
                  key={entry.key}
                  label={`${entry.title} on ${count} module(s)`}
                >
                  <Badge
                    variant={count ? "light" : "outline"}
                    color={
                      count
                        ? entry.key === "delete"
                          ? "red"
                          : undefined
                        : "gray"
                    }
                    size="sm"
                    radius={4}
                    style={{ opacity: count ? 1 : 0.35 }}
                  >
                    {entry.label}
                  </Badge>
                </Tooltip>
              );
            })}
          </Group>
        );
      },
    },
    {
      key: "created_at",
      header: "Created At",
      render: (role) => moment(role.created_at).format("LL"),
    },
  ];

  return (
    <Stack gap="lg">
      <Group justify="space-between" align="center">
        <Group gap={8} align="baseline">
          <Title order={6}>All Roles</Title>
          <Text fz={12} c="dimmed">
            {(tableProps.totalRows ?? 0).toLocaleString()} role(s)
          </Text>
        </Group>
        {accessScope.create || accessScope.superAdmin ? (
          <Button
            leftSection={<IconPlus size={16} />}
            onClick={() => void navigate("new")}
          >
            New role
          </Button>
        ) : null}
      </Group>

      <Card withBorder radius="lg" p="md">
        <Group justify="space-between" align="center" mb="sm">
          <Text fz={13} fw={600}>
            Console roles
          </Text>
          <TextInput
            size="xs"
            w={260}
            placeholder="Search role name"
            leftSection={<IconSearch size={14} />}
            value={searchDraft}
            onChange={(event) => setSearchDraft(event.currentTarget.value)}
            rightSection={
              searchDraft ? (
                <IconX
                  size={13}
                  style={{ cursor: "pointer" }}
                  onClick={() => setSearchDraft("")}
                />
              ) : null
            }
          />
        </Group>

        <MemberDataTable
          rows={tableProps.data}
          columns={columns}
          minWidth={1000}
          rowKey={(role) => String(role.id)}
          onRowClick={(role) => void navigate(String(role.id))}
          emptyMessage="No roles match"
        />
      </Card>
    </Stack>
  );
};

export default RolesClientPage;
