import { Fragment, useEffect, useMemo, useState } from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import { useQuery } from 'react-query';

import { fromPairs, pickBy, without } from 'lodash-es';
import { IconArrowsSort, IconFilter } from '@material-hu/icons/tabler';
import Badge from '@material-hu/mui/Badge';
import Card from '@material-hu/mui/Card';
import ClickAwayListener from '@material-hu/mui/ClickAwayListener';
import Divider from '@material-hu/mui/Divider';
import Popper from '@material-hu/mui/Popper';
import Stack from '@material-hu/mui/Stack';
import Typography from '@material-hu/mui/Typography';

import HuAlert from '@material-hu/components/design-system/Alert';
import Button from '@material-hu/components/design-system/Buttons/Button';
import HuCardContainer from '@material-hu/components/design-system/CardContainer';
import HuFormCheckbox from '@material-hu/components/design-system/Checkbox/Checkbox/form';
import HuDrawer from '@material-hu/components/design-system/Drawer';
import HuFormDatePicker from '@material-hu/components/design-system/Inputs/DatePicker/form';
import HuSpinner from '@material-hu/components/design-system/ProgressIndicators/Spinner';
import HuFormRadioButton from '@material-hu/components/design-system/RadioButton/RadioButton/form';
import useHuSnackbar from '@material-hu/components/design-system/Snackbar';
import HuFormSwitcher from '@material-hu/components/design-system/Switcher/form';
import HuTitle from '@material-hu/components/design-system/Title';

import { logEvent } from 'src/config/logging';
import { useGetManagerTypes } from 'src/hooks/queryHooks/vacations';
import { getSegmentationGroups } from 'src/services/segmentations';
import { getPolicies } from 'src/services/vacations';
import { EventName, Modules } from 'src/types/amplitude';
import {
  type PoliciesResponse,
  type PolicyTypes,
  type SectionsManager,
  VacationStatus,
  type VacationsFilterValues,
  VacationsRequestSort,
} from 'src/types/vacations';
import { useLokaliseTranslation } from 'src/utils/i18n';
import {
  getAmplitudeValuesForFilters,
  getOptionsSelected,
  getVisibilityCount,
  getVisibilityDescription,
  getVisibilityOptions,
} from 'src/utils/vacations';

import { vacationsKeys } from '../../queries';
import FormUserAutocompleteVacations from '../inputs/FormUserAutocompleteVacations';

import VacationsFilterAccordion from './GeneralFilterAccordion';
import SegmentationFilterAccordion from './SegmentationFilterAccordion';
import { SortOptionButton } from './SortOptionButton';

export type VacationsFilterProps = {
  policyTypes: PolicyTypes[];
  showStates?: boolean;
  showDates?: boolean;
  /** @deprecated it may be used again in the future for colaborator-side filtering */
  filterByPolicy?: boolean;
  excludedStates?: VacationStatus[];
  isCalendar?: boolean;
  isBalances?: boolean;
  sortable?: boolean;
  section: SectionsManager;
  isManagerView?: boolean;
};

const getDefaultValues = (
  isAdmin: boolean,
  isBalances: boolean,
  isManagerView: boolean,
) => {
  return {
    users: [],
    fromDate: null,
    toDate: null,
    policyTypeIds: {},
    policyIds: {},
    states: {},
    onlyPeopleWithRequests: !isManagerView,
    orderBy: VacationsRequestSort.CREATED_AT,
    order: 'DESC',
    segmentation: {},
    visibility: {
      all: isAdmin || !isBalances,
      directTeam: isBalances && !isAdmin,
      indirectTeam: false,
    },
  };
};

const useVacationsFilter = (props: VacationsFilterProps) => {
  const {
    policyTypes = [],
    showStates,
    showDates,
    filterByPolicy = true,
    excludedStates = [],
    isCalendar = false,
    isBalances = false,
    sortable = false,
    section,
    isManagerView = true,
  } = props;

  const { t } = useLokaliseTranslation('time_off');
  const { isAdmin } = useGetManagerTypes();

  const initialValues = getDefaultValues(isAdmin, isBalances, isManagerView);

  const [open, setOpen] = useState(false);
  const [finalValues, setFinalValues] =
    useState<VacationsFilterValues>(initialValues);

  const handleToggle = () => setOpen(!open);

  const form = useForm<VacationsFilterValues>({
    defaultValues: initialValues,
    mode: 'onChange',
  });

  const { enqueueSnackbar } = useHuSnackbar();

  const {
    users,
    fromDate,
    toDate,
    policyIds,
    policyTypeIds,
    states,
    onlyPeopleWithRequests,
    order,
    orderBy,
    segmentation,
    visibility,
  } = form.watch();

  const selectedPolicyTypesIds = Object.keys(policyTypeIds)
    .filter(key => policyTypeIds[Number(key)])
    .map(Number);

  const {
    selectedCount: selectedPolicyTypesCount,
    allSelected: allPolicyTypesSelected,
    someSelected: somePolicyTypesSelected,
  } = getOptionsSelected(policyTypeIds, policyTypes);

  const selectedStatesCount = Object.values(states).filter(Boolean).length;
  const selectedPoliciesCount = Object.values(policyIds).filter(Boolean).length;

  const visibilityCount = getVisibilityCount(visibility, isAdmin, isBalances);

  const filterCount = useMemo(() => {
    return (
      visibilityCount +
      (finalValues.users.length ? 1 : 0) +
      (finalValues.fromDate || finalValues.toDate ? 1 : 0) +
      (finalValues.onlyPeopleWithRequests ? 1 : 0) +
      (Object.values(
        filterByPolicy ? finalValues.policyIds : finalValues.policyTypeIds,
      ).some(Boolean)
        ? 1
        : 0) +
      (Object.values(finalValues.states).some(Boolean) ? 1 : 0) +
      (Object.values(finalValues.segmentation).some(Boolean) ? 1 : 0)
    );
  }, [
    visibilityCount,
    finalValues.users.length,
    finalValues.fromDate,
    finalValues.toDate,
    finalValues.onlyPeopleWithRequests,
    filterByPolicy,
    finalValues.policyIds,
    finalValues.policyTypeIds,
    finalValues.states,
    finalValues.segmentation,
  ]);

  const { data: availablePolicies = [] } = useQuery(
    vacationsKeys.policies(selectedPolicyTypesIds),
    () => getPolicies(selectedPolicyTypesIds),
    {
      onSuccess: (response: PoliciesResponse = []) => {
        // when the user selects a new policyType, we muest check the new policyIds checkboxes

        const newAvailablePolicyIds = response.map(p => p.id);

        // remove the previous policyIds from unchecked policyTypes
        const newPolicyIds = pickBy(
          policyIds,
          (value, key) =>
            //It is necessary to compare 'value' with 'false' since when the checkbox is mounted,
            //it is assigned with the value 'undefined'.
            newAvailablePolicyIds.includes(Number(key)) && value === false,
        );

        // set all NEW values to true
        without(
          newAvailablePolicyIds,
          ...Object.keys(newPolicyIds).map(Number),
        ).forEach(policyId => {
          newPolicyIds[policyId] = true;
        });
        form.setValue('policyIds', newPolicyIds);
      },
      select: r => (selectedPolicyTypesCount ? r.data : []),
      enabled: open && filterByPolicy && isManagerView,
    },
  );

  const {
    data: segmentationGroups = [],
    isLoading: isLoadingSegmentationGroups,
  } = useQuery(vacationsKeys.segmentation(), () => getSegmentationGroups(), {
    select: response => response.data.filter(group => group.items.length),
    staleTime: Infinity,
    enabled: open && isAdmin,
    onSuccess: response => {
      form.setValue(
        'segmentation',
        response.reduce(
          (acc, cur) => {
            cur.items.forEach(item => {
              acc[`selected_${item.id}`] = false;
            });
            return acc;
          },
          {} as Record<string, any>,
        ),
      );
    },
  });

  const handleApply = () => {
    const values = form.getValues();

    if (isManagerView) {
      const valuesToAmplitude = getAmplitudeValuesForFilters({
        section,
        values,
        users,
        availablePolicies,
        policyIds,
        policyTypes,
        policyTypeIds,
        states,
        fromDate,
        toDate,
        segmentation,
      });

      logEvent(
        EventName.TIME_OFF_MANAGEMENT_TABS_FILTER_APPLIED,
        valuesToAmplitude,
      );
    } else {
      logEvent(EventName.TIME_OFF_PERSONAL_SPACE_TABS_FILTER_APPLIED, {
        userIds: values.users.map(u => u.id),
        module: Modules.VACATIONS,
      });
    }

    enqueueSnackbar({
      title: t('filters_applied_success'),
      variant: 'info',
    });
    setFinalValues(values);
    setOpen(false);
  };

  const handleReset = () => {
    setFinalValues(initialValues);
    setOpen(false);
    enqueueSnackbar({
      title: t('general:filters_cleared_success'),
      variant: 'info',
    });
  };

  useEffect(
    // reset form values when closing the drawer
    () => {
      form.reset(finalValues);
    },
    [open],
  );

  const availableStates = without(
    Object.values(VacationStatus),
    ...excludedStates,
  );
  const allStatesSelected = selectedStatesCount === availableStates.length;
  const someStatesSelected = selectedStatesCount > 0 && !allStatesSelected;

  const allPoliciesSelected =
    selectedPoliciesCount === availablePolicies.length;
  const somePoliciesSelected =
    selectedPoliciesCount > 0 && !allPoliciesSelected;

  const [sortAnchorEl, setSortAnchorEl] = useState<null | HTMLElement>(null);
  const hideSortingPopper = () => setSortAnchorEl(null);
  const handleSortClick = (event: React.MouseEvent<HTMLElement>) => {
    setSortAnchorEl(sortAnchorEl ? null : event.currentTarget);
  };

  const filterOptions = [
    {
      title: t('general:state'),
      sortOptions: [
        {
          orderByValue: VacationsRequestSort.STATE,
          orderValue: 'ASC',
          children: t('pending_first'),
        },
      ],
    },
    {
      title: t('requested_on'),
      sortOptions: [
        {
          orderByValue: VacationsRequestSort.CREATED_AT,
          orderValue: 'DESC',
          children: t('recent_first'),
        },
        {
          orderByValue: VacationsRequestSort.CREATED_AT,
          orderValue: 'ASC',
          children: t('oldest_first'),
        },
      ],
    },
    {
      title: t('request_start_date'),
      sortOptions: [
        {
          orderByValue: VacationsRequestSort.FROM_DATE,
          orderValue: 'DESC',
          children: t('future_first'),
        },
        {
          orderByValue: VacationsRequestSort.FROM_DATE,
          orderValue: 'ASC',
          children: t('oldest_first'),
        },
      ],
    },
  ];

  const visibilityOptions = getVisibilityOptions(isAdmin, isBalances, t);

  const visibilityDescription = getVisibilityDescription(
    visibility,
    isAdmin,
    t,
  );

  const component = (
    <FormProvider {...form}>
      {sortable && (
        <>
          <Button
            startIcon={<IconArrowsSort />}
            onClick={handleSortClick}
            variant="tertiary"
          >
            {t('sort_by')}
          </Button>
          <Popper
            open={!!sortAnchorEl}
            anchorEl={sortAnchorEl}
          >
            <ClickAwayListener onClickAway={hideSortingPopper}>
              <Card sx={{ p: 2, width: 210 }}>
                <Stack sx={{ alignItems: 'flex-start', gap: 1 }}>
                  {filterOptions.map((category, index) => (
                    <Fragment key={category.title}>
                      <Typography
                        sx={{
                          color: theme =>
                            theme.palette.new.text.neutral.lighter,
                          pl: 1,
                        }}
                        variant="globalXXS"
                      >
                        {category.title}
                      </Typography>
                      {category.sortOptions.map(option => (
                        <SortOptionButton
                          {...option}
                          key={option.orderByValue + option.orderValue}
                          onClick={hideSortingPopper}
                        />
                      ))}
                      {index < filterOptions.length - 1 && (
                        <Divider sx={{ width: '100%', my: 1.5 }} />
                      )}
                    </Fragment>
                  ))}
                </Stack>
              </Card>
            </ClickAwayListener>
          </Popper>
        </>
      )}
      <Button
        variant="secondary"
        startIcon={<IconFilter />}
        onClick={handleToggle}
        endIcon={
          <Badge
            sx={{ mx: filterCount && 1.5 }}
            color="primary"
            badgeContent={filterCount}
          />
        }
      >
        {t('filter')}
      </Button>
      <HuDrawer
        open={open}
        onClose={handleToggle}
        title={t('filter_by')}
        primaryButtonProps={{
          children: t('apply'),
          onClick: handleApply,
          sx: { width: '100%' },
        }}
        secondaryButtonProps={{
          children: t('general:reset_filters'),
          onClick: handleReset,
          sx: { width: '100%' },
        }}
      >
        <Stack>
          {isCalendar && (
            <>
              <HuFormSwitcher
                name="onlyPeopleWithRequests"
                switcherProps={{
                  title: t('only_active_requests'),
                }}
              />
              <Divider sx={{ my: 3 }} />
            </>
          )}
          {isManagerView && (
            <VacationsFilterAccordion
              label={t('team_visibility')}
              accordionProps={{ description: visibilityDescription }}
            >
              <HuCardContainer sx={{ width: '100%' }}>
                <Stack sx={{ gap: 2 }}>
                  {visibilityOptions.map(visibilityOption => (
                    <HuFormRadioButton
                      isOnlyOption
                      key={visibilityOption.name}
                      name={visibilityOption.name}
                      radioButtonProps={{
                        label: visibilityOption.title,
                        description: visibilityOption.description,
                      }}
                    />
                  ))}
                </Stack>
              </HuCardContainer>
            </VacationsFilterAccordion>
          )}
          <VacationsFilterAccordion
            label={t('collaborators')}
            accordionProps={{
              defaultExpanded: users.length > 0,
            }}
          >
            {!!users.length && onlyPeopleWithRequests && (
              <HuAlert
                sx={{ mt: 1 }}
                severity="warning"
                title={t('selected_active_requests_only_title')}
                description={t('selected_active_request_only_description')}
                hasClose
              />
            )}
            <FormUserAutocompleteVacations
              type={section}
              isManagerView={isManagerView}
            />
          </VacationsFilterAccordion>
          {isManagerView && (
            <>
              <VacationsFilterAccordion
                label={t('policy_type_one')}
                accordionProps={{
                  defaultExpanded: Object.values(policyTypeIds).some(Boolean),
                }}
              >
                <HuCardContainer
                  sx={{
                    width: '100%',
                  }}
                >
                  <Stack sx={{ gap: 2 }}>
                    <HuFormCheckbox
                      checkBoxProps={{
                        label: t('select_all'),
                        onChange: () => {
                          const availablePolicyTypesIds = policyTypes.map(
                            pt => pt.id,
                          );
                          form.setValue(
                            'policyTypeIds',
                            fromPairs(
                              availablePolicyTypesIds.map(pt => [
                                pt,
                                !allPolicyTypesSelected,
                              ]),
                            ),
                            { shouldDirty: true },
                          );
                        },
                        checked: allPolicyTypesSelected,
                        indeterminate: somePolicyTypesSelected,
                      }}
                      name={'policyTypeIds.ALL'}
                    />
                    {policyTypes.map(policyType => (
                      <HuFormCheckbox
                        key={policyType.id}
                        name={`policyTypeIds.${policyType.id}`}
                        checkBoxProps={{
                          label: policyType.name,
                        }}
                      />
                    ))}
                  </Stack>
                </HuCardContainer>
              </VacationsFilterAccordion>
              {filterByPolicy && (
                <VacationsFilterAccordion
                  label={t('policies')}
                  accordionProps={{
                    defaultExpanded: Object.values(policyIds).some(Boolean),
                  }}
                >
                  {!!availablePolicies.length && (
                    <HuCardContainer sx={{ width: '100%' }}>
                      <Stack sx={{ gap: 2 }}>
                        <HuFormCheckbox
                          name={'policyIds.ALL'}
                          checkBoxProps={{
                            checked: allPoliciesSelected,
                            indeterminate: somePoliciesSelected,
                            label: t('select_all'),
                            onChange: () => {
                              const availablePolicyIds = availablePolicies.map(
                                p => p.id,
                              );
                              form.setValue(
                                'policyIds',
                                fromPairs(
                                  availablePolicyIds.map(p => [
                                    p,
                                    !allPoliciesSelected,
                                  ]),
                                ),
                                { shouldDirty: true },
                              );
                            },
                          }}
                        />
                        {availablePolicies.map(policy => (
                          <HuFormCheckbox
                            checkBoxProps={{
                              label: policy.name,
                            }}
                            name={`policyIds.${policy.id}`}
                            key={policy.id}
                          />
                        ))}
                      </Stack>
                    </HuCardContainer>
                  )}
                  {!availablePolicies.length && (
                    <HuAlert
                      severity="info"
                      title={t('must_select_policy')}
                    />
                  )}
                </VacationsFilterAccordion>
              )}
              {showStates && (
                <VacationsFilterAccordion
                  label={t('general:state')}
                  accordionProps={{
                    defaultExpanded: Object.values(states).some(Boolean),
                  }}
                >
                  <HuCardContainer sx={{ width: '100%' }}>
                    <Stack sx={{ gap: 2 }}>
                      <HuFormCheckbox
                        name={'states.ALL'}
                        checkBoxProps={{
                          label: t('select_all'),
                          checked: allStatesSelected,
                          indeterminate: someStatesSelected,
                          onChange: () => {
                            form.setValue(
                              'states',
                              fromPairs(
                                availableStates.map(status => [
                                  status,
                                  !allStatesSelected,
                                ]),
                              ),
                              { shouldDirty: true },
                            );
                          },
                        }}
                      />
                      {availableStates.map(status => (
                        <HuFormCheckbox
                          checkBoxProps={{
                            label: t('vacations_status', { context: status }),
                          }}
                          name={`states.${status}`}
                          key={status}
                        />
                      ))}
                    </Stack>
                  </HuCardContainer>
                </VacationsFilterAccordion>
              )}
              {showDates && (
                <VacationsFilterAccordion
                  label={t('request_period')}
                  accordionProps={{
                    defaultExpanded: !!fromDate || !!toDate,
                  }}
                >
                  <Stack sx={{ gap: 2 }}>
                    <HuFormDatePicker
                      name="fromDate"
                      inputProps={{
                        label: t('from'),
                      }}
                    />
                    <HuFormDatePicker
                      name="toDate"
                      inputProps={{
                        minDate: fromDate ? new Date(fromDate) : undefined,
                        label: t('until'),
                      }}
                    />
                  </Stack>
                </VacationsFilterAccordion>
              )}
              {isLoadingSegmentationGroups && <HuSpinner sx={{ mt: 3 }} />}
              {!!segmentationGroups.length && (
                <>
                  <HuTitle
                    variant="M"
                    title={t('general:segmentation')}
                    sx={{ my: 2 }}
                  />
                  {segmentationGroups?.map(group => (
                    <SegmentationFilterAccordion
                      key={group.id}
                      group={group}
                    />
                  ))}
                </>
              )}
            </>
          )}
        </Stack>
      </HuDrawer>
    </FormProvider>
  );

  return {
    component,
    values: {
      ...finalValues,
      orderBy: sortable ? orderBy : undefined,
      order: sortable ? order : undefined,
    },
  };
};

export type VacationsFilterReturn = ReturnType<typeof useVacationsFilter>;
export default useVacationsFilter;
