import {
  MonthYearPickerDialogueAndroid,
  MonthYearPickerViewIOS,
} from '@hero-design/react-native-month-year-picker';
import format from 'date-fns/fp/format';
import React, { useState } from 'react';
import { Platform, TouchableOpacity } from 'react-native';
import { useTheme } from '../../theme';
import { noop } from '../../utils/functions';
import Box from '../Box';
import ContentNavigator from '../ContentNavigator';
import Icon from '../Icon';
import Typography from '../Typography';
import CalendarRowItem from './CalendarRowItem';
import {
  StyledCalendarDayNameCell,
  StyledCalendarHeader,
  StyledCalendarRow,
  StyledCalendarRowItem,
  StyledContainer,
  StyledDisabledCalendarRowItem,
} from './StyledCalendar';
import {
  getCalendarButtonState,
  getCalendarDate,
  isDateInRange,
  isEqDate,
  setStartOrEndDate,
  shouldUseMonthPicker,
} from './helpers';
import SelectedDate from './CalendarRangeConnector';
import type { CalendarProps } from './types';
import { DAYS_OF_WEEK } from './constants';
import { useCalendarLayout } from './shared/hooks/useCalendarLayout';

export type CalendarDateRange = {
  startDate?: Date;
  endDate?: Date;
};

const CalendarRange = ({
  value,
  visibleDate,
  onChange,
  onPreviousPress = noop,
  onNextPress = noop,
  onTitlePress = noop,
  minDate,
  maxDate,
  markedDates = [],
  testID,
  onMonthChange = noop,
  onToggleMonthPicker = noop,
  monthPickerConfirmLabel,
  monthPickerCancelLabel,
}: Omit<CalendarProps, 'value' | 'onChange'> & {
  value?: CalendarDateRange;
  onChange?: (date: CalendarDateRange) => void;
}) => {
  const theme = useTheme();

  const [monthPickerVisible, setMonthPickerVisible] = useState(false);

  const { onLayout, contentHeight, contentWidth, calendarItemWidth } =
    useCalendarLayout();

  const {
    parsedMaskedDate,
    daysOfPreviousMonth,
    daysOfCurrentMonth,
    daysOfNextMonth,
  } = getCalendarDate({
    visibleDate,
    markedDates,
    minDate,
    maxDate,
    onMonthChange,
  });
  const { disablePrevButton, disableNextButton } = getCalendarButtonState({
    visibleDate,
    minDate,
    maxDate,
  });
  const shouldShowMonthPicker = shouldUseMonthPicker(onMonthChange);

  const now = new Date();

  const onDateChange = (date: Date) => {
    const newDateRange = setStartOrEndDate({
      date,
      startDate: value?.startDate,
      endDate: value?.endDate,
    });

    onChange?.(newDateRange);
  };

  const renderDateCell = ({
    date,
    isCurrentMonth,
  }: {
    date?: Date;
    isCurrentMonth: boolean;
  }) => {
    if (!date) {
      return (
        <StyledDisabledCalendarRowItem
          themeItemWidth={calendarItemWidth}
          testID="calendar-disabled-cell"
        />
      );
    }

    if (isEqDate(value?.startDate, date) || isEqDate(value?.endDate, date)) {
      return (
        <SelectedDate
          isStart={isEqDate(date, value?.startDate)}
          showConnector={
            !!value?.startDate &&
            !!value?.endDate &&
            !isEqDate(value.startDate, value.endDate)
          }
          key={date.toDateString()}
          date={date}
          onPress={() => onDateChange(date)}
          marked={parsedMaskedDate[date.toDateString()]}
          itemWidth={calendarItemWidth}
        />
      );
    }

    return (
      <CalendarRowItem
        itemWidth={calendarItemWidth}
        key={date.toDateString()}
        date={date}
        isCurrent={isEqDate(now, date)}
        isSelected={
          isEqDate(value?.startDate, date) || isEqDate(value?.endDate, date)
        }
        isHighlighted={isDateInRange({ date, range: value })}
        onPress={() => onDateChange(date)}
        marked={parsedMaskedDate[date.toDateString()]}
        textIntent={isCurrentMonth ? undefined : 'subdued'}
      />
    );
  };

  return (
    <StyledContainer testID={testID}>
      <StyledCalendarHeader>
        <ContentNavigator
          value={
            !shouldShowMonthPicker ? (
              format('MMMM yyyy', visibleDate)
            ) : (
              <TouchableOpacity
                testID="calendar-month-picker"
                onPress={() => {
                  onToggleMonthPicker?.(!monthPickerVisible);
                  setMonthPickerVisible(!monthPickerVisible);
                }}
              >
                <Box
                  flexDirection="row"
                  justifyContent="center"
                  alignItems="center"
                >
                  <Typography.Title
                    level="h5"
                    style={{
                      textAlign: 'center',
                      marginRight:
                        theme.__hd__.calendar.space.headerMarginRight,
                    }}
                  >
                    {format('MMMM yyyy', visibleDate)}
                  </Typography.Title>

                  <Icon
                    icon={monthPickerVisible ? 'arrow-up' : 'arrow-down'}
                    size="small"
                  />
                </Box>
              </TouchableOpacity>
            )
          }
          onPreviousPress={onPreviousPress}
          onNextPress={onNextPress}
          onPress={shouldShowMonthPicker ? undefined : onTitlePress}
          previousDisabled={disablePrevButton}
          nextDisabled={disableNextButton}
          fontSize="large"
        />
      </StyledCalendarHeader>
      {Platform.OS === 'ios' && monthPickerVisible ? (
        <Box style={{ overflow: 'hidden' }}>
          <MonthYearPickerViewIOS
            value={value?.startDate}
            minimumDate={minDate}
            maximumDate={maxDate}
            onChange={onMonthChange}
            style={{
              height:
                contentHeight +
                theme.__hd__.calendar.space.iosPickerMarginVertical * 2,
              marginVertical:
                -theme.__hd__.calendar.space.iosPickerMarginVertical,
              width: contentWidth,
            }}
          />
        </Box>
      ) : (
        <Box onLayout={onLayout}>
          <StyledCalendarRow>
            {DAYS_OF_WEEK.map((day) => (
              <StyledCalendarRowItem key={day}>
                <StyledCalendarDayNameCell themeItemWidth={calendarItemWidth}>
                  <Typography.Body variant="small">{day}</Typography.Body>
                </StyledCalendarDayNameCell>
              </StyledCalendarRowItem>
            ))}
          </StyledCalendarRow>
          <StyledCalendarRow>
            {daysOfPreviousMonth.map((date) =>
              renderDateCell({ date, isCurrentMonth: false })
            )}
            {daysOfCurrentMonth.map((date) =>
              renderDateCell({ date, isCurrentMonth: true })
            )}
            {daysOfNextMonth.map((date) =>
              renderDateCell({ date, isCurrentMonth: false })
            )}
          </StyledCalendarRow>

          {Platform.OS === 'android' && monthPickerVisible && (
            <MonthYearPickerDialogueAndroid
              themeVariant={theme.themeMode === 'dark' ? 'dark' : 'light'}
              doneButtonLabel={monthPickerConfirmLabel}
              cancelButtonLabel={monthPickerCancelLabel}
              value={value?.startDate}
              minimumDate={minDate}
              maximumDate={maxDate}
              onChange={(action, date) => {
                setMonthPickerVisible(false);
                onToggleMonthPicker?.(false);

                if (action === 'dateSetAction' && !!date) {
                  onMonthChange(date);
                }
              }}
            />
          )}
        </Box>
      )}
    </StyledContainer>
  );
};

export default CalendarRange;
