/** @jsxRuntime classic */
/** @jsx jsx */

import { Fragment, useMemo, memo, useCallback } from 'react';
import { jsx, useTheme } from '@emotion/react';
import dayjs from 'dayjs';
import {
  IEventsGroup,
  IAddNewSlotButtonsProps,
  IPlusButtonData,
} from '../../types';
import { AddNewSlotButtonsContainer } from '../styles';
import {
  calculateMarginFromMinutes,
  calculateMarginLeftFromMinutes,
  oneHourHeight,
  oneHourWidth,
} from '../../utils/timeUtils';
import { Plus } from '../../../components/icons_v2/all/Plus';
import { IColors } from '../../../types/theme';
import { useMiddlewareContext } from '../../hooks/useMiddlewareContext';

const defaultDuration = 60;
const defaultStep = 60;
const minimumMinutes = 10;

export const AddNewSlotButtons = memo(
  ({
    events,
    onClick = (data: any) => {},
    horizontal,
    parentId,
  }: IAddNewSlotButtonsProps) => {
    const { options, getTopAndHeight, day: daysCount } = useMiddlewareContext();
    const { date, addNewSlotButton = {} } = options || {};
    const { duration, step } = addNewSlotButton;
    const currentDate = dayjs.utc(date);
    const shift = horizontal ? 0 : 6;
    const hourSize = horizontal ? oneHourWidth : oneHourHeight;
    const minimumSpace = hourSize / 2;

    const getHeightByMinutes = (minutes: number) =>
      horizontal
        ? calculateMarginLeftFromMinutes(minutes)
        : calculateMarginFromMinutes(minutes);

    const buttonDuration = Math.max(
      minimumMinutes,
      duration || defaultDuration
    );
    const buttonStep = Math.max(minimumMinutes, step || defaultStep);
    const buttonDurationHeight = getHeightByMinutes(buttonDuration);
    const buttonStepHeight = getHeightByMinutes(buttonStep);

    const handleButtonClick = useCallback(
      (item: IPlusButtonData) => {
        let currentHour = (item.top - shift) / hourSize;
        let currentMinutes = Math.round(
          (currentHour - Math.floor(currentHour)) * 60
        );

        const startDate = dayjs
          .utc(currentDate)
          .hour(currentHour)
          .minute(currentMinutes)
          .toISOString();

        const startTime = dayjs
          .utc(currentDate)
          .hour(currentHour)
          .minute(currentMinutes)
          .format('HH:mm');

        currentHour = (item.bottom - shift) / hourSize;
        currentMinutes = Math.round(
          (currentHour - Math.floor(currentHour)) * 60
        );

        const endDate = dayjs
          .utc(currentDate)
          .hour(currentHour)
          .minute(currentMinutes)
          .toISOString();

        const endTime = dayjs
          .utc(currentDate)
          .hour(currentHour)
          .minute(currentMinutes)
          .format('HH:mm');

        if (onClick) {
          onClick({
            type: 'NEW_BOOKING',
            data: {
              startTime,
              endTime,
              startDate,
              endDate,
              parentId,
            },
          });
        }
      },
      [onClick, parentId, shift, hourSize, currentDate]
    );

    const list = useMemo(() => {
      // make a list of events/groups like IPlusButtonData objects
      const eventsItems = (JSON.parse(events) as IEventsGroup[])
        .map(({ item, ...rest }) => {
          if (!item) {
            const { top = 0, height = 0 } = rest;

            return {
              top,
              height,
              bottom: top + height,
              isEvent: true,
              isGroup: true,
            } as IPlusButtonData;
          }

          const { top, height } = getTopAndHeight(
            dayjs.utc(item.startDate).toDate().getTime(),
            dayjs.utc(item.endDate).toDate().getTime(),
            horizontal
          );

          return {
            top,
            height,
            bottom: top + height,
            isEvent: true,
          } as IPlusButtonData;
        })
        .filter(
          (item) =>
            item.top >= shift && item.bottom < 24 * hourSize * daysCount + shift
        )
        .sort((a, b) => a.top - b.top);

      if (eventsItems.length === 0) return [];

      let list = eventsItems.slice();

      // fill top empty space
      let topEmptySpace = eventsItems[0].top - shift;
      let emptyHoursCount = Math.floor(topEmptySpace / buttonStepHeight);
      let emptyTopSpaces = [] as IPlusButtonData[];

      for (let i = 0; i < emptyHoursCount; i++) {
        emptyTopSpaces.push({
          top: shift + i * buttonStepHeight,
          height: buttonDurationHeight,
          bottom: i * hourSize + buttonDurationHeight + shift,
        });
      }

      let last = emptyTopSpaces[emptyTopSpaces.length - 1];
      if (last) {
        last = {
          ...last,
          bottom: eventsItems[0].top,
          height: eventsItems[0].top - last.top,
        };
      }

      if (last) {
        list = [...list, ...emptyTopSpaces.slice(0, -1), { ...last }].sort(
          (a, b) => a.top - b.top
        );
      } else {
        if (eventsItems[0].top <= buttonDurationHeight + shift) {
          list = [
            {
              top: shift,
              height: eventsItems[0].top - shift,
              bottom: eventsItems[0].top,
            },
            ...list,
          ].sort((a, b) => a.top - b.top);
        }
      }
      // end filling top empty space

      // fill spaces between events
      for (let i = 0; i < eventsItems.length - 1; i++) {
        const current = eventsItems.slice()[i];
        const next = eventsItems.slice()[i + 1];

        const space = next.top - current.bottom;

        // check less than one empty place between 2 events
        if (
          space > getHeightByMinutes(minimumMinutes) &&
          space < buttonStepHeight
        ) {
          list.push({
            top: current.bottom,
            bottom: next.top,
            height: space,
          });
        } else if (space > getHeightByMinutes(minimumMinutes)) {
          const emptyHoursCount = Math.floor(space / buttonStepHeight);
          const currentHour = (current.bottom - shift) / hourSize;
          let currentMinutes = Math.round(
            (currentHour - Math.floor(currentHour)) * 60
          );
          while (currentMinutes % buttonStep !== 0) currentMinutes++;

          let emptyHours = [] as IPlusButtonData[];

          const shiftSpace =
            getHeightByMinutes(Math.floor(currentHour) * 60 + currentMinutes) +
            shift;

          for (let i = 0; i < emptyHoursCount; i++) {
            emptyHours.push({
              top: i * buttonStepHeight + shiftSpace,
              height: buttonDurationHeight,
              bottom: i * buttonStepHeight + shiftSpace + buttonDurationHeight,
            });
          }

          // Feeling space after last event/group and first button
          emptyHours[0] = {
            ...emptyHours[0],
            top: current.bottom,
            height: emptyHours[0].bottom - current.bottom,
          };
          // Feeling space before next event/group and last button
          emptyHours[emptyHours.length - 1] = {
            ...emptyHours[emptyHours.length - 1],
            bottom: next.top,
            height: next.top - emptyHours[emptyHours.length - 1].top,
          };

          list = list.concat(emptyHours).sort((a, b) => a.top - b.top);
        }
      }

      list.sort((a, b) => a.top - b.top);
      // end filling spaces between events

      // fill bottom space
      let bottomEmptySpace =
        24 * hourSize * daysCount +
        shift -
        eventsItems[eventsItems.length - 1].bottom;
      let currentHour =
        (eventsItems[eventsItems.length - 1].bottom - shift) / hourSize;
      if (Math.floor(currentHour) === 24 * daysCount) currentHour--;
      let currentMinutes = Math.round(
        (currentHour - Math.floor(currentHour)) * 60
      );
      while (currentMinutes % buttonStep !== 0) currentMinutes++;

      const shiftSpace =
        getHeightByMinutes(Math.floor(currentHour) * 60 + currentMinutes) +
        shift;

      emptyHoursCount = Math.floor(bottomEmptySpace / buttonStepHeight);
      let emptyBottomSpaces = [] as IPlusButtonData[];

      for (let i = 0; i < emptyHoursCount; i++) {
        emptyBottomSpaces.push({
          top: i * buttonStepHeight + shiftSpace,
          height: buttonDurationHeight,
          bottom: i * buttonStepHeight + shiftSpace + buttonDurationHeight,
        });
      }

      if (emptyHoursCount > 0 && emptyBottomSpaces[0]) {
        emptyBottomSpaces[0] = {
          bottom: emptyBottomSpaces[0].bottom,
          top: eventsItems[eventsItems.length - 1].bottom,
          height:
            emptyBottomSpaces[0].bottom -
            eventsItems[eventsItems.length - 1].bottom,
        };
        emptyBottomSpaces[emptyBottomSpaces.length - 1] = {
          ...emptyBottomSpaces[emptyBottomSpaces.length - 1],
          bottom: emptyBottomSpaces[emptyBottomSpaces.length - 1].bottom,
          height:
            daysCount * 24 * hourSize +
            shift -
            emptyBottomSpaces[emptyBottomSpaces.length - 1].top,
        };
      } else {
        const top = eventsItems[eventsItems.length - 1].bottom;
        const bottom = daysCount * hourSize * 24 + shift;
        const height = bottom - top;

        if (height >= minimumSpace) {
          emptyBottomSpaces.push({
            top,
            height,
            bottom,
          });
        }
      }

      list = list.concat(emptyBottomSpaces);

      return list
        .filter(
          (item) =>
            !item.isEvent &&
            item.top + item.height <= daysCount * 24 * hourSize + shift
        )
        .sort((a, b) => a.top - b.top);
    }, [events, daysCount]);

    const listOfButtons = useMemo(() => {
      if (list.length === 0) {
        let buttons = [] as any[];

        for (let day = 0; day < daysCount; day++) {
          const getTop = (i: number) =>
            i * buttonStepHeight + day * hourSize * 24 + shift;

          const count = Math.floor((24 * 60) / buttonStep);

          buttons = [
            ...buttons,
            ...Array(count)
              .fill(0)
              .map((_, i) => {
                const isLast = day + 1 === daysCount && i + 1 === count;

                return {
                  top: getTop(i),
                  bottom: getTop(i) + buttonDurationHeight,
                  height: isLast
                    ? Math.min(
                        buttonDurationHeight,
                        daysCount * 24 * hourSize + shift - getTop(i)
                      )
                    : buttonDurationHeight,
                } as IPlusButtonData;
              })
              .filter(
                (item) =>
                  item.top + item.height <= daysCount * 24 * hourSize + shift
              )
              .map((item) => (
                <AddNewSlotButton
                  key={item.top}
                  item={item}
                  horizontal={horizontal}
                  handleButtonClick={handleButtonClick}
                />
              )),
          ];
        }

        return buttons;
      }

      return list.map((item) => (
        <AddNewSlotButton
          key={item.top}
          item={item}
          horizontal={horizontal}
          handleButtonClick={handleButtonClick}
        />
      ));
    }, [list, handleButtonClick, daysCount]);

    return <Fragment>{listOfButtons}</Fragment>;
  }
);

function AddNewSlotButton({
  item,
  horizontal,
  handleButtonClick,
}: {
  item: IPlusButtonData;
  horizontal?: boolean;
  handleButtonClick: (item: IPlusButtonData) => void;
}) {
  const colors = useTheme() as IColors;
  const { options = {} } = useMiddlewareContext();
  const { addNewSlotButton = {} } = options;
  const { hoverDelay = '.3s' } = addNewSlotButton;

  const shift = horizontal ? 0 : 6;
  const hourSize = horizontal ? oneHourWidth : oneHourHeight;

  const currentHour = (item.top - shift) / hourSize;
  const currentMinutes = Math.round(
    (currentHour - Math.floor(currentHour)) * 60
  );

  const hoursLabel = dayjs
    .utc()
    .hour(currentHour)
    .minute(currentMinutes)
    .format('hh:mma');

  return (
    <div
      css={AddNewSlotButtonsContainer(
        colors,
        item.top,
        item.height,
        horizontal,
        hoverDelay
      )}
    >
      <div className="AddNewSlotButtonsLabel">{hoursLabel}</div>
      <div
        className="AddNewSlotButtons"
        onClick={() => handleButtonClick(item)}
      >
        <Plus
          width="17"
          height="17"
          viewBox="0 0 25 25"
          color={colors.tx_text_teriaty}
        />
      </div>
    </div>
  );
}
