import dayjs from "dayjs";
import React, {
  useCallback,
  useContext,
  useEffect,
  useMemo,
  useState,
} from "react";

import { CALENDAR_SIZE, DATE_FORMAT } from "../../constants";
import DatepickerContext from "../../contexts/DatepickerContext";
import {
  formatDate,
  getDaysInMonth,
  getFirstDayInMonth,
  getFirstDaysInMonth,
  getLastDaysInMonth,
  getNumberOfDay,
  loadLanguageModule,
  nextMonth,
  previousMonth,
} from "../../helpers";
import {
  ChevronLeftIcon,
  ChevronRightIcon,
  DoubleChevronLeftIcon,
  DoubleChevronRightIcon,
  RoundedButton,
} from "../utils";

import Days from "./Days";
import Months from "./Months";
import Week from "./Week";
import Years from "./Years";

import { DateType } from "../../types";

interface Props {
  date: dayjs.Dayjs;
  minDate?: DateType | null;
  maxDate?: DateType | null;
  onClickPrevious: () => void;
  onClickNext: () => void;
  changeMonth: (month: number) => void;
  changeYear: (year: number) => void;
  mode?: "monthly" | "daily";
  onMark?: (day: number, date: Date) => any;
  style?: string
}

const Calendar: React.FC<Props> = ({
  date,
  minDate,
  maxDate,
  onClickPrevious,
  onClickNext,
  changeMonth,
  changeYear,
  onMark,
  mode = "daily",
  style = "prasi"
}) => {
  // Contexts
  const {
    period,
    changePeriod,
    changeDayHover,
    showFooter,
    changeDatepickerValue,
    hideDatepicker,
    asSingle,
    i18n,
    startWeekOn,
    input,
  } = useContext(DatepickerContext);
  loadLanguageModule(i18n);

  // States
  const [showMonths, setShowMonths] = useState(false);
  const [showYears, setShowYears] = useState(false);
  const [year, setYear] = useState(date.year());
  useEffect(() => {
    if (mode === "monthly") {
      setShowMonths(true);
      hideYears();
    }
  }, []);
  // Functions
  const previous = useCallback(() => {
    return getLastDaysInMonth(
      previousMonth(date),
      getNumberOfDay(getFirstDayInMonth(date).ddd, startWeekOn)
    );
  }, [date, startWeekOn]);

  const current = useCallback(() => {
    return getDaysInMonth(formatDate(date));
  }, [date]);

  const next = useCallback(() => {
    return getFirstDaysInMonth(
      previousMonth(date),
      CALENDAR_SIZE - (previous().length + current().length)
    );
  }, [current, date, previous]);

  const hideMonths = useCallback(() => {
    showMonths && setShowMonths(false);
  }, [showMonths]);

  const hideYears = useCallback(() => {
    showYears && setShowYears(false);
  }, [showYears]);

  const clickMonth = useCallback(
    (month: number) => {
      setTimeout(() => {
        changeMonth(month);
        if (mode === "daily") {
          setShowMonths(!showMonths);
        } else {
          hideDatepicker();
          clickDay(1, month, date.year());
        }
      }, 250);
    },
    [changeMonth, showMonths]
  );

  const clickYear = useCallback(
    (year: number) => {
      setTimeout(() => {
        changeYear(year);
        setShowYears(!showYears);
        if (mode === "monthly") {
          setShowMonths(true);
          clickDay(1, date.month() + 1, year);
        }
      }, 250);
    },
    [changeYear, showYears]
  );

  const clickDay = useCallback(
    (day: number, month = date.month() + 1, year = date.year()) => {
      const fullDay = `${year}-${month}-${day}`;
      let newStart;
      let newEnd = null;
      function chosePeriod(start: string, end: string) {
        const ipt = input?.current;
        changeDatepickerValue(
          {
            startDate: dayjs(start).format(DATE_FORMAT),
            endDate: dayjs(end).format(DATE_FORMAT),
          },
          ipt
        );
        hideDatepicker();
      }

      if (period.start && period.end) {
        if (changeDayHover) {
          changeDayHover(null);
        }
        changePeriod({
          start: null,
          end: null,
        });
      }

      if ((!period.start && !period.end) || (period.start && period.end)) {
        if (!period.start && !period.end) {
          changeDayHover(fullDay);
        }
        newStart = fullDay;
        if (asSingle) {
          newEnd = fullDay;
          chosePeriod(fullDay, fullDay);
        }
      } else {
        if (period.start && !period.end) {
          // start not null
          // end null
          const condition =
            dayjs(fullDay).isSame(dayjs(period.start)) ||
            dayjs(fullDay).isAfter(dayjs(period.start));
          newStart = condition ? period.start : fullDay;
          newEnd = condition ? fullDay : period.start;
        } else {
          // Start null
          // End not null
          const condition =
            dayjs(fullDay).isSame(dayjs(period.end)) ||
            dayjs(fullDay).isBefore(dayjs(period.end));
          newStart = condition ? fullDay : period.start;
          newEnd = condition ? period.end : fullDay;
        }

        if (!showFooter) {
          if (newStart && newEnd) {
            chosePeriod(newStart, newEnd);
          }
        }
      }

      if (!(newEnd && newStart) || showFooter) {
        changePeriod({
          start: newStart,
          end: newEnd,
        });
      }
    },
    [
      asSingle,
      changeDatepickerValue,
      changeDayHover,
      changePeriod,
      date,
      hideDatepicker,
      period.end,
      period.start,
      showFooter,
      input,
    ]
  );

  const clickPreviousDays = useCallback(
    (day: number) => {
      const newDate = previousMonth(date);
      clickDay(day, newDate.month() + 1, newDate.year());
      onClickPrevious();
    },
    [clickDay, date, onClickPrevious]
  );

  const clickNextDays = useCallback(
    (day: number) => {
      const newDate = nextMonth(date);
      clickDay(day, newDate.month() + 1, newDate.year());
      onClickNext();
    },
    [clickDay, date, onClickNext]
  );

  // UseEffects & UseLayoutEffect
  useEffect(() => {
    setYear(date.year());
  }, [date]);

  // Variables
  const calendarData = useMemo(() => {
    return {
      date: date,
      days: {
        previous: previous(),
        current: current(),
        next: next(),
      },
    };
  }, [current, date, next, previous]);
  const minYear = React.useMemo(
    () => (minDate && dayjs(minDate).isValid() ? dayjs(minDate).year() : null),
    [minDate]
  );
  const maxYear = React.useMemo(
    () => (maxDate && dayjs(maxDate).isValid() ? dayjs(maxDate).year() : null),
    [maxDate]
  );

  return (
    <div className="c-w-full md:c-w-[296px] md:c-min-w-[296px] calender">
      <div
        className={cx(
          "c-flex c-items-stretch c-space-x-1.5 c-px-2 c-py-1.5",
          css`
            border-bottom: 1px solid #d1d5db;
          `
        )}
      >
        {!showMonths && !showYears && (
          <div className="c-flex-none">
            <RoundedButton roundedFull={true} onClick={onClickPrevious}>
              <ChevronLeftIcon className="c-h-5 c-w-5" />
            </RoundedButton>
          </div>
        )}

        {showYears && (
          <div className="c-flex-none">
            <RoundedButton
              roundedFull={true}
              onClick={() => {
                setYear(year - 12);
              }}
            >
              <DoubleChevronLeftIcon className="c-h-5 c-w-5" />
            </RoundedButton>
          </div>
        )}

        <div className=" c-flex c-flex-1 c-items-stretch c-space-x-1.5">
          <div className="c-w-1/2 c-flex c-items-stretch">
            <RoundedButton
              onClick={() => {
                setShowMonths(!showMonths);
                hideYears();
              }}
            >
              <>{calendarData.date.locale(i18n).format(style === "google" ? "MMMM" :"MMM")}</>
            </RoundedButton>
          </div>

          <div className="c-w-1/2 c-flex c-items-stretch">
            <RoundedButton
              onClick={() => {
                setShowYears(!showYears);
                hideMonths();
              }}
            >
              <div className="c-py-2">{calendarData.date.year()}</div>
            </RoundedButton>
          </div>
        </div>

        {showYears && (
          <div className="c-flex-none">
            <RoundedButton
              roundedFull={true}
              onClick={() => {
                setYear(year + 12);
              }}
            >
              <DoubleChevronRightIcon className="c-h-5 c-w-5" />
            </RoundedButton>
          </div>
        )}

        {!showMonths && !showYears && (
          <div className="c-flex-none">
            <RoundedButton roundedFull={true} onClick={onClickNext}>
              <ChevronRightIcon className="c-h-5 c-w-5" />
            </RoundedButton>
          </div>
        )}
      </div>
      <div className={cx("c-mt-0.5 c-min-h-[285px] calender-body")}>
        {showMonths && (
          <Months
            currentMonth={calendarData.date.month() + 1}
            clickMonth={clickMonth}
            style={style}
          />
        )}

        {showYears && (
          <Years
            year={year}
            minYear={minYear}
            maxYear={maxYear}
            currentYear={calendarData.date.year()}
            clickYear={clickYear}
          />
        )}

        {!showMonths && !showYears && (
          <>
            <Week style={style}/>

            <Days
              calendarData={calendarData}
              onClickPreviousDays={clickPreviousDays}
              onClickDay={clickDay}
              onClickNextDays={clickNextDays}
              style={style}
              onIcon={(day, date) => {
                if(typeof onMark === "function"){
                  return onMark(day, date)
                }
                return <></>
                if (new Date().getDate() === day)
                  return (
                    <div className="c-absolute c-inset-y-0 c-left-0 -c-translate-y-1/2 -c-translate-x-1/2">
                      <div className="c-w-full c-h-full c-flex c-flex-row c-items-center c-justif-center c-px-0.5">
                       !
                      </div>
                    </div>
                  );
                return <></>
              }}
            />
          </>
        )}
      </div>
    </div>
  );
};

export default Calendar;
