import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import { forwardRef, useEffect, useRef, useState } from 'react';
import styled, { css } from 'styled-components';
import { StyledFormControl } from '../../FormControl';
import FormField from '../../FormField';
import { useConfiguration, useConsolidatedRef, useFocusWithin, useI18n, useOuterEvent, useTestIds } from '../../../hooks';
import Select, { Option } from '../../Select';
import Flex from '../../Flex';
import Button from '../../Button';
import Icon, { registerIcon } from '../../Icon';
import * as calendarIcon from '../../Icon/icons/calendar.icon';
import DatePicker from '../Picker/DatePicker';
import { hasProp, withTestIds } from '../../../utils';
import { getDateTimeInputTestIds } from '../DateTime.test-ids';
import { getAdjustedUTCDateTime } from '../utils';
import StyledDateTime, { StyledClearNowButtonsContainer, StyledDatePickerDialog, StyledDialogContent, StyledInputButton, StyledInputContainer } from './DateTime.styles';
import { generatePickerButtonAriaLabel, getDateFormat, getDateTimeParts, getTimeOptions, is12HClockFormat, parsePastedTextToDate, parseToDate, validateDate, validateDateTime, validateTime } from './utils';
import useAutoFocusNextInput from './useAutoFocusNextInput';
import PartInput from './PartInput';
import usePickerButton from './usePickerButton';
registerIcon(calendarIcon);
export const StyledDateTimeInput = styled(StyledDateTime)(() => {
    return css `
    padding-inline-end: 0;
    min-width: 0;
  `;
});
const StyledDateTimeFieldset = styled.fieldset `
  min-width: 0;
`;
function getMeridiem(hour) {
    return hour >= 12 ? 'PM' : 'AM';
}
const convertTo12h = (hour) => {
    let converted = hour;
    if (hour > 12)
        converted = hour - 12;
    else if (hour === 0)
        converted = 12;
    return converted;
};
const convertTo24h = (hour, meridiem) => {
    let converted = hour;
    if (meridiem === 'PM' && hour < 12)
        converted = hour + 12;
    else if (meridiem === 'AM' && hour === 12)
        converted = 0;
    return converted;
};
export const convertToCallbackParameter = (date, parts) => {
    const resultDate = Number.isNaN(date.getTime())
        ? new Date(new Date().setUTCHours(0, 0, 0, 0))
        : new Date(date);
    const withSeconds = hasProp(parts, 'second');
    const { year, month: monthIndex, day, hour, minute, second = resultDate.getUTCSeconds().toString(), dayPeriod: meridiem } = parts;
    const toValidate = withSeconds
        ? [year, monthIndex, day, hour, minute, second]
        : [year, monthIndex, day, hour, minute];
    if (toValidate.every(Boolean)) {
        const is12h = meridiem !== undefined;
        if (!validateDateTime(Number(year), Number(monthIndex) + 1, Number(day), Number(hour), Number(minute), Number(second), 0, is12h)) {
            return {
                valueAsISOString: '',
                valueAsTimestamp: NaN,
                state: 'invalid'
            };
        }
        resultDate.setUTCFullYear(Number(year), Number(monthIndex), Number(day));
        resultDate.setUTCHours(is12h ? convertTo24h(Number(hour), meridiem) : Number(hour), Number(minute), Number(second), resultDate.getUTCMilliseconds());
        return {
            valueAsISOString: resultDate.toISOString(),
            valueAsTimestamp: resultDate.getTime()
        };
    }
    if ([year, monthIndex, day, hour, minute].every(p => !p)) {
        return {
            valueAsISOString: undefined,
            valueAsTimestamp: undefined
        };
    }
    return {
        valueAsISOString: '',
        valueAsTimestamp: NaN,
        state: 'incomplete'
    };
};
function dateOptions() {
    return {
        year: 'numeric',
        month: 'numeric',
        day: 'numeric'
    };
}
const DateTimeInput = forwardRef(function DateTimeInput(props, ref) {
    const { locale } = useConfiguration();
    const t = useI18n();
    const { testId, value, min, max, id, label, labelHidden, withSeconds = false, clockFormat, info = getDateFormat(t, locale, withSeconds ? 'datetime+seconds' : 'datetime'), status, required, readOnly, disabled, showWeekNumber, pickerInterval = 30, onChange, onFocus, onBlur, onResolveSuggestion, additionalInfo, autoFocus, ...restProps } = props;
    const testIds = useTestIds(testId, getDateTimeInputTestIds);
    const containerRef = useConsolidatedRef(ref);
    const dateTime = value !== undefined && value !== '' ? parseToDate(value) : undefined;
    const is12h = clockFormat ? clockFormat === 12 : is12HClockFormat(locale);
    const [yearInputRef, monthInputRef, dayInputRef, hourInputRef, minuteInputRef, secondInputRef, meridiemSelectRef] = [
        useRef(null),
        useRef(null),
        useRef(null),
        useRef(null),
        useRef(null),
        useRef(null),
        useRef(null)
    ];
    const [hasValue, setHasValue] = useState(!!dateTime);
    useEffect(() => {
        setHasValue(!!(yearInputRef.current?.value ||
            monthInputRef.current?.value ||
            dayInputRef.current?.value ||
            hourInputRef.current?.value ||
            minuteInputRef.current?.value ||
            (withSeconds && secondInputRef.current?.value)));
    }, [dateTime]);
    useEffect(() => {
        if (is12h && meridiemSelectRef.current && dateTime?.getUTCHours() !== undefined) {
            meridiemSelectRef.current.value = getMeridiem(dateTime?.getUTCHours());
        }
    }, [is12h, dateTime?.getUTCHours()]);
    const [pickerSelection, setPickerSelection] = useState(undefined);
    const pickDateParts = () => {
        return {
            day: dayInputRef.current?.value || undefined,
            month: monthInputRef.current?.value
                ? (Number(monthInputRef.current.value) - 1).toString()
                : undefined,
            year: yearInputRef.current?.value || undefined
        };
    };
    const pickTimeParts = () => {
        const parts = {
            hour: hourInputRef.current?.value || undefined,
            minute: minuteInputRef.current?.value || undefined
        };
        if (withSeconds) {
            parts.second = secondInputRef.current?.value;
        }
        if (meridiemSelectRef.current) {
            parts.dayPeriod = meridiemSelectRef.current?.value;
        }
        return parts;
    };
    const pickParts = () => {
        return { ...pickDateParts(), ...pickTimeParts() };
    };
    const currentlySelectedDate = () => {
        const parts = pickDateParts();
        if (Object.values(parts).every(Boolean)) {
            const year = Number(parts.year);
            const month = Number(parts.month);
            const day = Number(parts.day);
            return validateDate(year, month + 1, day)
                ? new Date(Date.UTC(year, month, day))
                : undefined;
        }
    };
    const currentlySelectedTime = () => {
        const parts = pickParts();
        if (Object.values(parts).every(Boolean)) {
            const hour = Number(parts.hour);
            const minute = Number(parts.minute);
            const second = parts.second ? Number(parts.second) : 0;
            const meridiem = parts.dayPeriod;
            // make a copy of current value to avoid mutation
            const current = dateTime !== undefined ? new Date(dateTime) : getAdjustedUTCDateTime();
            return validateTime(hour, minute, second, 0, meridiem !== undefined)
                ? new Date(current.setUTCHours(meridiem !== undefined ? convertTo24h(hour, meridiem) : hour, minute, second, dateTime?.getUTCMilliseconds() || 0))
                : undefined;
        }
    };
    const currentlySelectedDateTime = () => {
        const selectedDate = currentlySelectedDate();
        const selectedTime = currentlySelectedTime();
        return selectedDate && selectedTime
            ? new Date(selectedDate.setUTCHours(selectedTime.getUTCHours(), selectedTime.getUTCMinutes(), selectedTime.getUTCSeconds(), selectedTime.getUTCMilliseconds()))
            : undefined;
    };
    const setDefaultTimeIfEmpty = () => {
        setPickerSelection(undefined);
        // set value to midnight if time empty, skip if date not fully set
        if (Object.values(pickDateParts()).every(Boolean) &&
            Object.values(pickTimeParts()).every(p => !p)) {
            if (hourInputRef.current)
                hourInputRef.current.value = is12h ? '12' : '00';
            if (minuteInputRef.current)
                minuteInputRef.current.value = '00';
            if (withSeconds && secondInputRef.current)
                secondInputRef.current.value = '00';
            if (is12h && meridiemSelectRef.current) {
                meridiemSelectRef.current.value = 'AM';
            }
        }
    };
    const [open, setOpen, , setPopoverRef, buttonEl, setButtonEl] = usePickerButton(setDefaultTimeIfEmpty);
    const onInputChange = () => {
        onChange?.(convertToCallbackParameter(dateTime ? new Date(dateTime) : new Date(), pickParts()));
        buttonEl?.setAttribute('aria-label', generatePickerButtonAriaLabel(currentlySelectedDateTime(), 'datetime', locale, t, open, is12h));
        setHasValue(!!(dayInputRef.current?.value ||
            monthInputRef.current?.value ||
            yearInputRef.current?.value ||
            hourInputRef.current?.value ||
            minuteInputRef.current?.value ||
            (withSeconds && secondInputRef.current?.value)));
    };
    const setInputValue = (newValue) => {
        dayInputRef.current.value = newValue?.getUTCDate().toString().padStart(2, '0') ?? '';
        monthInputRef.current.value =
            (newValue && (newValue.getUTCMonth() + 1).toString().padStart(2, '0')) || '';
        yearInputRef.current.value = newValue?.getUTCFullYear().toString() ?? '';
        if (newValue) {
            const hours = newValue.getUTCHours();
            hourInputRef.current.value = (is12h ? convertTo12h(hours) : hours)
                .toString()
                .padStart(2, '0');
        }
        else {
            hourInputRef.current.value = '';
        }
        minuteInputRef.current.value = newValue?.getUTCMinutes().toString().padStart(2, '0') ?? '';
        if (withSeconds) {
            secondInputRef.current.value = newValue?.getUTCSeconds().toString().padStart(2, '0') ?? '';
        }
        if (meridiemSelectRef.current) {
            meridiemSelectRef.current.value = newValue ? getMeridiem(newValue.getUTCHours()) : 'AM';
        }
    };
    const onPaste = (e) => {
        const text = e.clipboardData.getData('text');
        const date = parsePastedTextToDate(text, locale, dateTime || new Date(), 'datetime');
        if (date) {
            e.preventDefault();
            setInputValue(date);
            setPickerSelection(date);
            onChange?.(convertToCallbackParameter(date, pickParts()));
            setHasValue(true);
        }
    };
    const inputContainerRef = useRef(null);
    const onFocusChange = (focused) => {
        const callbackParam = convertToCallbackParameter(dateTime ? new Date(dateTime) : new Date(), pickParts());
        if (onFocus && focused)
            onFocus(callbackParam);
        if (!focused) {
            if (inputContainerRef.current)
                inputContainerRef.current.scrollTop = 0;
            onBlur?.(callbackParam);
        }
    };
    // autoclose picker on focusing out
    useOuterEvent('focusin', [containerRef], e => {
        if (containerRef.current?.contains(e.relatedTarget)) {
            setPickerSelection(undefined);
            setOpen(false);
        }
    });
    const displayNames = new Intl.DisplayNames(locale, { style: 'long', type: 'dateTimeField' });
    const DayInput = (_jsx(PartInput, { "data-testid": testIds.controlDay, placeholder: t('day_placeholder'), "aria-label": displayNames.of('day'), value: dateTime?.getUTCDate(), onChange: onInputChange, size: 2, maxLength: 2, readOnly: readOnly, min: 1, max: 31, ref: dayInputRef }, 'day'));
    const MonthInput = (_jsx(PartInput, { "data-testid": testIds.controlMonth, placeholder: t('month_placeholder'), "aria-label": displayNames.of('month'), value: dateTime ? dateTime.getUTCMonth() + 1 : undefined, onChange: onInputChange, size: 2, maxLength: 2, readOnly: readOnly, min: 1, max: 12, ref: monthInputRef }, 'month'));
    const YearInput = (_jsx(PartInput, { "data-testid": testIds.controlYear, placeholder: t('year_placeholder'), "aria-label": displayNames.of('year'), value: dateTime?.getUTCFullYear(), onChange: onInputChange, size: 4, maxLength: 4, padWithZeros: false, readOnly: readOnly, min: 1, ref: yearInputRef }, 'year'));
    const hour = dateTime && is12h ? convertTo12h(dateTime.getUTCHours()) : dateTime?.getUTCHours();
    const HourInput = (_jsx(PartInput, { "data-testid": testIds.controlHour, placeholder: t('hour_placeholder'), "aria-label": displayNames.of('hour'), value: hour, onChange: onInputChange, size: 2, maxLength: 2, readOnly: readOnly, min: is12h ? 1 : 0, max: is12h ? 12 : 23, ref: hourInputRef }, 'hour'));
    const MinuteInput = (_jsx(PartInput, { "data-testid": testIds.controlMinute, placeholder: t('minute_placeholder'), "aria-label": displayNames.of('minute'), value: dateTime?.getUTCMinutes(), onChange: onInputChange, size: 2, maxLength: 2, readOnly: readOnly, max: 59, ref: minuteInputRef }, 'minute'));
    const SecondInput = (_jsx(PartInput, { "data-testid": testIds.controlSecond, placeholder: t('second_placeholder'), "aria-label": displayNames.of('second'), value: dateTime?.getUTCSeconds(), onChange: onInputChange, size: 2, maxLength: 2, readOnly: readOnly, max: 59, ref: secondInputRef }, 'second'));
    const Period = (_jsxs(Select, { "data-testid": testIds.controlPeriod, ref: meridiemSelectRef, "aria-label": displayNames.of('dayPeriod'), required: required, disabled: disabled, onChange: () => {
            if (Object.values(pickParts()).every(Boolean))
                onInputChange();
        }, readOnly: readOnly, children: [_jsx(Option, { value: 'AM', children: t('meridiem_value_am') }), _jsx(Option, { value: 'PM', children: t('meridiem_value_pm') })] }, 'period'));
    const orderedRefs = [];
    const dateInputs = getDateTimeParts(locale, dateOptions()).map(part => {
        switch (part) {
            case 'day':
                orderedRefs.push(dayInputRef);
                return DayInput;
            case 'month':
                orderedRefs.push(monthInputRef);
                return MonthInput;
            case 'year':
                orderedRefs.push(yearInputRef);
                return YearInput;
            default:
                return part;
        }
    });
    const timeInputs = getDateTimeParts(locale, getTimeOptions(withSeconds, is12h)).map(part => {
        switch (part) {
            case 'hour':
                orderedRefs.push(hourInputRef);
                return HourInput;
            case 'minute':
                orderedRefs.push(minuteInputRef);
                return MinuteInput;
            case 'second':
                orderedRefs.push(secondInputRef);
                return SecondInput;
            case 'dayPeriod':
                return Period;
            default:
                return part;
        }
    });
    useEffect(() => {
        if (autoFocus)
            orderedRefs[0].current?.focus();
    }, [autoFocus]);
    useFocusWithin([containerRef], onFocusChange);
    const Picker = (_jsxs(_Fragment, { children: [_jsx(StyledInputButton, { "data-testid": testIds.openClosePicker, variant: 'simple', icon: true, ref: setButtonEl, onClick: () => {
                    setOpen(cur => !cur);
                }, "aria-label": generatePickerButtonAriaLabel(currentlySelectedDateTime(), 'datetime', locale, t, open, is12h), readOnly: readOnly, disabled: disabled, children: _jsx(Icon, { name: 'calendar' }) }), open && buttonEl && (_jsx(StyledDatePickerDialog, { ref: setPopoverRef, target: buttonEl, placement: 'bottom-end', "aria-label": t('choose_date_and_time'), children: _jsxs(Flex, { container: { direction: 'column' }, as: StyledDialogContent, children: [_jsx(DatePicker, { testId: testIds.root, mode: 'datetime', selected: currentlySelectedDateTime(), onChange: selected => {
                                setPickerSelection(selected);
                            }, min: min, max: max, showWeekNumber: showWeekNumber, pickerInterval: pickerInterval, withSeconds: withSeconds, clockFormat: clockFormat }), _jsxs(Flex, { as: StyledClearNowButtonsContainer, container: { justify: 'between' }, children: [_jsx(Button, { "data-testid": testIds.discardChanges, "aria-label": t('cancel'), onClick: () => {
                                        setPickerSelection(undefined);
                                        setOpen(false);
                                        setTimeout(() => buttonEl?.focus());
                                        onInputChange();
                                    }, children: t('cancel') }), _jsx(Button, { "data-testid": testIds.applyChanges, "aria-label": t('apply'), variant: 'primary', onClick: () => {
                                        if (pickerSelection) {
                                            setInputValue(pickerSelection);
                                        }
                                        setOpen(false);
                                        setTimeout(() => buttonEl?.focus());
                                        onInputChange();
                                    }, children: t('apply') })] })] }) }))] }));
    const Comp = (_jsxs(Flex, { "data-testid": testIds.root, as: StyledDateTimeInput, forwardedAs: StyledFormControl, hasSuggestion: status === 'pending' && !!onResolveSuggestion, container: { alignItems: 'center', wrap: 'nowrap', justify: 'between' }, status: status, readOnly: readOnly, disabled: disabled, ref: containerRef, onClick: (e) => {
            if (e.target === containerRef.current)
                orderedRefs[0].current?.focus();
        }, ...restProps, children: [_jsxs(Flex, { ref: inputContainerRef, as: StyledInputContainer, container: { alignItems: 'center', wrap: 'wrap' }, onPaste: onPaste, children: [dateInputs, timeInputs] }), _jsxs(Flex, { container: true, children: [!readOnly && hasValue && (_jsx(StyledInputButton, { icon: true, onClick: () => {
                            setPickerSelection(undefined);
                            setInputValue(undefined);
                            setTimeout(() => buttonEl?.focus());
                            onInputChange();
                        }, variant: 'simple', compact: true, "aria-label": t('clear_current_date_and_time'), label: t('clear'), "data-testid": testIds.clearCurrentDateAndTime, children: _jsx(Icon, { name: 'times' }) })), !readOnly && Picker] })] }));
    const { description, descriptionID } = useAutoFocusNextInput(orderedRefs);
    return label ? (_jsxs(_Fragment, { children: [_jsx(FormField, { testId: testIds, as: StyledDateTimeFieldset, labelAs: 'legend', "aria-labelledby": id, ref: ref, label: label, labelHidden: labelHidden, id: id, info: info, readOnly: readOnly, status: status, required: required, disabled: disabled, onResolveSuggestion: onResolveSuggestion, additionalInfo: additionalInfo, "aria-describedby": descriptionID, children: Comp }), description] })) : (Comp);
});
export default withTestIds(DateTimeInput, getDateTimeInputTestIds);
//# sourceMappingURL=DateTimeInput.js.map