import React, { useCallback, useEffect, useRef, useState } from 'react';
import { CellDateInput, CellTimeInput, dateToTime, timeToDate, useTranslations, } from '@wix/patterns-fields';
import { useCloseOnAncestorScroll } from '../../../../hooks/useCloseOnAncestorScroll';
import { mergeDate, mergeTime } from './mergeDateTime';
import { datePlaceholder, formatDatePart, formatTimePart, timePlaceholder, } from './formatters';
import { isValidDate, setNativeInputValue } from './helpers';
import { useSubCellClickTracker } from './subCellContext';
import { classes } from './Edit.st.css.js';
export const DateTimeEdit = ({ value, onChange, onCommit, onCancel, inputRef, editTrigger, }) => {
    const { t } = useTranslations();
    // Initial sub-cell. The View records the clicked half via
    // `onMouseDownCapture` (capture phase, before the framework's onClick
    // stopPropagation) into the wrapper-provided tracker; consume it once.
    const tracker = useSubCellClickTracker();
    const [subEdit, setSubEdit] = useState(() => {
        if (editTrigger === 'click' || editTrigger === 'doubleClick') {
            return tracker.consume() ?? 'date';
        }
        return 'date';
    });
    const dateWrapperRef = useRef(null);
    const timeWrapperRef = useRef(null);
    const datePickerRef = useRef(null);
    const timeInputRef = useRef(null);
    // Direct refs to the underlying <input> in each half. Populated by
    // CellDateInput / CellTimeInput via their object-ref `inputRef` prop.
    // Used for focus, programmatic typed-key forwarding, and the "is the
    // date input focused?" check — instead of querySelector'ing into WDS
    // internals.
    const dateInputElRef = useRef(null);
    const timeInputElRef = useRef(null);
    const isInsideCell = useCallback((target) => {
        if (!(target instanceof Node)) {
            return false;
        }
        return Boolean(dateWrapperRef.current?.contains(target) ||
            timeWrapperRef.current?.contains(target));
    }, []);
    // Latest date from the date input (typed or picked), NOT yet committed to
    // editingValue. WDS DateInput's `componentDidUpdate` reformats the input
    // text on every props.value change, so calling onChange while the user is
    // still in the date half would overwrite mid-typing input. We stash here
    // and flush on focusout / intra-cell Tab / Escape.
    const typedDateRef = useRef(null);
    const onChangeRef = useRef(onChange);
    const onCommitRef = useRef(onCommit);
    const valueRef = useRef(value);
    const subEditRef = useRef(subEdit);
    onChangeRef.current = onChange;
    onCommitRef.current = onCommit;
    valueRef.current = value;
    subEditRef.current = subEdit;
    // Type-to-edit trigger is consumed exactly once on mount. Without this
    // guard, both type-forwarding effects below would re-inject the typed
    // character every time `subEdit` changes (Tab between halves), because
    // `editTrigger` stays stable for the lifetime of the edit session.
    const typeTriggerConsumedRef = useRef(false);
    const commitTypedDate = useCallback(() => {
        if (typedDateRef.current) {
            onChangeRef.current(typedDateRef.current);
            typedDateRef.current = null;
        }
    }, []);
    const closeCalendar = useCallback(() => datePickerRef.current?.closeCalendar(), []);
    useCloseOnAncestorScroll(dateWrapperRef, closeCalendar, subEdit === 'date');
    const closeTimeDropdown = useCallback(() => timeInputRef.current?.blur(), []);
    useCloseOnAncestorScroll(timeWrapperRef, closeTimeDropdown, subEdit === 'time');
    // Flush typed-but-not-committed date when focus leaves the date half. The
    // gesture paths (calendar day pick, Tab/Enter on focused day, intra-cell
    // Tab, Enter on input) flush via their own handlers; this covers external
    // exits — clicking another cell, the toolbar, or anything else outside
    // the date wrapper. Without it the framework's commitEdit (triggered by
    // the focus shift to another cell) would read the stale editingValue and
    // silently discard the user's typing.
    useEffect(() => {
        if (subEdit !== 'date') {
            return;
        }
        const wrapper = dateWrapperRef.current;
        if (!wrapper) {
            return;
        }
        const onFocusOut = (e) => {
            const next = e.relatedTarget;
            if (next && wrapper.contains(next)) {
                return;
            }
            commitTypedDate();
        };
        wrapper.addEventListener('focusout', onFocusOut);
        return () => {
            wrapper.removeEventListener('focusout', onFocusOut);
        };
    }, [subEdit, commitTypedDate]);
    // Focus active sub-input on mount / sub-cell switch. Deferred via rAF so
    // any in-flight click chain finishes before WDS dropdown options mount —
    // otherwise a stray mousedown can land on a freshly-rendered option and
    // silently commit a value the user never picked.
    useEffect(() => {
        if (subEdit === 'time') {
            const raf = requestAnimationFrame(() => {
                const input = timeInputElRef.current;
                if (input) {
                    input.focus();
                    input.click(); // auto-open WDS TimeInput dropdown
                }
            });
            return () => cancelAnimationFrame(raf);
        }
        const raf = requestAnimationFrame(() => {
            datePickerRef.current?.focus();
        });
        return () => cancelAnimationFrame(raf);
    }, [subEdit]);
    // Type-to-edit: forward the typed character into the initial sub-cell's
    // input. Runs once via typeTriggerConsumedRef — re-running on sub-cell
    // switches would re-inject the digit every time the user Tabs between the
    // date and time halves (editTrigger stays stable for the whole session).
    useEffect(() => {
        if (typeTriggerConsumedRef.current) {
            return;
        }
        if (!editTrigger ||
            typeof editTrigger !== 'object' ||
            editTrigger.kind !== 'type') {
            return;
        }
        typeTriggerConsumedRef.current = true;
        const key = editTrigger.key;
        if (key.length !== 1) {
            return;
        }
        const raf = requestAnimationFrame(() => {
            if (subEditRef.current === 'date') {
                datePickerRef.current?.openCalendar();
                const dpInput = dateInputElRef.current;
                if (dpInput) {
                    setNativeInputValue(dpInput, key);
                }
                return;
            }
            const input = timeInputElRef.current;
            if (input) {
                setNativeInputValue(input, key);
            }
        });
        return () => cancelAnimationFrame(raf);
    }, [editTrigger]);
    // Wire inputRef to whichever sub-input is currently active.
    useEffect(() => {
        if (!inputRef) {
            return;
        }
        inputRef.current =
            (subEdit === 'time' ? timeInputElRef.current : dateInputElRef.current) ??
                undefined;
    }, [subEdit, inputRef]);
    // Intra-cell Tab (date → time, Shift+Tab time → date). Document-capture
    // so we run BEFORE the table's useKeyboardNavigation Tab handler — the
    // framework only sees the Tab that should actually exit the cell.
    useEffect(() => {
        const onKeyDownCapture = (e) => {
            if (e.key !== 'Tab') {
                return;
            }
            if (!isInsideCell(e.target)) {
                return;
            }
            if (!e.shiftKey && subEditRef.current === 'date') {
                e.preventDefault();
                e.stopImmediatePropagation();
                commitTypedDate();
                setSubEdit('time');
                return;
            }
            if (e.shiftKey && subEditRef.current === 'time') {
                e.preventDefault();
                e.stopImmediatePropagation();
                setSubEdit('date');
                return;
            }
        };
        document.addEventListener('keydown', onKeyDownCapture, true);
        return () => {
            document.removeEventListener('keydown', onKeyDownCapture, true);
        };
    }, [commitTypedDate, isInsideCell]);
    // Click on the inactive sub-cell switches sub-edit. pointerdown capture
    // only: calling preventDefault on a primary pointer's pointerdown
    // suppresses the synthesized mousedown / click that would otherwise let
    // WDS DropdownBase.onSelect commit whichever option happens to sit under
    // the click coords. Pair with the rAF defer on dropdown open above.
    useEffect(() => {
        const onPointerDown = (e) => {
            if (!isInsideCell(e.target)) {
                return;
            }
            const target = e.target;
            const sub = target?.closest('[data-sub-cell]')?.getAttribute('data-sub-cell');
            if (sub !== 'date' && sub !== 'time') {
                return;
            }
            if (sub === subEditRef.current) {
                return;
            }
            e.preventDefault();
            e.stopImmediatePropagation();
            setSubEdit(sub);
        };
        document.addEventListener('pointerdown', onPointerDown, true);
        return () => {
            document.removeEventListener('pointerdown', onPointerDown, true);
        };
    }, [isInsideCell]);
    const handleDateChange = useCallback((newDate) => {
        // Stash, don't commit — WDS DateInput reformats input text on every
        // props.value change, clobbering mid-typing input. The focusout effect
        // (and intra-cell Tab) flushes this to editingValue at the right moment.
        typedDateRef.current = mergeDate(valueRef.current, newDate);
    }, []);
    const handleTimeChange = useCallback((time) => {
        if (!time) {
            // Per spec: no clear on the time half. CellTimeInput's clear button
            // is suppressed (no onClear prop). If WDS somehow emits null, no-op.
            return;
        }
        const asDate = timeToDate(time);
        const merged = mergeTime(valueRef.current, asDate);
        onChange(merged);
        onCommit();
    }, [onChange, onCommit]);
    // Clears the entire DateTime value. Wired to the date half's clear (X)
    // button — the single "remove this DateTime" affordance. Time half has no
    // clear button: there is no "date set, time null" state.
    const handleDateClear = useCallback(() => {
        // Drop any stashed typed date so the focusout flush doesn't restore it
        // after we commit null.
        typedDateRef.current = null;
        onChange(null);
        onCommit();
    }, [onChange, onCommit]);
    const handleEscape = useCallback((e) => {
        if (e.key === 'Escape') {
            e.preventDefault();
            e.stopPropagation();
            // Cancellation drops any typed-but-stashed date. Without this, a
            // delayed focusout (browsers fire blur asynchronously when the
            // focused element is removed) could re-introduce the typed value
            // after the user already pressed Escape.
            typedDateRef.current = null;
            onCancel();
        }
    }, [onCancel]);
    const validDate = isValidDate(value) ? value : null;
    const timeAsString = validDate ? dateToTime(validDate) : null;
    const placeholderTokens = {
        day: t('CMS.cellDateTime.placeholder.day'),
        month: t('CMS.cellDateTime.placeholder.month'),
        year: t('CMS.cellDateTime.placeholder.year'),
        hour: t('CMS.cellDateTime.placeholder.hour'),
        minute: t('CMS.cellDateTime.placeholder.minute'),
    };
    const datePh = datePlaceholder(placeholderTokens);
    const timePh = timePlaceholder(placeholderTokens);
    // Event-suppression layers in this component, by owner:
    //   1. halfHandlers.onClick / onMouseDown (React, below)
    //      → swallow the framework's "click inside cell during edit" handling
    //        so clicks on either half don't re-trigger an edit start.
    //   2. halfHandlers.onKeyDown = handleEscape (React, below)
    //      → cancels the edit before the framework's Escape behavior runs.
    //   3. document keydown capture for intra-cell Tab (useEffect above)
    //      → date↔time half switch runs BEFORE the framework's Tab handler
    //        would move focus to the next column.
    //   4. document pointerdown capture for sub-cell switch (useEffect above)
    //      → click on the inactive half switches subEdit without committing,
    //        and preventDefault suppresses the synthesized mousedown/click
    //        that would otherwise let WDS DropdownBase.onSelect commit a
    //        random option under the cursor.
    const halfHandlers = {
        onKeyDown: handleEscape,
        onClick: (e) => e.stopPropagation(),
        onMouseDown: (e) => e.stopPropagation(),
    };
    return (React.createElement(React.Fragment, null,
        React.createElement("div", { ref: dateWrapperRef, "data-sub-cell": "date", className: classes.dateHalf, ...halfHandlers }, subEdit === 'date' ? (React.createElement(CellDateInput, { ref: datePickerRef, inputRef: dateInputElRef, value: validDate, onChange: handleDateChange, onClear: handleDateClear, placeholder: datePh, ariaLabel: t('CMS.cellDateTime.ariaLabel.date'), hidePrefix: true, dataHook: "datetime-date-edit" })) : validDate ? (React.createElement("span", { className: classes.passiveText }, formatDatePart(validDate))) : (React.createElement("span", { className: classes.placeholderText }, datePh))),
        React.createElement("span", { "aria-hidden": true, className: classes.divider }),
        React.createElement("div", { ref: timeWrapperRef, "data-sub-cell": "time", className: classes.timeHalf, "aria-label": t('CMS.cellDateTime.ariaLabel.time'), ...halfHandlers }, subEdit === 'time' ? (React.createElement(CellTimeInput, { ref: timeInputRef, inputRef: timeInputElRef, value: timeAsString, onChange: handleTimeChange, placeholder: timePh, clearable: false, dataHook: "datetime-time-edit" })) : validDate ? (React.createElement("span", { className: classes.passiveText }, formatTimePart(validDate))) : (React.createElement("span", { className: classes.placeholderText }, timePh)))));
};
//# sourceMappingURL=Edit.js.map