import React from 'react';

// date-fns
import { addWeeks, eachDayOfInterval, endOfMonth, endOfWeek, format, getMonth, getYear, isSameMonth, startOfDay, startOfMonth, startOfWeek } from 'date-fns';
import { Locale } from 'date-fns';
import { enUS } from 'date-fns/locale';
import { nl } from 'date-fns/locale';
import { fr } from 'date-fns/locale';

// redux
import { useSelector } from 'react-redux';
import { QSMRootState } from '../../store/qsm-store';

// internal component
import CalendarDay from './calendar-day';

// Props interface
interface CalendarProps {
  year?: number;
  month?: number;
  hasPreviousButton?: boolean;
  hasNextButton?: boolean;
  hasFixedHeight?: boolean;
  weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
  isStartDate?: (date: Date) => boolean;
  isEndDate?: (date: Date) => boolean;
  isInRange?: (date: Date) => boolean;
  onDayClick?: (date: Date) => void;
  onDayMouseOver?: (date: Date) => void;
  onNextClick?: (year: number, month: number) => void;
  onPreviousClick?: (year: number, month: number) => void;
  locale?: Locale;
  extraClassNamesFunction?: (date: Date) => string[];
  isMobile?: boolean;
  minDate?: Date;
  maxDate?: Date;
}

const Calendar: React.FC<CalendarProps> = ({
  year = getYear(new Date()),
  month = getMonth(new Date()),
  hasPreviousButton = true,
  hasNextButton = true,
  hasFixedHeight = true,
  weekStartsOn = 1,
  isStartDate,
  isEndDate,
  isInRange,
  onDayClick,
  onDayMouseOver,
  onNextClick,
  onPreviousClick,
  extraClassNamesFunction,
  isMobile = false,
  minDate,
  maxDate
}) => {
  const language = useSelector((state: QSMRootState) => state.qsm.language);
  const languageCode = language.split('-')[0];

  const localeMap: Record<string, Locale> = {
    en: enUS,
    nl: nl,
    fr: fr
  };
  const currentLocale = localeMap[languageCode] || enUS;

  // Date range setup
  const focusDate = new Date(year, month);
  const firstDay = startOfWeek(startOfMonth(focusDate), {
    weekStartsOn
  });
  const lastDay = hasFixedHeight ? endOfWeek(addWeeks(firstDay, 5), { weekStartsOn }) : endOfWeek(endOfMonth(focusDate), { weekStartsOn });

  const calendarDays = eachDayOfInterval({
    start: firstDay,
    end: lastDay
  });

  // Event handlers
  const handleDayClick = (day: Date) => onDayClick?.(day);
  const handleDayMouseOver = (day: Date) => onDayMouseOver?.(day);

  const handlePreviousClick = () => {
    const prevMonth = (month - 1 + 12) % 12;
    const prevYear = prevMonth > month ? year - 1 : year;
    onPreviousClick?.(prevYear, prevMonth);
  };

  const handleNextClick = () => {
    const baseMonth = isMobile ? month + 2 : month + 1;
    const nextMonth = baseMonth % 12;
    const nextYear = nextMonth < month ? year + 1 : year;
    onNextClick?.(nextYear, nextMonth);
  };

  const isBeforeMin = (date: Date) => (minDate ? startOfDay(date) < startOfDay(minDate) : false);
  const isAfterMax = (date: Date) => (maxDate ? startOfDay(date) > startOfDay(maxDate) : false);
  const isOutOfBounds = (date: Date) => isBeforeMin(date) || isAfterMax(date);

  // Build weeks for rendering
  const weekRows = [];
  for (let i = 0; i < calendarDays.length; i += 7) {
    weekRows.push(calendarDays.slice(i, i + 7));
  }

  return (
    <div className="calendar">
      {/* Header */}
      <div className="calendar__header">
        <div className="calendar__pager">
          {hasPreviousButton && (
            <svg
              onClick={handlePreviousClick}
              className="calendar__pager-icon"
              width={16}
              height={10.667}
              id="datepicker-arrow-left-icon"
              viewBox="0 0 16 10.667">
              <path
                id="Path_60"
                data-name="Path 60"
                d="M202.667-661.333l-1.417-1.417,2.917-2.917H192v-2h12.167l-2.917-2.917L202.667-672,208-666.667Z"
                transform="translate(208 -661.333) rotate(180)"
                fill="#abd5d9"
              />
            </svg>
          )}

          <div className="calendar__current-month">
            {format(focusDate, 'MMMM yyyy', {
              locale: currentLocale
            }).replace(/^(.)(.*)$/, (_, a, b) => `${a.toUpperCase()}${b}`)}
          </div>

          {hasNextButton && (
            <svg
              onClick={handleNextClick}
              className="calendar__pager-icon calendar__pager-icon--right"
              width={16}
              height={10.667}
              id="datepicker-arrow-left-icon"
              viewBox="0 0 16 10.667">
              <path
                id="Path_60"
                data-name="Path 60"
                d="M202.667-661.333l-1.417-1.417,2.917-2.917H192v-2h12.167l-2.917-2.917L202.667-672,208-666.667Z"
                transform="translate(208 -661.333) rotate(180)"
                fill="#abd5d9"
              />
            </svg>
          )}
        </div>

        {/* Week day labels */}
        <div className="calendar__day-labels">
          {[0, 1, 2, 3, 4, 5, 6].map((i) => (
            <div className="calendar__day-label" key={`label-${i}`}>
              {format(calendarDays[i], 'eee', {
                locale: currentLocale
              })
                .slice(0, 2)
                .replace(/^(.)(.)$/, (_, a, b) => `${a.toUpperCase()}${b.toLowerCase()}`)}
            </div>
          ))}
        </div>
      </div>

      {/* Calendar grid */}
      <div className="calendar__body">
        {weekRows.map((week, index) => (
          <div className="calendar__week" key={`week-${index}`}>
            {week.map((day) => {
              const isDisabled = isOutOfBounds(day);

              const isSelected = isStartDate?.(day) || isEndDate?.(day);
              const inRange = isInRange?.(day) ?? false;
              const isOutsideMonth = !isSameMonth(day, focusDate);

              let extraClassNames: string[] = [];
              if (inRange) extraClassNames.push('calendar__day--range');
              if (isSelected) extraClassNames.push('calendar__day--selected');

              if (extraClassNamesFunction) {
                const additional = extraClassNamesFunction(day);
                if (Array.isArray(additional)) {
                  extraClassNames = extraClassNames.concat(additional);
                }
              }

              return (
                <CalendarDay
                  key={day.toISOString()}
                  day={day}
                  isSelected={isSelected || false}
                  isDisabled={isDisabled}
                  isOutsideMonth={isOutsideMonth}
                  extraClassNames={extraClassNames}
                  onClick={handleDayClick}
                  onMouseOver={handleDayMouseOver}
                />
              );
            })}
          </div>
        ))}
      </div>
    </div>
  );
};

export default Calendar;
