import React, { useLayoutEffect, useEffect, useState, useRef, useMemo, useCallback } from 'react';
import { getMonth, getYear, isAfter, isEqual, isWithinInterval, startOfDay, endOfDay, addYears, addDays, startOfToday } from 'date-fns';
import { useSelector, useDispatch } from 'react-redux';
import { QSMRootState } from '../../store/qsm-store';
import { nl } from 'date-fns/locale';
import Calendar from './calendar';
import useMediaQuery from '../../../shared/utils/use-media-query-util';
import { setSelectedFlexRange } from '../../store/qsm-slice';

interface DateRangePickerProps {
  fromDate?: Date;
  toDate?: Date;
  onSelectionChange?: (fromDate?: Date, toDate?: Date) => void;
  isSingleDate?: boolean;
  onRequestClose?: () => void;
}

const DateRangePicker: React.FC<DateRangePickerProps> = ({
  fromDate: initialFromDate,
  toDate: initialToDate,
  onSelectionChange,
  onRequestClose,
  isSingleDate = false
}) => {
  // ---------------------------------------------------------------------------
  // Local state
  // ---------------------------------------------------------------------------
  const [fromDate, setFromDate] = useState<Date | undefined>(initialFromDate);
  const [toDate, setToDate] = useState<Date | undefined>(initialToDate);
  const [waitingForToDate, setWaitingForToDate] = useState(false);
  const [flexibleRangeDatePicker, setFlexibleRangeDatePicker] = useState<{
    start: Date;
    end: Date;
  } | null>(null);
  const [focusMonth, setFocusMonth] = useState<{
    month: number;
    year: number;
  }>({
    month: getMonth(initialFromDate || new Date()),
    year: getYear(initialFromDate || new Date())
  });

  // ---------------------------------------------------------------------------
  // Redux & context
  // ---------------------------------------------------------------------------
  const dispatch = useDispatch();
  const { minDate, maxDate, dateFlexibility, selectedFlexRange } = useSelector((state: QSMRootState) => ({
    minDate: state.qsm.minDate ? new Date(state.qsm.minDate) : undefined,
    maxDate: state.qsm.maxDate ? new Date(state.qsm.maxDate) : undefined,
    dateFlexibility: state.qsm.dateFlexibility ?? [],
    selectedFlexRange: state.qsm.selectedFlexRange
  }));

  // ---------------------------------------------------------------------------
  // Constants & helpers
  // ---------------------------------------------------------------------------
  const today = startOfToday();

  const subtractOneMonth = (d: Date) => new Date(d.getFullYear(), d.getMonth() - 1, d.getDate());

  const safeMinDate = minDate ? subtractOneMonth(minDate) : today;
  const safeMaxDate = maxDate ? subtractOneMonth(maxDate) : addYears(today, 1);
  const isMobile = useMediaQuery('(max-width: 768px)');
  const rootRef = useRef<HTMLDivElement>(null);
  const [alignRight, setAlignRight] = useState(false);

  // Default flex option (exact date)
  const DEFAULT_FLEX_OPTION = useMemo(() => ({ name: 'exacte datum', before: 0, after: 0 }), []);

  const emitSelection = useCallback(
    (start: Date | undefined, end?: Date) => {
      onSelectionChange?.(start, isSingleDate ? undefined : end);
    },
    [onSelectionChange, isSingleDate]
  );
  // Merge default + server‑side flex options
  const combinedFlexibility = useMemo(() => {
    const withoutZeroZero = dateFlexibility.filter((opt) => opt.before !== 0 || opt.after !== 0);
    return [DEFAULT_FLEX_OPTION, ...withoutZeroZero];
  }, [dateFlexibility, DEFAULT_FLEX_OPTION]);

  const isSameFlexRange = useCallback(
    (a: { before: number; after: number } | undefined, b: { before: number; after: number }) => a?.before === b.before && a?.after === b.after,
    []
  );

  // ---------------------------------------------------------------------------
  // Flex option auto‑select (only once)
  // ---------------------------------------------------------------------------
  useEffect(() => {
    if (combinedFlexibility.length > 0 && !selectedFlexRange) {
      dispatch(setSelectedFlexRange(DEFAULT_FLEX_OPTION));
    }
  }, [combinedFlexibility, selectedFlexRange]);

  // ---------------------------------------------------------------------------
  // Keep internal state in sync with parent props
  // ---------------------------------------------------------------------------
  useEffect(() => {
    setFromDate(initialFromDate);
    setToDate(initialToDate);
  }, [initialFromDate, initialToDate]);

  // ---------------------------------------------------------------------------
  // Build flexible window (works for single & range)
  // ---------------------------------------------------------------------------
  useEffect(() => {
    if (!selectedFlexRange || !fromDate) {
      setFlexibleRangeDatePicker(null);
      return;
    }

    // Determine chronological first & last selected dates
    const firstSelected = toDate && isAfter(fromDate, toDate) ? toDate : fromDate;
    const lastSelected = toDate && isAfter(fromDate, toDate) ? fromDate : toDate ?? fromDate;

    // Expand with flex option
    const flexStart = addDays(firstSelected, -selectedFlexRange.before);
    const flexEnd = addDays(lastSelected, selectedFlexRange.after);

    setFlexibleRangeDatePicker({
      start: flexStart,
      end: flexEnd
    });
  }, [fromDate, toDate, selectedFlexRange]);

  // ---------------------------------------------------------------------------
  // Calendar navigation handlers
  // ---------------------------------------------------------------------------
  const handlePreviousClick = useCallback(() => {
    setFocusMonth((prev) => {
      const month = (prev.month - 1 + 12) % 12;
      const year = month > prev.month ? prev.year - 1 : prev.year;
      return { month, year };
    });
  }, []);

  const handleNextClick = useCallback(() => {
    setFocusMonth((prev) => {
      const month = (prev.month + 1) % 12;
      const year = month < prev.month ? prev.year + 1 : prev.year;
      return { month, year };
    });
  }, []);

  // ---------------------------------------------------------------------------
  // Date selection logic
  // ---------------------------------------------------------------------------
  const isOutOfBounds = useCallback(
    (date: Date) => {
      const start = safeMinDate ? startOfDay(safeMinDate) : undefined;
      const end = safeMaxDate ? startOfDay(safeMaxDate) : undefined;
      return (start && date < start) || (end && date > end);
    },
    [safeMinDate, safeMaxDate]
  );

  const handleDayClick = useCallback(
    (day: Date) => {
      if (isOutOfBounds(day)) return;

      if (isSingleDate) {
        setFromDate(day);
        setToDate(undefined);
        setWaitingForToDate(false);
        emitSelection(day);
        return;
      }

      if (waitingForToDate && fromDate && isAfter(day, fromDate)) {
        setToDate(day);
        setWaitingForToDate(false);
        emitSelection(fromDate, day);
      } else {
        setFromDate(day);
        setToDate(undefined);
        setWaitingForToDate(true);
        emitSelection(day, undefined);
      }
    },
    [fromDate, waitingForToDate, isSingleDate, isOutOfBounds]
  );

  const handleDayMouseOver = useCallback(
    (day: Date) => {
      if (!isSingleDate && waitingForToDate && fromDate && (isEqual(day, fromDate) || isAfter(day, fromDate))) {
        setToDate(day);
      }
    },
    [isSingleDate, waitingForToDate, fromDate]
  );

  // ---------------------------------------------------------------------------
  // Flex option click
  // ---------------------------------------------------------------------------
  const handleFlexSearchClick = useCallback(
    (index: number) => {
      const option = combinedFlexibility[index];
      if (!option) return;
      dispatch(
        setSelectedFlexRange({
          before: option.before,
          after: option.after
        })
      );
    },
    [combinedFlexibility]
  );

  // ---------------------------------------------------------------------------
  // Confirm / clear
  // ---------------------------------------------------------------------------
  const handleClear = useCallback(() => {
    setFromDate(undefined);
    setToDate(undefined);
    setWaitingForToDate(false);
    dispatch(setSelectedFlexRange(DEFAULT_FLEX_OPTION));
    onSelectionChange?.(undefined, undefined);
    onRequestClose?.();
  }, [onSelectionChange, onRequestClose]);

  const handleConfirm = useCallback(() => {
    if (!fromDate) {
      return;
    }
    onSelectionChange?.(fromDate, isSingleDate ? undefined : toDate);

    onRequestClose?.();
  }, [fromDate, toDate, onSelectionChange, isSingleDate]);

  // ---------------------------------------------------------------------------
  // Utility helpers for Calendar rendering
  // ---------------------------------------------------------------------------
  const checkIfDateIsInRange = useCallback(
    (date: Date) => {
      if (isSingleDate || !fromDate || !toDate) {
        return false;
      }

      return isWithinInterval(date, {
        start: startOfDay(fromDate),
        end: endOfDay(toDate)
      });
    },
    [isSingleDate, fromDate, toDate]
  );

  const checkIfDateIsInFlexibleRange = useCallback(
    (date: Date) => {
      if (!flexibleRangeDatePicker) {
        return false;
      }

      const [flexStart, flexEnd] =
        flexibleRangeDatePicker.start < flexibleRangeDatePicker.end
          ? [startOfDay(flexibleRangeDatePicker.start), endOfDay(flexibleRangeDatePicker.end)]
          : [startOfDay(flexibleRangeDatePicker.end), endOfDay(flexibleRangeDatePicker.start)];

      const isInFlexRange = isWithinInterval(date, {
        start: flexStart,
        end: flexEnd
      });

      if (isSingleDate) {
        return isInFlexRange;
      }

      const isInSelectedRange =
        fromDate &&
        toDate &&
        isWithinInterval(date, {
          start: startOfDay(fromDate),
          end: endOfDay(toDate)
        });

      return !isInSelectedRange && isInFlexRange;
    },
    [flexibleRangeDatePicker, isSingleDate, fromDate, toDate]
  );

  const isStart = useCallback((date: Date) => !!fromDate && isEqual(startOfDay(date), startOfDay(fromDate)), [fromDate]);
  const isEnd = useCallback((date: Date) => !isSingleDate && !!toDate && isEqual(startOfDay(date), startOfDay(toDate)), [isSingleDate, toDate]);

  const extraClassNamesFunction = useCallback(
    (date: Date) => {
      const classes: string[] = [];
      if (checkIfDateIsInFlexibleRange(date)) {
        classes.push('date-range-picker__flexible-range');
      }
      return classes;
    },
    [checkIfDateIsInFlexibleRange]
  );

  // ---------------------------------------------------------------------------
  // Layout alignment (desktop popover)
  // ---------------------------------------------------------------------------
  useLayoutEffect(() => {
    const el = rootRef.current;
    if (!el) {
      return;
    }

    setAlignRight(false);
    // Delay until browser paints so we have up‑to‑date bounding box
    const id = window.requestAnimationFrame(() => {
      const rect = el.getBoundingClientRect();
      if (rect.right > window.innerWidth) {
        setAlignRight(true);
      }
    });

    return () => window.cancelAnimationFrame(id);
  }, [focusMonth, isMobile]);

  // ---------------------------------------------------------------------------
  // Derived calendar months (memoised)
  // ---------------------------------------------------------------------------
  const firstMonth = focusMonth.month;
  const firstYear = focusMonth.year;
  const secondMonth = (firstMonth + 1) % 12;
  const secondYear = secondMonth === 0 && firstMonth === 11 ? firstYear + 1 : firstMonth === 11 ? firstYear + 1 : firstYear;

  // ---------------------------------------------------------------------------
  // Render
  // ---------------------------------------------------------------------------
  return (
    <div ref={rootRef} className={`date-range-picker${alignRight ? ' date-range-picker--align-right' : ''}`}>
      {/* -------------------------------------------------------------- Months */}
      {/* <div className="date-range-picker__months"> */}
      <div className="date-range-picker__from">
        <Calendar
          year={firstYear}
          month={firstMonth}
          onDayClick={handleDayClick}
          onDayMouseOver={handleDayMouseOver}
          onPreviousClick={handlePreviousClick}
          onNextClick={isMobile ? handleNextClick : undefined}
          hasNextButton={isMobile}
          hasPreviousButton
          isStartDate={isStart}
          isEndDate={isEnd}
          isInRange={checkIfDateIsInRange}
          minDate={safeMinDate}
          maxDate={safeMaxDate}
          locale={nl}
          extraClassNamesFunction={extraClassNamesFunction}
          isMobile={isMobile}
        />
      </div>

      {!isMobile && (
        <div className="date-range-picker__to">
          <Calendar
            year={secondYear}
            month={secondMonth}
            onDayClick={handleDayClick}
            onDayMouseOver={handleDayMouseOver}
            onNextClick={handleNextClick}
            hasPreviousButton={false}
            hasNextButton
            isStartDate={isStart}
            isEndDate={isEnd}
            isInRange={checkIfDateIsInRange}
            minDate={safeMinDate}
            maxDate={safeMaxDate}
            locale={nl}
            extraClassNamesFunction={extraClassNamesFunction}
            isMobile={false}
          />
        </div>
      )}

      {/* ------------------------------------------------------------ Flex tags */}
      {/* {combinedFlexibility.length > 1 && (
        <div className="date-range-picker-flex-days">
          {combinedFlexibility.map((option, index) => (
            <span
              key={index}
              className={`date-range-picker-flex-days__tag ${isSameFlexRange(selectedFlexRange, option) ? 'date-range-picker-flex-days__tag--active' : ''}`}
              onClick={() => handleFlexSearchClick(index)}>
              {option.name}
            </span>
          ))}
        </div>
      )} */}

      {/* ----------------------------------------------------------- Actions */}
      {/* <div className="date-range-picker__actions">
        <div
          className={`date-range-picker__actions-button${!fromDate || (!isSingleDate && !toDate) ? ' date-range-picker__actions-button--disabled' : ''}`}
          onClick={handleConfirm}>
          Opslaan
        </div>

        <div className="date-range-picker__actions-button date-range-picker__actions-button--clear" onClick={handleClear}>
          Wis
        </div>
      </div> */}
    </div>
  );
};

export default DateRangePicker;
