import { useEffect } from 'react';
import { Controller, useFormContext, useWatch } from 'react-hook-form';
import { useInfiniteQuery, useQuery } from 'react-query';
import { useLocation } from 'react-router-dom';

import isNil from 'lodash-es/isNil';
import Stack from '@material-hu/mui/Stack';

import HuAutocomplete from '@material-hu/components/design-system/Inputs/Autocomplete';

import { useCycleContext } from 'src/pages/dashboard/goals/CycleContext';
import { CycleValues } from 'src/pages/dashboard/goals/constants';
import { goalsKeys } from 'src/pages/dashboard/goals/queries';
import { getDefaultGoalCycleId } from 'src/pages/dashboard/goals/utils';
import { useLokaliseTranslation } from 'src/utils/i18n';
import { flatPages } from 'src/utils/pagination';

import { getGoalCycle, getGoalCycles } from '../services';

export type CycleSelectorProps = {
  showAllAndUnassignedOptions?: boolean;
  onChange?: (nextValue: number | null) => void;
};

const DEFAULT_PAGINATION = { page: 0, limit: 20 };

const CycleSelector = ({
  showAllAndUnassignedOptions = false,
  onChange,
}: CycleSelectorProps) => {
  const { t } = useLokaliseTranslation();
  const { control, setValue } = useFormContext();
  const { setSelectedCycleId } = useCycleContext();
  const location = useLocation();

  const goalCycleId = useWatch({ name: 'goalCycleId' });
  const goalCycleIdFromState = (location.state as { goalCycleId?: number })
    ?.goalCycleId;

  useEffect(() => {
    if (!isNil(goalCycleId)) {
      setSelectedCycleId(goalCycleId);
    }
  }, [goalCycleId, setSelectedCycleId]);

  const {
    data: goalCyclesPages,
    isLoading,
    fetchNextPage,
    hasNextPage,
    isFetchingNextPage,
  } = useInfiniteQuery(
    goalsKeys.cycles(DEFAULT_PAGINATION),
    ({ pageParam = DEFAULT_PAGINATION.page }) =>
      getGoalCycles({ ...DEFAULT_PAGINATION, page: pageParam }),
    {
      getNextPageParam: lastPage => {
        const { page, totalPages } = lastPage.data;
        return page < totalPages ? page : undefined;
      },
      keepPreviousData: true,
      staleTime: 5 * 60 * 1000,
    },
  );

  const goalCycles = flatPages(goalCyclesPages);

  const shouldFetchSelectedCycle =
    !!goalCycleId &&
    goalCycleId > 0 &&
    !goalCycles.some(cycle => cycle.id === goalCycleId);

  const { data: selectedGoalCycle } = useQuery(
    goalsKeys.cycle(goalCycleId),
    () => getGoalCycle(goalCycleId),
    {
      enabled: shouldFetchSelectedCycle,
      select: response => response.data,
    },
  );

  useEffect(() => {
    if (goalCycles.length > 0 && !goalCycleId) {
      const defaultValue = getDefaultGoalCycleId(
        goalCycleIdFromState,
        showAllAndUnassignedOptions,
        goalCycles,
      );
      setValue('goalCycleId', defaultValue);
    }
  }, [
    goalCycles,
    goalCycleId,
    goalCycleIdFromState,
    showAllAndUnassignedOptions,
    setValue,
  ]);

  const baseOptions = new Map(
    goalCycles.map(cycle => [cycle.id, { value: cycle.id, label: cycle.name }]),
  );

  if (selectedGoalCycle && !baseOptions.has(selectedGoalCycle.id)) {
    baseOptions.set(selectedGoalCycle.id, {
      value: selectedGoalCycle.id,
      label: selectedGoalCycle.name,
    });
  }

  const goalsCyclesOptions = showAllAndUnassignedOptions
    ? [
        ...Array.from(baseOptions.values()),
        { value: CycleValues.ALL_CYCLES, label: t('general:all') },
        {
          value: CycleValues.UNASSIGNED_CYCLES,
          label: t('general:unassigned'),
        },
      ]
    : Array.from(baseOptions.values());

  const selectedOption =
    goalsCyclesOptions.find(option => option.value === goalCycleId) || null;

  return (
    goalsCyclesOptions.length > 0 && (
      <Controller
        name="goalCycleId"
        control={control}
        render={({ field }) => (
          <Stack
            sx={{
              width: '100%',
              maxWidth: 400,
            }}
          >
            <HuAutocomplete
              options={goalsCyclesOptions}
              value={selectedOption}
              onChange={nextOption => {
                const nextValue = nextOption ? nextOption.value : null;
                field.onChange(nextValue);
                onChange?.(nextValue);
              }}
              loading={isLoading}
              hasMoreOptions={hasNextPage}
              onLoadMore={() => {
                if (!hasNextPage || isFetchingNextPage) return;
                fetchNextPage();
              }}
              label={t('general:cycle')}
              clearIcon={false}
              disableClearable
              freeSolo={false}
              isServerFiltered={false}
              ListboxProps={{
                style: { maxHeight: 360 },
              }}
              fullWidth
            />
          </Stack>
        )}
      />
    )
  );
};

export default CycleSelector;
