import React, { useEffect, useMemo } from 'react';
import { Button, EmptyMessage, FormDropdown } from '@nova-hf/ui';
import { formatDate } from '../../utils/helpers';
import { PaymentPlanDraft } from '../../pages/thjonusta/stillingar/timabil/components/PaymentPlanDraft';
import s from '../../pages/thjonusta/stillingar/timabil/Timabil.module.scss';
import { useTranslation } from '../../utils/i18n';
import { useSnackbar } from 'notistack';
import { useLazyQuery, useMutation } from '@apollo/client';
import { SUBSCRIPTION_PERIODS } from '../subscription-periods/SubscriptionPeriodsQuery';
import {
  CALCULATE_PERIODS,
  CHANGE_SUBSCRIPTION_PERIOD,
} from '../../graphql/mutations/subscription';
import { periodMachine } from '../../pages/thjonusta/stillingar/timabil/periodMachine';
import { useMachine } from '@xstate/react';
import { addDays, addMonths, getDate, isSameDay } from 'date-fns';
import { inject } from 'mobx-react';
import { useRouter } from 'next/router';

interface ChangePayDateProps {
  authentication?: any;
  subscriptionId: string;
}

const ChangePayDate = ({
  subscriptionId,
  authentication: { accountInput },
}: ChangePayDateProps) => {
  const { t } = useTranslation(['stillingar']);
  const { enqueueSnackbar } = useSnackbar();
  const router = useRouter();
  const ssn = router.query.ssn ?? '';
  const needsRedirection = () => {
    if (router.pathname.includes('endurvirkjun')) {
      return true;
    }
    return false;
  };

  const [subscriptionPeriods, { data: getPeriodsData, error: getPeriodsError }] = useLazyQuery(
    SUBSCRIPTION_PERIODS,
    {
      variables: { subscriptionId, input: { ...accountInput } },
      fetchPolicy: 'network-only',
    },
  );

  const [changeSubscriptionPeriod] = useMutation(CHANGE_SUBSCRIPTION_PERIOD);
  const [calculatePeriods] = useMutation(CALCULATE_PERIODS);

  /*
    useMemo to ensure that the machine isn't recreated every time the component rerenders.
  */
  const periodMachineInitialized = useMemo(() => {
    return periodMachine.withContext({
      subscriptionId,
      selectedDay: null,
      nextDueDate: null,
      calculatedPeriods: [],
      getPeriods: subscriptionPeriods,
      newPeriod: changeSubscriptionPeriod,
      calculatePeriods,
      error: null,
    });
  }, []);

  const [state, send] = useMachine(periodMachineInitialized);

  const { selectedDay, nextDueDate, calculatedPeriods, error } = state.context;

  // Handles response from subscriptionPeriods call and maps to machine events
  useEffect(() => {
    if (getPeriodsData) {
      send('SUCCESS', {
        value: getPeriodsData?.subscriptionPeriods.periods,
      });
    }

    if (getPeriodsError) {
      send('ERROR', { value: t('period.errors.getPeriods') });
    }
  }, [getPeriodsError, getPeriodsData]);

  /**
   * Make option elements for every available new payment day of month
   * @param dueDate Date
   * @return Array of option elements
   */
  const makeOptions = (dueDate: Date | null) => {
    const availableDaysOfMonth = 28;
    const daysOptions: Array<React.ReactElement> = [];
    const today = new Date();

    const addOneDay = (date: Date) => {
      return addDays(date, 1);
    };

    let day = dueDate ? dueDate : today;

    for (let index = 0; index < availableDaysOfMonth; index = index + 1) {
      day = addOneDay(day);
      // Do not show option to pick same day of month as new payment day
      if (isSameDay(day, addMonths(dueDate ?? today, 1))) {
        break;
      }
      // Skip dates after after availableDaysOfMonth
      while (getDate(day) > availableDaysOfMonth) {
        day = addOneDay(day);
      }
      daysOptions.push(
        <option key={`option_${index + 1}`} value={day.toISOString()}>
          {`${formatDate(day, 'd')}. hvers mánaðar`}
        </option>,
      );
    }

    return daysOptions;
  };

  const daySelect = (daySelected: Date) => {
    send('selectedDay.UPDATE', { value: daySelected });
  };

  const confirmClick = () => {
    send({ type: 'CONFIRM' });
  };

  if (['success'].some(state.matches)) {
    send('RESET');
    setTimeout(() => {
      /*
        enqueueSnackbar causes "Warning: Cannot update during exixting state transition..." when called here.
        Wrapping enqueueSnackbar in 0ms setTimeout prevents the warning.
      */
      enqueueSnackbar(
        t('period.success', {
          newDay: selectedDay && formatDate(selectedDay, 'd.'),
        }),
        {
          variant: 'success',
        },
      );
    }, 0);
    if (needsRedirection()) {
      router.push(`/${ssn}/thjonusta/${subscriptionId}`);
    }
  }
  return (
    <>
      <h2>{t('period.title')}</h2>
      {['initialize'].some(state.matches) && <p>Sæki...</p>}
      {['editing', 'calculate', 'setNewPeriod'].some(state.matches) && (
        <>
          <p>
            {nextDueDate
              ? t('period.nextDueDate', {
                  nextDueDate: formatDate(nextDueDate, 'do MMMM'),
                })
              : t('period.noNextDueDate')}
          </p>
          <div>
            {/* Set label to one space as a temp fix for label being visible behind selected option. */}
            <FormDropdown
              name="dayOfMonthSelect"
              label={selectedDay ? ' ' : t('period.dropdownLabel')}
              onSelect={(selection: Date) => {
                daySelect(selection);
              }}
            >
              {makeOptions(nextDueDate)}
            </FormDropdown>
          </div>

          {selectedDay && calculatedPeriods && (
            <>
              <PaymentPlanDraft calculatedPeriods={calculatedPeriods} nextDueDate={nextDueDate} />
              <div className={s.buttonContainer}>
                <Button
                  onClick={confirmClick}
                  disabled={!selectedDay}
                  loading={['calculate', 'setNewPeriod'].some(state.matches)}
                >
                  {t('period.confirm')}
                </Button>
              </div>
            </>
          )}
        </>
      )}
      {['error'].some(state.matches) && (
        <>
          <EmptyMessage title={t('period.errors.title')} description={error} />
        </>
      )}
    </>
  );
};

export default inject('authentication')(ChangePayDate);
