import { useMemo } from "react";
import {
  Alert,
  Badge,
  Card,
  Checkbox,
  Group,
  Stack,
  Text,
  Tooltip,
} from "@mantine/core";
import { IconShieldLock, IconWand } from "@tabler/icons-react";
import { useTranslation } from "react-i18next";
import {
  ADMIN_WILDCARD,
  IPermissionDescriptor,
  IPermissionGroupView,
} from "@/features/acadenice/rbac/types/rbac.types";
import classes from "@/features/acadenice/rbac/styles/permission-matrix.module.css";

export interface PermissionMatrixProps {
  /** full closed catalogue from GET /acadenice/permissions */
  catalog: IPermissionDescriptor[];
  /** currently selected permission keys (the controlled set the parent owns) */
  value: string[];
  onChange: (next: string[]) => void;
  /** when true, every input is read-only (used for system roles) */
  disabled?: boolean;
  /** describes why the matrix is disabled, shown as an Alert */
  disabledReason?: string;
}

/**
 * Groups the catalogue by `group` field, separating `<group>:*` wildcards from
 * the atomic permissions, and pulls the global `admin:*` out as its own row.
 */
function buildGroupViews(
  catalog: IPermissionDescriptor[],
): { groups: IPermissionGroupView[]; adminWildcard: IPermissionDescriptor | null } {
  const byGroup = new Map<string, IPermissionGroupView>();
  let adminWildcard: IPermissionDescriptor | null = null;
  for (const p of catalog) {
    if (p.key === ADMIN_WILDCARD) {
      adminWildcard = p;
      continue;
    }
    if (!byGroup.has(p.group)) {
      byGroup.set(p.group, { group: p.group, items: [], hasGroupWildcard: false });
    }
    const view = byGroup.get(p.group)!;
    if (p.key.endsWith(":*")) {
      view.hasGroupWildcard = true;
    } else {
      view.items.push(p);
    }
  }
  // stable order : declared catalogue order is preserved within a group.
  return { groups: Array.from(byGroup.values()), adminWildcard };
}

export function PermissionMatrix({
  catalog,
  value,
  onChange,
  disabled,
  disabledReason,
}: PermissionMatrixProps) {
  const { t } = useTranslation();
  const valueSet = useMemo(() => new Set(value), [value]);
  const { groups, adminWildcard } = useMemo(
    () => buildGroupViews(catalog),
    [catalog],
  );

  const hasAdmin = valueSet.has(ADMIN_WILDCARD);

  const isGroupWildcardSelected = (group: string) =>
    valueSet.has(`${group}:*`);

  const selectedItemsInGroup = (g: IPermissionGroupView) =>
    g.items.filter((it) => valueSet.has(it.key)).length;

  const replaceSet = (next: Set<string>) => {
    onChange(Array.from(next).sort());
  };

  const toggleAdmin = () => {
    if (disabled) return;
    if (hasAdmin) {
      const next = new Set(valueSet);
      next.delete(ADMIN_WILDCARD);
      replaceSet(next);
    } else {
      // admin:* short-circuits everything ; keep only the wildcard so the
      // resulting set is the canonical representation the backend stores.
      replaceSet(new Set([ADMIN_WILDCARD]));
    }
  };

  const toggleGroupWildcard = (group: string) => {
    if (disabled || hasAdmin) return;
    const wildcard = `${group}:*`;
    const next = new Set(valueSet);
    if (next.has(wildcard)) {
      next.delete(wildcard);
    } else {
      next.add(wildcard);
      // remove individuals — they are subsumed by the wildcard
      for (const k of Array.from(next)) {
        if (k.startsWith(`${group}:`) && k !== wildcard) {
          next.delete(k);
        }
      }
    }
    replaceSet(next);
  };

  const togglePerm = (key: string) => {
    if (disabled || hasAdmin) return;
    const colonIdx = key.indexOf(":");
    const group = colonIdx > 0 ? key.slice(0, colonIdx) : null;
    if (group && isGroupWildcardSelected(group)) return;
    const next = new Set(valueSet);
    if (next.has(key)) {
      next.delete(key);
    } else {
      next.add(key);
    }
    replaceSet(next);
  };

  return (
    <Stack gap="md">
      {disabled && disabledReason ? (
        <Alert
          icon={<IconShieldLock size={16} />}
          color="yellow"
          variant="light"
          aria-live="polite"
        >
          {disabledReason}
        </Alert>
      ) : null}

      {adminWildcard ? (
        <Card
          withBorder
          padding="md"
          className={classes.adminCard}
          data-testid="admin-wildcard-card"
        >
          <Group justify="space-between" wrap="nowrap">
            <Group gap="sm">
              <IconWand size={20} aria-hidden />
              <div>
                <Text fw={600}>{t("Workspace owner")}</Text>
                <Text size="xs" c="dimmed">
                  {adminWildcard.description}
                </Text>
              </div>
            </Group>
            <Tooltip
              label={t(
                "Selecting this overrides every other permission. Use sparingly.",
              )}
              withArrow
            >
              <Checkbox
                aria-label={t("Toggle admin wildcard")}
                checked={hasAdmin}
                onChange={toggleAdmin}
                disabled={disabled}
                data-testid="cb-admin-wildcard"
              />
            </Tooltip>
          </Group>
        </Card>
      ) : null}

      {groups.map((g) => {
        const selectedCount = selectedItemsInGroup(g);
        const groupWildcardOn = isGroupWildcardSelected(g.group);
        const indeterminate =
          !groupWildcardOn &&
          !hasAdmin &&
          selectedCount > 0 &&
          selectedCount < g.items.length;
        const groupAllOn = groupWildcardOn || hasAdmin;
        const childrenDisabled = disabled || hasAdmin || groupWildcardOn;

        return (
          <Card
            key={g.group}
            withBorder
            padding="md"
            data-testid={`group-card-${g.group}`}
          >
            <Group justify="space-between" mb="xs" wrap="nowrap">
              <Group gap="xs">
                <Text fw={600} tt="capitalize">
                  {g.group}
                </Text>
                <Badge
                  size="sm"
                  variant="light"
                  color={groupAllOn ? "blue" : "gray"}
                  data-testid={`group-count-${g.group}`}
                >
                  {hasAdmin
                    ? t("inherited via admin")
                    : groupWildcardOn
                      ? t("all granted")
                      : `${selectedCount}/${g.items.length}`}
                </Badge>
              </Group>

              {g.hasGroupWildcard ? (
                <Tooltip
                  label={t(
                    "Grant every permission in this group, including future ones.",
                  )}
                  withArrow
                >
                  <Checkbox
                    label={t("All {{group}}", { group: g.group })}
                    aria-label={t("Toggle wildcard for {{group}}", {
                      group: g.group,
                    })}
                    checked={groupWildcardOn || hasAdmin}
                    indeterminate={indeterminate}
                    onChange={() => toggleGroupWildcard(g.group)}
                    disabled={disabled || hasAdmin}
                    data-testid={`cb-group-wildcard-${g.group}`}
                  />
                </Tooltip>
              ) : null}
            </Group>

            <Stack gap="xs" pl="sm">
              {g.items.map((item) => {
                const checked =
                  hasAdmin || groupWildcardOn || valueSet.has(item.key);
                return (
                  <Tooltip
                    key={item.key}
                    label={item.description}
                    position="right"
                    openDelay={300}
                    withArrow
                  >
                    <Checkbox
                      label={
                        <Group gap={6} wrap="nowrap">
                          <Text size="sm">{item.key}</Text>
                          <Text size="xs" c="dimmed">
                            — {item.description}
                          </Text>
                        </Group>
                      }
                      aria-label={item.key}
                      checked={checked}
                      onChange={() => togglePerm(item.key)}
                      disabled={childrenDisabled}
                      data-testid={`cb-perm-${item.key}`}
                    />
                  </Tooltip>
                );
              })}
            </Stack>
          </Card>
        );
      })}
    </Stack>
  );
}

export default PermissionMatrix;
