import React, {useCallback, useEffect, useMemo, useState} from "react";
import {formatTime, Modify} from "../../Utils";

export function TimepickerInput(
	{
		children, className,
		value, onChange,
		// Properties to pass down.
		onKeyUp, onBlur,
		// Already set properties.
		type, placeholder,
		...props
	}: React.PropsWithChildren<Modify<React.InputHTMLAttributes<HTMLInputElement>, {
		value?: Date|null;

		onChange: (newDateTime: Date) => void;

		// Already set properties.
		type?: never;
		placeholder?: never;
	}>>): React.ReactElement
{
	// Time text state.
	const [timeText, setTimeText] = useState("");

	// Update time text when datetime value has changed.
	useEffect(() => {
		if (value && value instanceof Date && !isNaN(value.getTime()))
			setTimeText(formatTime(value));
	}, [value]);

	// Check if time is valid.
	const invalidTime = useMemo(() => !(value && value instanceof Date && !isNaN(value.getTime())) && timeText.length > 0, [value, timeText]);
	const timeValue = useMemo(() => invalidTime ? new Date() : (value ?? new Date()), [invalidTime, value]);

	/**
	 * Submit a new time from its raw text.
	 */
	const submitTime = useCallback((customTimeText?: string) => {
		// Get the current date text status.
		const timeTextMatch = (customTimeText ?? timeText).match(/^([0-2]?[0-9]):([0-5]?[0-9])$/);

		if (timeTextMatch)
		{
			// Parse hours.
			let rawHours = timeTextMatch[1];
			if (rawHours.length < 2) rawHours = "0" + rawHours;
			let hours = parseInt(rawHours);
			if (isNaN(hours)) hours = 0;

			// Parse minutes.
			let rawMinutes = timeTextMatch[2];
			if (rawMinutes.length < 2) rawMinutes = "0" + rawMinutes;
			let minutes = parseInt(rawMinutes);
			if (isNaN(minutes)) minutes = 0;

			//TODO
			// Parse seconds?

			// Try to build the structurally valid date with this time.
			//TODO
			const datetime = new Date(timeValue);
			datetime.setHours(hours, minutes);
			// Put the date back, if it changed to another day.
			datetime.setFullYear(timeValue.getFullYear(), timeValue.getMonth(), timeValue.getDate());

			// Set the structurally valid date.
			onChange?.(datetime);
		}
		else
			// No structurally valid date, removing it.
			onChange?.(null);
	}, [timeText, timeValue, onChange]);

	return (
		<label className={`timepicker${className ? ` ${className}` : ""}`}>
			{children}

			<input type={"text"} placeholder={"HH:MM"} value={timeText}
			       onChange={useCallback((event) => {
				       let timeText = event.currentTarget.value;

				       // Add first ':' if missing.
				       if (timeText.match(/^[0-9]{2}[^:]*$/))
					       timeText = timeText.substring(0, 2) + ":" + timeText.substring(2);

				       // Set the new time text.
				       setTimeText(timeText);

				       // If a full time has been entered, reading it.
				       const fullTimeMatch = timeText.match(/^([0-2][0-9]):([0-5][0-9])$/);
				       if (fullTimeMatch)
				       { // We have a structurally valid time, submitting it.
					       submitTime(timeText);
				       }
			       }, [submitTime])}
			       onKeyUp={useCallback((event) => {
				       if (event.key == "Enter")
					       // Submit time when enter is pressed.
					       submitTime();
				       return onKeyUp?.(event);
			       }, [submitTime, onKeyUp])}
			       onBlur={useCallback((event) => {
				       // Submit time when leaving form input.
				       submitTime();
				       return onBlur?.(event);
			       }, [submitTime, onBlur])}
			       {...props} />

			{ // The date is invalid, showing a subtext to say so.
				invalidTime && (
					<span className={"error subtext"}>Invalid time.</span>
				)
			}
		</label>
	);
}
