import * as React from "react";
import { useState } from "react";

import { blue } from "@mui/material/colors";
import { styled } from "@mui/material/styles";
import { AdapterLuxon } from "@mui/x-date-pickers/AdapterLuxon";
import { DateCalendar } from "@mui/x-date-pickers/DateCalendar";
import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider";
import {
  PickersDay as MuiPickersDay,
  PickersDayProps as MuiPickersDayProps,
} from "@mui/x-date-pickers/PickersDay";

import { DateTime } from "luxon";

import { useDashboardFilter } from "../../../../contexts/DashboardFilterContext";

export type Selecting = "start" | "end";

export interface PickerDayProps extends MuiPickersDayProps<DateTime> {
  isSelected: boolean;
  isHovered: boolean;
  isStart: boolean;
  isEnd: boolean;
  selecting: Selecting;
}

// TODO Clean up
// TODO Use theme colors instead of blue
export const PickersDay = styled(MuiPickersDay, {
  shouldForwardProp: (prop) =>
    prop !== "isSelected" && prop !== "isHovered" && prop !== "isStart" && prop !== "isEnd",
})<PickerDayProps>(({ theme, isSelected, isHovered, isStart, isEnd, day, selecting }) => ({
  borderRadius: 0,
  boxSizing: "border-box",
  border: "2px solid transparent",
  width: 40,
  height: 40,
  "&.MuiPickersDay-today": {
    borderColor: "transparent",
    "& .content": {
      border: `1px solid ${theme.palette.grey[500]}`,
      borderRadius: "50%",
      width: 40,
      height: 40,
    },
  },

  "& .content": {
    boxSizing: "border-box",
    display: "flex",
    alignItems: "center",
    justifyContent: "center",
    zIndex: 1,
    position: "absolute",
    borderRadius: "50%",
  },
  "&:hover .content": {
    border: `1px solid ${theme.palette.grey[800]}`,
    boxSizing: "border-box",
    width: 40,
    height: 40,
  },
  ...(isSelected && {
    backgroundColor: blue[50],
    "&:hover, &:focus": {
      backgroundColor: blue[50],
    },
    "&:hover .content": {
      backgroundColor: blue[100],
      width: 40,
      height: 40,
    },
  }),
  ...((isStart || isEnd) && {
    "& .content": {
      display: "flex",
      alignItems: "center",
      justifyContent: "center",
      color: theme.palette.primary.contrastText,
      zIndex: 0,
      position: "absolute",
      width: 40,
      height: 40,
      backgroundColor:
        (isStart && selecting === "end") || (isEnd && selecting === "start")
          ? blue[500]
          : blue[300],
      borderRadius: "50%",
    },
  }),
  ...(isStart && {
    backgroundColor: blue[50],
    color: theme.palette.primary.contrastText,
    borderTopLeftRadius: "50%",
    borderBottomLeftRadius: "50%",

    position: "relative",
    "&:hover, &:focus": {
      backgroundColor: blue[50],
    },
  }),
  ...(isEnd && {
    backgroundColor: blue[50],
    color: theme.palette.primary.contrastText,
    borderTopRightRadius: "50%",
    borderBottomRightRadius: "50%",

    "&:hover, &:focus": {
      backgroundColor: blue[50],
    },
  }),
  ...(isHovered && {
    boxSizing: "border-box",
    borderTop: `2px dashed ${theme.palette.grey[300]}`,
    borderBottom: `2px dashed ${theme.palette.grey[300]}`,
    "& *": {
      boxSizing: "border-box",
    },
  }),
  ...(day.weekday === 1 && {
    borderTopLeftRadius: "50%",
    borderBottomLeftRadius: "50%",
  }),
  ...(day.weekday === 7 && {
    borderTopRightRadius: "50%",
    borderBottomRightRadius: "50%",
  }),
})) as React.ComponentType<PickerDayProps>;

const isInRange = (day: DateTime, startDate: DateTime, endDate: DateTime) => {
  if (day.startOf("day") >= startDate.startOf("day") && day.endOf("day") <= endDate.endOf("day")) {
    return true;
  }
  return false;
};

const getHovered = (
  day: DateTime,
  hoveredDay: DateTime,
  startDate: DateTime,
  endDate: DateTime,
  selecting: "start" | "end",
) => {
  if (selecting === "start") {
    return (
      day.startOf("day") < startDate.startOf("day") && day.endOf("day") > hoveredDay?.startOf("day")
    );
  } else {
    return (
      day.startOf("day") > endDate.startOf("day") && day.endOf("day") < hoveredDay?.startOf("day")
    );
  }
};

export interface DayProps extends MuiPickersDayProps<DateTime> {
  selecting: "start" | "end";
  startDate: DateTime;
  endDate: DateTime;
  selectedDay: DateTime;
  hoveredDay: DateTime;
}
export const Day: React.FC<DayProps> = ({
  day,
  selectedDay,
  hoveredDay,
  startDate,
  endDate,
  selecting,
  ...props
}) => {
  const isStart = startDate?.hasSame(day, "day") ?? false;
  const isEnd = endDate?.hasSame(day, "day") ?? false;

  return (
    <PickersDay
      {...props}
      disableMargin
      day={day}
      isEnd={isEnd}
      isHovered={getHovered(day, hoveredDay, startDate, endDate, selecting)}
      isSelected={isInRange(day, startDate, endDate) && !isStart && !isEnd}
      isStart={isStart}
      selected={false}
      selecting={selecting}
    >
      <span className="content">{day.toFormat("d")}</span>
    </PickersDay>
  );
};

export const DateRangePicker: React.FC = () => {
  const { filter, setFilter } = useDashboardFilter();
  const [hoveredDay, setHoveredDay] = useState<DateTime | null>(null);
  const [value, setValue] = useState<DateTime | null>(DateTime.now());
  const [selecting, setSelecting] = useState<Selecting>("start");

  /**
   * Decide whether to set start or end date,
   * trying to match Mui X DateRange behavior
   */
  const handleChange = (date: DateTime) => {
    setValue(date);
    if (selecting === "start") {
      if (date >= filter.endDate) {
        setFilter({ ...filter, startDate: date, endDate: date });
      } else {
        setFilter({ ...filter, startDate: date });
      }
      setSelecting("end");
    } else {
      if (date < filter.startDate) {
        setFilter({ ...filter, startDate: date, endDate: date });
        setSelecting("end");
      } else {
        setFilter({ ...filter, endDate: date });
        setSelecting("start");
      }
    }
  };

  return (
    <LocalizationProvider dateAdapter={AdapterLuxon}>
      <DateCalendar
        showDaysOutsideCurrentMonth
        slotProps={{
          day: (ownerState) => ({
            selecting,
            endDate: filter.endDate,
            startDate: filter.startDate,
            selectedDay: value,
            hoveredDay,
            onPointerEnter: () => setHoveredDay(ownerState.day),
            onPointerLeave: () => setHoveredDay(null),
          }),
        }}
        slots={{ day: Day as typeof MuiPickersDay }}
        value={value}
        onChange={handleChange}
      />
    </LocalizationProvider>
  );
};

export default DateRangePicker;
