import { type FC, useEffect } from 'react';
import {
  Controller,
  FormProvider,
  useForm,
  useFormContext,
} from 'react-hook-form';

import { difference, intersection, uniq, without } from 'lodash-es';
import SearchIcon from '@material-hu/icons/material/Search';
import Card, { type CardProps } from '@material-hu/mui/Card';
import Stack from '@material-hu/mui/Stack';
import Typography from '@material-hu/mui/Typography';

import Button from '@material-hu/components/design-system/Buttons/Button';

import { OXXO_ID } from 'src/constants';
import useAuth from 'src/contexts/JWTContext';
import useUserIdsBySegmentationItem from 'src/hooks/queryHooks/useUserIdsBySegmentationItem';
import useFullUsers from 'src/hooks/useFullUsers';
import {
  UsersItemsOperation,
  useInfiniteUsersPublicSearch,
} from 'src/services/usersQueries';
import { type User } from 'src/types/user';
import { addOrRemove } from 'src/utils/arrayUtils';
import { useLokaliseTranslation } from 'src/utils/i18n';
import { type PaginatedResponse } from 'src/utils/tableUtils';

import CircularProgress from 'src/components/CircularProgress';
import FormSelectSegmentationItem from 'src/components/FormInputs/FormSelectSegmentationItem';
import FormTextField from 'src/components/FormInputs/FormTextField';
import UsersList from 'src/components/UsersList';

type UsersPage = {
  data: PaginatedResponse<User>;
};

type FormType = {
  search: string;
  searchAssigned: string;
  itemIds: number[];
  selected: number[];
};

type Props = {
  name: string;
  participantsLabel?: string;
  searchLabel?: string;
  useCustomUserSearch?: Function;
  usersPool?: number[];
  disabled?: boolean;
  filterOperation?: UsersItemsOperation;
  slotProps?: Partial<{
    card: Partial<CardProps>;
  }>;
};

const FormEmployeesSelection: FC<Props> = ({
  name,
  participantsLabel,
  searchLabel,
  useCustomUserSearch,
  usersPool = [],
  disabled = false,
  filterOperation = UsersItemsOperation.UNION,
  slotProps = { card: {} },
}: Props) => {
  const { t } = useLokaliseTranslation(['backoffice_only']);
  const { control, watch, setValue } = useFormContext();
  const assigned = watch(name, []) as number[];

  const { instance } = useAuth();
  const form = useForm<FormType>({
    defaultValues: {
      search: '',
      searchAssigned: '',
      itemIds: [],
      selected: [],
    },
  });
  const { search, searchAssigned, itemIds, selected } = form.watch();

  useEffect(
    () => form.setValue('selected', assigned), // deselect all when search or itemId changes
    [search, itemIds],
  );

  const allUsers = (useCustomUserSearch || useInfiniteUsersPublicSearch)({
    search,
    segmentationItemIds: [itemIds],
    itemIdFilterOperation: filterOperation,
  });
  const selectedUsers = useFullUsers(assigned, searchAssigned);
  const isOxxo = instance?.id === OXXO_ID;
  const groupItemIds = isOxxo
    ? { data: [], isFetching: false }
    : useUserIdsBySegmentationItem();

  if (groupItemIds.isFetching) {
    return (
      <CircularProgress
        centered
        sx={{ mt: 1 }}
      />
    );
  }

  const addSelectedUsers = () => {
    if (disabled) return;
    setValue(name, uniq([...assigned, ...selected]));
    form.setValue('searchAssigned', '');
  };

  const selectedGroupItems =
    groupItemIds.data?.filter(item => itemIds.includes(item.id)) || [];
  const groupItemUsers = !itemIds.length
    ? groupItemIds.data?.flatMap(item => item.userIds) || []
    : filterOperation === UsersItemsOperation.INTERSECT
      ? selectedGroupItems.reduce<number[]>(
          (acc, item, index) =>
            index === 0 ? item.userIds : intersection(acc, item.userIds),
          [],
        )
      : selectedGroupItems.flatMap(item => item.userIds);

  const usersForSelectAll =
    usersPool.length > 0
      ? intersection(usersPool, groupItemUsers)
      : groupItemUsers;

  const allUsersSelected = difference(usersForSelectAll, selected).length === 0;
  const someUsersSelected =
    !allUsersSelected && difference(selected, assigned).length > 0;

  const onSelectAll = () => {
    if (disabled) return;
    form.setValue(
      'selected',
      allUsersSelected || someUsersSelected
        ? assigned
        : uniq([...selected, ...usersForSelectAll]),
    );
  };

  const removeAssignedUser = (userId: number) => {
    if (disabled) return;
    setValue(name, without(assigned, userId));
    form.setValue('selected', without(selected, userId));
  };

  const handleDeselectAll = () => {
    if (disabled) return;
    form.setValue('selected', []);
    setValue(name, []);
  };

  return (
    <Controller
      control={control}
      name={name}
      render={() => (
        <FormProvider {...form}>
          <Card
            {...slotProps.card}
            sx={{ padding: 0, ...slotProps.card?.sx }}
          >
            <Stack
              direction="row"
              justifyContent="space-between"
              spacing={2}
            >
              <Stack
                sx={{ flex: 2, height: 450 }}
                spacing={1}
                px={2}
                py={3}
              >
                <FormSelectSegmentationItem
                  name="itemIds"
                  label={t('backoffice_only:form_employees_selection.filters')}
                  selectProps={{ multiple: true }}
                  filterIcon
                />
                <FormTextField
                  name="search"
                  placeholder={
                    searchLabel ||
                    t(
                      'backoffice_only:form_employees_selection.search_employees',
                    )
                  }
                  InputProps={{ endAdornment: <SearchIcon /> }}
                  disabled={disabled}
                />
                {allUsers.isFetching && <CircularProgress centered />}
                {!allUsers.isFetching && (
                  <>
                    <UsersList
                      checkbox={{
                        usersCount: allUsers.data.pages[0].data.count,
                        selected,
                        disabled: assigned,
                        onSelect: userId => {
                          if (disabled) return;
                          form.setValue(
                            'selected',
                            addOrRemove(selected, userId),
                          );
                        },
                        onSelectAll:
                          !isOxxo && !search && !disabled
                            ? onSelectAll
                            : undefined,
                        allSelected: !isOxxo && allUsersSelected,
                        someSelected: !isOxxo && someUsersSelected,
                      }}
                      users={allUsers.data.pages.flatMap(
                        (page: UsersPage) => page.data.items,
                      )}
                      sx={{
                        overflowY: 'scroll',
                        flex: 1,
                        '::-webkit-scrollbar': {
                          width: '5px',
                          background: 'transparent',
                        },
                        '::-webkit-scrollbar-thumb': {
                          borderRadius: '5px',
                          backgroundColor: '#C1C1C1',
                        },
                      }}
                      onEndScroll={allUsers.fetchNextPage}
                      showDivider
                    />
                    {allUsers.isFetchingNextPage && (
                      <CircularProgress centered />
                    )}
                  </>
                )}
                <Button
                  fullWidth
                  disabled={!selected?.length || disabled}
                  loading={selectedUsers.isLoading}
                  variant="contained"
                  sx={{ alignSelf: 'center' }}
                  onClick={addSelectedUsers}
                >
                  {t('backoffice_only:form_employees_selection.add')}
                </Button>
              </Stack>
              <Stack
                sx={{ flex: 1, height: 450, backgroundColor: '#FAFAFA' }}
                px={2}
                py={3}
              >
                <Typography variant="h5">
                  {participantsLabel ||
                    t('backoffice_only:form_employees_selection.participants')}
                </Typography>
                <FormTextField
                  name="searchAssigned"
                  size="small"
                  sx={{ my: 1 }}
                  placeholder={t('general:search')}
                  disabled={disabled}
                />
                {!!assigned?.length && (
                  <>
                    <UsersList
                      users={selectedUsers.data}
                      sx={{
                        overflowY: 'scroll',
                        flex: 1,
                        my: 1,
                        '::-webkit-scrollbar': {
                          width: '5px',
                          background: 'transparent',
                        },
                        '::-webkit-scrollbar-thumb': {
                          borderRadius: '5px',
                          backgroundColor: '#C1C1C1',
                        },
                      }}
                      onRemove={disabled ? undefined : removeAssignedUser}
                      showAvatar={false}
                    />
                    <Button
                      fullWidth
                      disabled={selectedUsers.isLoading || disabled}
                      variant="outlined"
                      onClick={handleDeselectAll}
                    >
                      {t('backoffice_only:form_employees_selection.delete_all')}
                    </Button>
                  </>
                )}
              </Stack>
            </Stack>
          </Card>
        </FormProvider>
      )}
    />
  );
};

export default FormEmployeesSelection;
