import { useState, useEffect, useRef } from 'react';
import dayjs from 'dayjs';
import isToday from 'dayjs/plugin/isToday';
import localeData from 'dayjs/plugin/localeData';
import updateLocale from 'dayjs/plugin/updateLocale';

import { Button } from 'components';
import { useSettingsStore } from 'stores';

import {
	Wrapper,
	StyledLabel,
	StyledSubLabel,
	CalendarHeader,
	CalendarDays,
	CalendarBody,
	Date,
	Day,
} from './calendarFieldStyle';

dayjs.extend(isToday);
dayjs.extend(localeData);
dayjs.extend(updateLocale);

const calculateMonth = (month, year, weekStart) => {
	const date = dayjs().month(month).year(year);
	const daysInMonth = date.daysInMonth();
	const daysBefore = date.date(1).day() - weekStart;
	const daysAfter = 6 - date.date(daysInMonth).day() + weekStart;

	let dates = [];
	let curDate = date.date(1).subtract(daysBefore, 'day');
	let y = 0;
	let x = 0;
	for (let i = 0; i < daysBefore + daysInMonth + daysAfter; i++) {
		if (x === 0) dates[y] = [];
		dates[y][x] = curDate.clone();
		curDate = curDate.add(1, 'day');
		x++;
		if (x > 6) {
			x = 0;
			y++;
		}
	}

	return dates;
};

const CalendarField = ({
	label,
	subLabel,
	id,
	register,
	...props
}) => {
  const weekStart = useSettingsStore(state => state.weekStart);

	const [dates, setDates] = useState(calculateMonth(dayjs().month(), dayjs().year(), weekStart));
	const [month, setMonth] = useState(dayjs().month());
	const [year, setYear] = useState(dayjs().year());

	const [selectedDates, setSelectedDates] = useState([]);
	const [selectingDates, _setSelectingDates] = useState([]);
	const staticSelectingDates = useRef([]);
	const setSelectingDates = newDates => {
		staticSelectingDates.current = newDates;
		_setSelectingDates(newDates);
	};

	const startPos = useRef({});
	const staticMode = useRef(null);
	const [mode, _setMode] = useState(staticMode.current);
	const setMode = newMode => {
		staticMode.current = newMode;
		_setMode(newMode);
	};

	useEffect(() => {
    if (weekStart !== dayjs.Ls.en.weekStart) {
      dayjs.updateLocale('en', {
        weekStart: weekStart,
        weekdaysShort: weekStart ? 'Mon_Tue_Wed_Thu_Fri_Sat_Sun'.split('_') : 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'),
      });
    }
		setDates(calculateMonth(month, year, weekStart));
	}, [weekStart, month, year]);

	return (
		<Wrapper>
			{label && <StyledLabel htmlFor={id}>{label}</StyledLabel>}
			{subLabel && <StyledSubLabel htmlFor={id}>{subLabel}</StyledSubLabel>}
			<input
				id={id}
				type="hidden"
				ref={register}
				value={JSON.stringify(selectedDates)}
				{...props}
			/>

			<CalendarHeader>
				<Button
					buttonHeight="30px"
					buttonWidth="30px"
					title="Previous month"
					type="button"
					onClick={() => {
						if (month-1 < 0) {
							setYear(year-1);
							setMonth(11);
						} else {
							setMonth(month-1);
						}
					}}
				>&lt;</Button>
				<span>{dayjs.months()[month]} {year}</span>
				<Button
					buttonHeight="30px"
					buttonWidth="30px"
					title="Next month"
					type="button"
					onClick={() => {
						if (month+1 > 11) {
							setYear(year+1);
							setMonth(0);
						} else {
							setMonth(month+1);
						}
					}}
				>&gt;</Button>
			</CalendarHeader>

			<CalendarDays>
				{dayjs.weekdaysShort().map((name, i) =>
					<Day key={i}>{name}</Day>
				)}
			</CalendarDays>
			<CalendarBody>
				{dates.length > 0 && dates.map((dateRow, y) =>
					dateRow.map((date, x) =>
						<Date
							key={y+x}
							otherMonth={date.month() !== month}
							isToday={date.isToday()}
							title={`${date.date()} ${dayjs.months()[date.month()]}${date.isToday() ? ' (today)' : ''}`}
							selected={selectedDates.includes(date.format('DDMMYYYY'))}
							selecting={selectingDates.includes(date)}
							mode={mode}
							onPointerDown={(e) => {
								startPos.current = {x, y};
								setMode(selectedDates.includes(date.format('DDMMYYYY')) ? 'remove' : 'add');
								setSelectingDates([date]);
								e.currentTarget.releasePointerCapture(e.pointerId);

								document.addEventListener('pointerup', () => {
									if (staticMode.current === 'add') {
										setSelectedDates([...selectedDates, ...staticSelectingDates.current.map(d => d.format('DDMMYYYY'))]);
									} else if (staticMode.current === 'remove') {
										const toRemove = staticSelectingDates.current.map(d => d.format('DDMMYYYY'));
										setSelectedDates(selectedDates.filter(d => !toRemove.includes(d)));
									}
									setMode(null);
								}, { once: true });
							}}
							onPointerEnter={() => {
								if (staticMode.current) {
									let found = [];
									for (let cy = Math.min(startPos.current.y, y); cy < Math.max(startPos.current.y, y)+1; cy++) {
										for (let cx = Math.min(startPos.current.x, x); cx < Math.max(startPos.current.x, x)+1; cx++) {
											found.push({y: cy, x: cx});
										}
									}
									setSelectingDates(found.map(d => dates[d.y][d.x]));
								}
							}}
						>{date.date()}</Date>
					)
				)}
			</CalendarBody>
		</Wrapper>
	);
};

export default CalendarField;
