"use client";

import React from "react";

import { buildDatePresets } from "@/lib/frontend/search/datePresets";
import { getSearchDateRangeValidation } from "@/lib/frontend/search/searchDateValidation";

import {
	formatIsoDateDe,
	formatIsoDateRangeLabelDe,
	toDateFromIsoDateYmd,
	toIsoDateYmdFromDate,
} from "@/lib/frontend/search/dateRange";

import {
	normalizeDayClickArgs,
	buildCalendarState,
} from "@/lib/frontend/search/dateRangePickerUtils";

const ACTIVE_FIELD = Object.freeze({
	FROM: "from",
	TO: "to",
});

function hasOwn(obj, key) {
	return Object.prototype.hasOwnProperty.call(obj || {}, key);
}

function focusRef(ref) {
	requestAnimationFrame(() => ref?.current?.focus?.());
}

export function useSearchDateRangePicker({
	from,
	to,
	onDateRangeChange,
	isSubmitting,
}) {
	const disabled = Boolean(isSubmitting);

	const [open, setOpen] = React.useState(false);
	const [activeField, setActiveField] = React.useState(ACTIVE_FIELD.FROM);

	const fromRef = React.useRef(null);
	const toRef = React.useRef(null);

	const fromDate = React.useMemo(() => toDateFromIsoDateYmd(from), [from]);
	const toDate = React.useMemo(() => toDateFromIsoDateYmd(to), [to]);

	const validation = React.useMemo(() => {
		return getSearchDateRangeValidation(from ?? null, to ?? null);
	}, [from, to]);

	const isRangeInvalid = validation?.code === "VALIDATION_SEARCH_RANGE";

	// Presets should reflect the current day even if the app is open across midnight.
	// We refresh presets on popover open, and also compute a fresh preset on click.
	const [presetsNow, setPresetsNow] = React.useState(() => new Date());

	const presets = React.useMemo(() => {
		return buildDatePresets({ now: presetsNow });
	}, [presetsNow]);

	const presetsRow1 = React.useMemo(() => presets.slice(0, 4), [presets]);
	const presetsRow2 = React.useMemo(() => presets.slice(4), [presets]);

	const [month, setMonth] = React.useState(() => {
		return fromDate || toDate || new Date();
	});

	React.useEffect(() => {
		if (!open) return;

		// Refresh preset reference "now" whenever the popover opens.
		setPresetsNow(new Date());

		// Only run on open transition.
		// We intentionally do not depend on from/to to avoid focus jumps while clicking.
		if (from && !to) {
			setActiveField(ACTIVE_FIELD.TO);
			focusRef(toRef);
		} else {
			setActiveField(ACTIVE_FIELD.FROM);
			focusRef(fromRef);
		}

		setMonth(fromDate || toDate || new Date());
		// eslint-disable-next-line react-hooks/exhaustive-deps
	}, [open]);

	const safeOnDateRangeChange = React.useCallback(
		(patch) => {
			if (typeof onDateRangeChange !== "function") return;

			const hasFrom = hasOwn(patch, "from");
			const hasTo = hasOwn(patch, "to");

			onDateRangeChange({
				from: hasFrom ? patch.from : (from ?? null),
				to: hasTo ? patch.to : (to ?? null),
			});
		},
		[onDateRangeChange, from, to],
	);

	const { calendarSelected, calendarModifiers, calendarModifiersClassNames } =
		React.useMemo(() => {
			return buildCalendarState({ fromDate, toDate, isRangeInvalid });
		}, [fromDate, toDate, isRangeInvalid]);

	const calendarKey = React.useMemo(() => {
		return `${from || ""}|${to || ""}|${isRangeInvalid ? "inv" : "ok"}`;
	}, [from, to, isRangeInvalid]);

	const summary = React.useMemo(() => {
		return formatIsoDateRangeLabelDe({ from, to }) || "Zeitraum auswählen";
	}, [from, to]);

	const fromDisplay = React.useMemo(() => formatIsoDateDe(from) || "", [from]);
	const toDisplay = React.useMemo(() => formatIsoDateDe(to) || "", [to]);

	const handlePickDay = React.useCallback(
		(...args) => {
			if (disabled) return;

			const { day, modifiers } = normalizeDayClickArgs(args);
			if (!(day instanceof Date) || Number.isNaN(day.getTime())) return;
			if (modifiers?.disabled) return;

			const iso = toIsoDateYmdFromDate(day);
			if (!iso) return;

			if (activeField === ACTIVE_FIELD.FROM) {
				safeOnDateRangeChange({ from: iso });
				setActiveField(ACTIVE_FIELD.TO);
				focusRef(toRef);
				return;
			}

			safeOnDateRangeChange({ to: iso });
			setActiveField(ACTIVE_FIELD.TO);
			focusRef(toRef);
		},
		[disabled, activeField, safeOnDateRangeChange],
	);

	const handleClearFrom = React.useCallback(
		(e) => {
			e?.preventDefault?.();
			e?.stopPropagation?.();
			if (disabled) return;

			safeOnDateRangeChange({ from: null });
			setActiveField(ACTIVE_FIELD.FROM);
			focusRef(fromRef);
		},
		[disabled, safeOnDateRangeChange],
	);

	const handleClearTo = React.useCallback(
		(e) => {
			e?.preventDefault?.();
			e?.stopPropagation?.();
			if (disabled) return;

			safeOnDateRangeChange({ to: null });
			setActiveField(ACTIVE_FIELD.TO);
			focusRef(toRef);
		},
		[disabled, safeOnDateRangeChange],
	);

	const handleReset = React.useCallback(() => {
		if (disabled) return;

		safeOnDateRangeChange({ from: null, to: null });
		setActiveField(ACTIVE_FIELD.FROM);
		setMonth(new Date());
		focusRef(fromRef);
	}, [disabled, safeOnDateRangeChange]);

	const applyPreset = React.useCallback(
		(preset) => {
			if (disabled) return;
			if (!preset?.key) return;

			// Defensive: compute a fresh preset at click-time (midnight edge-case).
			const freshList = buildDatePresets({ now: new Date() });
			const fresh = freshList.find((p) => p.key === preset.key) || preset;

			if (!fresh?.from || !fresh?.to) return;

			safeOnDateRangeChange({ from: fresh.from, to: fresh.to });

			const nextMonth =
				toDateFromIsoDateYmd(fresh.from) ||
				toDateFromIsoDateYmd(fresh.to) ||
				new Date();

			setMonth(nextMonth);
			setActiveField(ACTIVE_FIELD.TO);
			focusRef(toRef);
		},
		[disabled, safeOnDateRangeChange],
	);

	return {
		disabled,
		open,
		setOpen,
		activeField,
		setActiveField,
		fromRef,
		toRef,
		month,
		setMonth,
		summary,
		fromDisplay,
		toDisplay,
		presetsRow1,
		presetsRow2,
		calendarKey,
		calendarSelected,
		calendarModifiers,
		calendarModifiersClassNames,
		isRangeInvalid,
		handlePickDay,
		handleClearFrom,
		handleClearTo,
		handleReset,
		applyPreset,
	};
}
