"use client";

import {
  Box,
  Checkbox,
  Divider,
  FormControl,
  InputLabel,
  ListItemIcon,
  ListItemText,
  ListSubheader,
  MenuItem,
  Select,
  SelectProps,
  Stack,
  Typography,
} from "@mui/material";
import React, { useCallback, useEffect, useState } from "react";
import { LabelGroup, LabelWithId, TransferListProps } from "./TransferList";
import { useTranslation } from "react-i18next";
import uniqBy from "lodash/uniqBy";
import groupBy from "lodash/groupBy";

export const MultiSelectList: React.FC<TransferListProps> = ({
  allItems,
  initiallySelectedItems,
  selectedItemsLabel,
  allItemsLabel,
  onChange,
}) => {
  // Keep a map for the original order of items
  const sortMap = React.useMemo(() => {
    return allItems.reduce<{ [id: string]: LabelWithId & { index: number } }>(
      (acc, item, index) => ({ ...acc, [item.id]: { ...item, index } }),
      {},
    );
  }, [allItems]);
  const compareFn = React.useCallback(
    (a: number, b: number) => sortMap[a].index - sortMap[b].index,
    [sortMap],
  );
  const [selectedItems, setSelectedItems] = useState(
    initiallySelectedItems.map((item) => item.id),
  );
  const handleChange = useCallback<
    NonNullable<SelectProps<typeof selectedItems>["onChange"]>
  >((event) => {
    const {
      target: { value },
    } = event;
    setSelectedItems(typeof value === "string" ? [Number(value)] : value);
  }, []);

  const handleToggleAll = useCallback(
    () => () => {
      if (selectedItems.length === allItems.length) {
        setSelectedItems([]);
      } else {
        setSelectedItems(allItems.map((item) => item.id));
      }
    },
    [allItems, selectedItems.length],
  );

  useEffect(() => {
    onChange(selectedItems.map((item) => sortMap[item]));
  }, [onChange, selectedItems, sortMap]);

  const { t } = useTranslation();
  const groups: LabelGroup[] = uniqBy(
    [
      ...allItems.reduce<LabelGroup[]>((acc, item) => {
        return item.group ? [...acc, item.group] : acc;
      }, []),
      // Items with no group
      { id: 0, name: t("Ungrouped") },
    ],
    "id",
  );
  const groupedItems = groupBy(allItems, (item) => item.group?.id ?? 0);

  return (
    <Box>
      <FormControl fullWidth>
        <InputLabel>{selectedItemsLabel}</InputLabel>
        <Select
          multiple
          value={selectedItems}
          onChange={handleChange}
          renderValue={(values) => {
            return (
              <Stack spacing={2}>
                {values.toSorted(compareFn).map((value) => (
                  <Typography key={value} whiteSpace="normal">
                    {sortMap[value].label}
                  </Typography>
                ))}
              </Stack>
            );
          }}
          MenuProps={{
            slotProps: {
              paper: {
                sx: { maxHeight: 400 },
              },
            },
            anchorOrigin: {
              vertical: "top",
              horizontal: "left",
            },
            transformOrigin: {
              vertical: "top",
              horizontal: "left",
            },
          }}
        >
          <ListSubheader disableGutters sx={{ zIndex: 1 }}>
            <Stack
              direction="row"
              paddingY={1}
              paddingX={3}
              onClick={handleToggleAll()}
            >
              <ListItemIcon>
                <Checkbox
                  disableRipple
                  checked={
                    selectedItems.length === allItems.length &&
                    allItems.length !== 0
                  }
                  indeterminate={
                    selectedItems.length !== allItems.length &&
                    selectedItems.length !== 0
                  }
                />
              </ListItemIcon>
              <Stack>
                <Typography variant="subtitle2">{allItemsLabel}</Typography>
                <Typography variant="caption">{`${selectedItems.length}/${allItems.length} selected`}</Typography>
              </Stack>
            </Stack>
            <Divider />
          </ListSubheader>
          {groups.flatMap((group) => {
            const groupItems = groupedItems[group.id];
            if (!groupItems) return null;

            return [
              <ListSubheader disableSticky key={`${group.id}-${group.name}`}>
                {group.name}
              </ListSubheader>,
              ...groupItems.map((item) => {
                return (
                  <MenuItem key={item.id} value={item.id} disableRipple>
                    <Checkbox
                      checked={selectedItems.includes(item.id)}
                      disableRipple
                    />
                    <ListItemText sx={{ whiteSpace: "normal" }}>
                      {item.label}
                    </ListItemText>
                  </MenuItem>
                );
              }),
            ];
          })}
        </Select>
      </FormControl>
    </Box>
  );
};

export default MultiSelectList;
