import { type FC, useCallback, useMemo, useRef, useState } from 'react';
import { useQuery } from 'react-query';

import { useDrawerV2 } from '@material-hu/hooks/useDrawerV2';
import Stack from '@material-hu/mui/Stack';

import useSelectedCollaboratorsDrawer from '@material-hu/components/composed-components/audience/hooks/useSelectedCollaboratorsDrawer';
import SegmentationGroupItemsSelection from '@material-hu/components/composed-components/audience/SegmentationGroupItemsSelection';
import HuAlert from '@material-hu/components/design-system/Alert';
import useSnackbar from '@material-hu/components/design-system/Snackbar';
import Title from '@material-hu/components/design-system/Title';

import { useAuth } from 'src/contexts/JWTContext';
import { feedKeys } from 'src/pages/dashboard/feed/queries';
import { getSegmentationGroups } from 'src/services/segmentations';
import {
  getAmountUsersReachSegmentation,
  getUserListSegmentation,
} from 'src/services/users';
import {
  type Segmentation,
  type SegmentationMapValues,
} from 'src/types/segmentation';
import { useLokaliseTranslation } from 'src/utils/i18n';

type UseSegmentationDrawerParams = {
  selectedItems: Map<number, SegmentationMapValues[]>;
  onSubmit?: (
    segmentationsSelected: Map<number, SegmentationMapValues[]>,
  ) => void;
  onCancel?: () => void;
  readOnly?: boolean;
};

const mapToRecord = (
  map: Map<number, SegmentationMapValues[]>,
): Record<number, Set<number>> => {
  const record: Record<number, Set<number>> = {};
  for (const [groupId, items] of map.entries()) {
    record[groupId] = new Set(items.map(item => item.id));
  }
  return record;
};

const recordToMap = (
  record: Record<number, Set<number>>,
  groups: Segmentation[],
): Map<number, SegmentationMapValues[]> => {
  const map = new Map<number, SegmentationMapValues[]>();
  for (const [groupIdStr, ids] of Object.entries(record)) {
    const groupId = Number(groupIdStr);
    if (!ids.size) continue;
    const group = groups.find(g => g.id === groupId);
    if (!group) continue;
    const items: SegmentationMapValues[] = [];
    for (const id of ids) {
      const item = group.items.find(i => i.id === id);
      if (item) items.push({ id: item.id, name: item.name });
    }
    if (items.length) map.set(groupId, items);
  }
  return map;
};

const getSegmentationIdsString = (
  record: Record<number, Set<number>>,
): string => {
  const ids = Object.values(record).flatMap(set => [...set]);
  return ids.length ? ids.join(',') : '';
};

const segmentationsQueryDataParser = (data: unknown) =>
  ((data as { data: Segmentation[] })?.data ?? [])
    .map((group: Segmentation) => ({
      id: group.id,
      name: group.name,
      items: group.items.map(item => ({
        id: item.id,
        name: item.name,
      })),
    }))
    .filter(g => g.items.length > 0);

type SegmentationDrawerBodyProps = {
  initialValue: Record<number, Set<number>>;
  readOnly: boolean;
  segmentationsQuery: ReturnType<typeof useQuery>;
  onChange: (value: Record<number, Set<number>>) => void;
};

const SegmentationDrawerBody: FC<SegmentationDrawerBodyProps> = ({
  initialValue,
  readOnly,
  segmentationsQuery,
  onChange,
}) => {
  const [value, setValue] = useState(initialValue);
  const { t } = useLokaliseTranslation(['audience', 'post', 'backoffice_only']);
  const { instance } = useAuth();

  const segmentationIds = useMemo(
    () => getSegmentationIdsString(value),
    [value],
  );

  const { data: amountOfUsersReach = 0 } = useQuery(
    feedKeys.segmentate.usersCount(segmentationIds, true),
    () =>
      getAmountUsersReachSegmentation(
        {
          instanceId: instance?.id ?? 0,
          segmentationItemIds: segmentationIds,
          includeLoggedUser: true,
        },
        true,
      ),
    {
      enabled: !!segmentationIds,
      keepPreviousData: true,
    },
  );

  const totalSelected = useMemo(
    () => Object.values(value).reduce((sum, set) => sum + set.size, 0),
    [value],
  );

  const handleChange = useCallback(
    (data: { selectedSegmentationIds: Record<number, Set<number>> }) => {
      if (readOnly) return;
      setValue(data.selectedSegmentationIds);
      onChange(data.selectedSegmentationIds);
    },
    [readOnly, onChange],
  );

  const { selectedCollaboratorsDrawer, showSelectedCollaboratorsDrawer } =
    useSelectedCollaboratorsDrawer();

  const collaboratorsService = useCallback(
    async (params: { q?: string; limit: number; cursor?: string }) => {
      const page = params.cursor ? Number.parseInt(params.cursor) : 1;
      const response = await getUserListSegmentation(
        {
          segmentationItemIds: segmentationIds,
          includeLoggedUser: true,
          page,
          limit: params.limit,
        },
        true,
      );
      return {
        data: {
          cursor:
            response.data.page < response.data.totalPages
              ? String(response.data.page + 1)
              : undefined,
          items: response.data.items,
        },
      };
    },
    [segmentationIds],
  );

  return (
    <Stack sx={{ gap: 2, height: '100%' }}>
      <Title
        variant="M"
        title={t('audience:segmentations.segmentation')}
      />
      <SegmentationGroupItemsSelection
        segmentationsQuery={segmentationsQuery}
        segmentationsQueryDataParser={segmentationsQueryDataParser}
        value={value}
        onChange={handleChange}
        allowSelectAll={false}
        slotProps={{
          search: {},
          stateCard: {
            title: t(
              'audience:form_segmentation_group_selector__no_search_results_title',
            ),
            description: t(
              'audience:form_segmentation_group_selector__no_search_results_description',
            ),
          },
          collapsibleSelectionList: {
            allowSelectAll: false,
            slotProps: {
              accordion: {
                getDescription: (selected: Set<number>, total: number) =>
                  selected.size > 0 ? `${selected.size}/${total}` : '',
              },
            },
          },
        }}
      />
      {totalSelected > 0 && (
        <Stack
          sx={{
            mt: 'auto',
            pt: 3,
            borderTop: theme =>
              `1px solid ${theme.palette.new.border.neutral.default}`,
          }}
        >
          {selectedCollaboratorsDrawer}
          <HuAlert
            title={t('post:reach_amount_users', {
              usersAmount: amountOfUsersReach,
            })}
            severity="info"
            action={{
              text: t('audience:see_collaborators'),
              onClick: () =>
                showSelectedCollaboratorsDrawer({
                  service: collaboratorsService,
                  queryKey: 'segmentation-collaborators',
                  totalCount: amountOfUsersReach,
                }),
            }}
          />
        </Stack>
      )}
    </Stack>
  );
};

export const useSegmentationDrawer = (params: UseSegmentationDrawerParams) => {
  const { selectedItems, onSubmit, onCancel, readOnly = false } = params;
  const { t } = useLokaliseTranslation(['audience', 'general']);
  const { enqueueSnackbar } = useSnackbar();

  const selectionRef = useRef<Record<number, Set<number>>>(
    mapToRecord(selectedItems),
  );
  const [openCount, setOpenCount] = useState(0);

  const segmentationsQuery = useQuery(
    feedKeys.segmentate.groups(),
    () => getSegmentationGroups(false, true),
    {
      enabled: openCount > 0,
      onError: () => {
        enqueueSnackbar({
          title: t('audience:error_loading_segmentation_groups'),
          variant: 'error',
        });
      },
    },
  );

  const groups = segmentationsQuery.data?.data ?? [];

  const handleSelectionChange = useCallback(
    (value: Record<number, Set<number>>) => {
      selectionRef.current = value;
    },
    [],
  );

  const { drawer, showDrawer } = useDrawerV2(({ closeDrawer }) => ({
    title: t('audience:segmented_users_title'),
    children: (
      <SegmentationDrawerBody
        key={openCount}
        initialValue={mapToRecord(selectedItems)}
        readOnly={readOnly}
        segmentationsQuery={segmentationsQuery}
        onChange={handleSelectionChange}
      />
    ),
    ...(readOnly
      ? {}
      : {
          primaryButtonProps: {
            children: t('general:select'),
            fullWidth: true,
            disabled: segmentationsQuery.isLoading,
            onClick: () => {
              onSubmit?.(recordToMap(selectionRef.current, groups));
              closeDrawer();
            },
          },
          secondaryButtonProps: {
            children: t('general:cancel'),
            fullWidth: true,
            onClick: () => {
              onCancel?.();
              closeDrawer();
            },
          },
        }),
    onClose: () => {
      onCancel?.();
      closeDrawer();
    },
  }));

  const showDrawerSynced: typeof showDrawer = useCallback(
    (...args) => {
      selectionRef.current = mapToRecord(selectedItems);
      setOpenCount(count => count + 1);
      return showDrawer(...args);
    },
    [showDrawer, selectedItems],
  );

  return { drawer, showDrawer: showDrawerSynced };
};
