import "./index.css";

import { getLocalizedString } from "@olenbetong/appframe-core";
import clsx from "clsx";
import { useEffect, useRef, useState } from "react";

function getUiCulture() {
	return af?.userSession?.culture ?? "default";
}

function Arrow({
	direction,
	...rest
}: { direction: "left" | "right" } & React.ButtonHTMLAttributes<HTMLButtonElement>) {
	return (
		<button
			className={clsx(
				"ObDateNavigator-arrow btn-reset",
				direction === "left" ? "ObDateNavigator-arrowLeft" : "ObDateNavigator-arrowRight",
			)}
			aria-label={direction === "left" ? getLocalizedString("Previous day") : getLocalizedString("Next day")}
			{...rest}
		>
			<span className="ObDateNavigator-icon">
				{direction === "left" ? (
					<svg aria-hidden="true" viewBox="0 0 320 512">
						<path
							fill="currentColor"
							d="M34.52 239.03L228.87 44.69c9.37-9.37 24.57-9.37 33.94 0l22.67 22.67c9.36 9.36 9.37 24.52.04 33.9L131.49 256l154.02 154.75c9.34 9.38 9.32 24.54-.04 33.9l-22.67 22.67c-9.37 9.37-24.57 9.37-33.94 0L34.52 272.97c-9.37-9.37-9.37-24.57 0-33.94z"
						/>
					</svg>
				) : (
					<svg aria-hidden="true" viewBox="0 0 320 512">
						<path
							fill="currentColor"
							d="M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z"
						/>
					</svg>
				)}
			</span>
		</button>
	);
}

const formatter = new Intl.DateTimeFormat(getUiCulture(), {
	weekday: "short",
	day: "numeric",
	month: "short",
	year: "numeric",
	timeZone: "utc",
});

const oneDay = 24 * 60 * 60 * 1000;

export type DateNavigatorProps = {
	date?: Date | string | number;
	defaultDate?: Date | string | number; // For default uncontrolled value
	disableTodayButton?: boolean;
	onChange?: (date: Date) => void;
	minDate?: Date | string | number;
	maxDate?: Date | string | number;
};

function sanitizeDate(date: Date | string | number): Date {
	if (typeof date === "string" || typeof date === "number") {
		let parsedDate = new Date(date);
		if (Number.isNaN(parsedDate.getTime())) {
			throw new Error(`Invalid date value: ${date}`);
		}
		return parsedDate;
	} else if (date instanceof Date) {
		if (Number.isNaN(date.getTime())) {
			throw new Error(`Invalid date object: ${date}`);
		}
		return new Date(date.getTime()); // Return a copy of the date
	} else {
		throw new Error(`Unsupported date type: ${typeof date}`);
	}
}

export function DateNavigator({
	date: dateProp,
	defaultDate,
	disableTodayButton,
	onChange,
	minDate: minDateProp,
	maxDate: maxDateProp,
}: DateNavigatorProps) {
	let [internalDate, setInternalDate] = useState(() => (defaultDate ? sanitizeDate(defaultDate) : new Date()));
	let date = dateProp ? sanitizeDate(dateProp) : internalDate;
	let dateUtc = Date.UTC(date.getFullYear(), date.getMonth(), date.getDate());
	let today = new Date();
	let todayUtc = Date.UTC(today.getFullYear(), today.getMonth(), today.getDate());
	let liveRef = useRef<HTMLDivElement>(null);

	let text = (() => {
		let base = formatter.format(new Date(dateUtc));
		if (todayUtc === dateUtc) return `${getLocalizedString("Today")}, ${base}`;
		if (todayUtc === dateUtc + oneDay) return `${getLocalizedString("Yesterday")}, ${base}`;
		if (todayUtc === dateUtc - oneDay) return `${getLocalizedString("Tomorrow")}, ${base}`;
		return base;
	})();

	let minDate = minDateProp && sanitizeDate(minDateProp);
	let maxDate = maxDateProp && sanitizeDate(maxDateProp);
	let disablePrev = !!minDate && dateUtc <= Date.UTC(minDate.getFullYear(), minDate.getMonth(), minDate.getDate());
	let disableNext = !!maxDate && dateUtc >= Date.UTC(maxDate.getFullYear(), maxDate.getMonth(), maxDate.getDate());

	function updateDate(newDate: Date) {
		if (minDate && newDate < minDate) {
			newDate = new Date(minDate); // Ensure we don't go below minDate
		} else if (maxDate && newDate > maxDate) {
			newDate = new Date(maxDate); // Ensure we don't go above maxDate
		}

		onChange?.(newDate);
		setInternalDate(newDate);
	}

	function jumpToToday() {
		updateDate(new Date());
	}

	useEffect(() => {
		if (liveRef.current) {
			liveRef.current.textContent = text;
		}
	}, [text]);

	function handleKeyDown(event: React.KeyboardEvent<HTMLDivElement>) {
		if (event.key !== "ArrowLeft" && event.key !== "ArrowRight") return;
		if (event.defaultPrevented) return;
		if (event.altKey || event.metaKey) return; // Ignore if alt or meta key is pressed

		if (disablePrev && event.key === "ArrowLeft") return;
		if (disableNext && event.key === "ArrowRight") return;

		if (event.ctrlKey && event.shiftKey) {
			// If both ctrl and shift are pressed, navigate by 1 year
			let newDate = new Date(dateUtc);
			if (event.key === "ArrowLeft") {
				newDate.setFullYear(newDate.getFullYear() - 1);
			} else if (event.key === "ArrowRight") {
				newDate.setFullYear(newDate.getFullYear() + 1);
			}
			updateDate(newDate);
			return;
		}

		if (event.ctrlKey) {
			// Navigate by month
			let newDate = new Date(dateUtc);
			if (event.key === "ArrowLeft") {
				newDate.setMonth(newDate.getMonth() - 1);
			} else if (event.key === "ArrowRight") {
				newDate.setMonth(newDate.getMonth() + 1);
			}
			updateDate(newDate);
			return;
		}

		let modifier = event.shiftKey ? 7 : 1;
		if (event.key === "ArrowLeft") {
			updateDate(new Date(dateUtc - oneDay * modifier));
		} else if (event.key === "ArrowRight") {
			updateDate(new Date(dateUtc + oneDay * modifier));
		}
	}

	return (
		// oxlint-disable-next-line jsx-a11y/no-noninteractive-tabindex -- I hate this component
		// oxlint-disable-next-line jsx-a11y/no-static-element-interactions -- legacy component
		// oxlint-disable-next-line jsx-a11y/role-supports-aria-props -- legacy component
		<div className="ObDateNavigator-root" onKeyDown={handleKeyDown} tabIndex={0} aria-label="Date navigator">
			<Arrow direction="left" onClick={() => updateDate(new Date(dateUtc - oneDay))} disabled={disablePrev} />
			<div className="ObDateNavigator-date">{text}</div>
			<Arrow direction="right" onClick={() => updateDate(new Date(dateUtc + oneDay))} disabled={disableNext} />
			{!disableTodayButton && (
				<button type="button" className="ObDateNavigator-today btn-reset" onClick={jumpToToday}>
					{getLocalizedString("Today")}
				</button>
			)}
			<div aria-live="polite" ref={liveRef} style={{ position: "absolute", left: "-9999px" }} />
		</div>
	);
}
