import React__default, { useState, useCallback, useEffect, useId, useRef } from 'react';
import { _ as __rest } from '../tslib.es6-es.js';
import { a as debounce } from '../debounce-es.js';
import { c as FormFieldWrapper, h as formFieldStyles, m as mergeRefs } from '../mergeRefs-es.js';
import 'classnames';
import '@jobber/design';
import 'react-hook-form';
import '../Button-es.js';
import { f as filterDataAttributes } from '../filterDataAttributes-es.js';
import { o as omit } from '../omit-es.js';
import { F as FormField } from '../FormField-es.js';
import '@jobber/hooks';
import 'framer-motion';
import '../_commonjsHelpers-es.js';
import '../isObjectLike-es.js';
import '../isSymbol-es.js';
import '../Icon-es.js';
import '../Text-es.js';
import '../Typography-es.js';
import '../useFormFieldFocus-es.js';
import '../InputValidation-es.js';
import '../Spinner-es.js';
import 'react-router-dom';
import '../_getAllKeys-es.js';
import '../isTypedArray-es.js';
import '../identity-es.js';
import '../_getTag-es.js';
import '../keysIn-es.js';
import '../_baseAssignValue-es.js';
import '../_baseFlatten-es.js';
import '../_setToString-es.js';

const DEBOUNCE_TIME = 300;
function useTimePredict({ value, handleChange }) {
    const [IS_12_HOUR_FORMAT, set12HourFormat] = useState(false);
    const [typedTime, setTypedTime] = useState("");
    const predictTime = useCallback(debounce(() => {
        if (value)
            return;
        handleChange(predictHours(typedTime, IS_12_HOUR_FORMAT));
    }, DEBOUNCE_TIME), [typedTime, value, handleChange, IS_12_HOUR_FORMAT]);
    useEffect(() => {
        set12HourFormat(Boolean(Intl.DateTimeFormat(navigator.language, {
            hour: "numeric",
        }).resolvedOptions().hour12));
    }, []);
    /**
     * Predict the hour when user types a number
     */
    useEffect(() => {
        /**
         * Don't try to predict if the user types 0 as the user would almost always
         * type another number after 0.
         */
        if (typedTime && typedTime !== "0") {
            predictTime();
            if ((IS_12_HOUR_FORMAT && typedTime !== "1") || typedTime.length === 2) {
                // Immediately predict the hour
                predictTime.flush();
            }
        }
        return predictTime.cancel;
    }, [typedTime]);
    /**
     * Reset typed time when the value changed
     */
    useEffect(() => {
        setTypedTime("");
    }, [value]);
    return {
        setTypedTime,
    };
}
function predictHours(time, is12HourFormat = false) {
    const today = new Date();
    const currentHour = today.getHours();
    const parsedTime = parseInt(time, 10);
    let predictedTime;
    if (is12HourFormat) {
        predictedTime = predict12Hours(parsedTime, currentHour);
    }
    else {
        predictedTime = predict24Hours(time, parsedTime, currentHour);
    }
    return formatHour(predictedTime);
}
function predict12Hours(parsedTime, currentHour) {
    /**
     * We need to predict what the user wants when they type 1 since it could be
     * 10, 11, 12, or 1.
     *
     * If the current hour is over 12PM, we can skip this and go to the next logic.
     */
    if (parsedTime === 1 && currentHour < 12) {
        if (currentHour < 10) {
            return 10;
        }
        else {
            return currentHour + 1;
        }
    }
    /**
     * Typing 1-5 predicts that the user want that exact hour on the afternoon.
     */
    if (parsedTime <= 5) {
        return parsedTime + 12;
    }
    /**
     * Typing 13-15 predicts that the user want the 2nd number as the minute interval.
     */
    if (parsedTime > 12 && parsedTime <= 15) {
        const timeArray = parsedTime.toString().split("").map(Number); // 13 -> [1, 3]
        const convertToHoursAndMinutes = timeArray.map((time, i) => {
            if (i === 0)
                return time + 12; // 1 -> 13 as 1PM
            if (i === 1)
                return time * 10; // 3 -> 30 as 30 minutes
        });
        return Number(convertToHoursAndMinutes.join("")); // [13, 30] -> 1330
    }
    /**
     * Anything after 5 will be predicted to be set as AM.
     */
    return parsedTime;
}
function predict24Hours(time, parsedTime, currentHour) {
    /**
     * Typing 1 could be predicted from 10 to 19.
     * Typing 01 skips this logic and sets the time to 01:00.
     */
    if (parsedTime === 1 && time !== "01") {
        if (currentHour < 10 || currentHour > 19) {
            return 10;
        }
        else {
            return currentHour + 1;
        }
    }
    /**
     * Typing 2 could be predicted from 20 to 24.
     * Typing 02 skips this logic and sets the time to 02:00.
     */
    if (parsedTime === 2 && time !== "02") {
        if (currentHour < 20) {
            return 20;
        }
        else {
            return currentHour + 1;
        }
    }
    /**
     * Typing 24 or 00 is set to midnight. This ensures the time doesn't get set
     * to 24 as that would be invalid.
     */
    if (parsedTime === 24 || time === "00") {
        return 0;
    }
    return parsedTime;
}
function formatHour(time) {
    if (time > 99) {
        const splitTime = time.toString().split("");
        if (splitTime.length === 3)
            splitTime.unshift("0"); // 130 -> 0130
        splitTime.splice(2, 0, ":"); // 0130 -> 01:30
        return splitTime.join("");
    }
    if (time < 10) {
        return `0${time}:00`;
    }
    return `${time}:00`;
}

function dateToTimeString(date, includeSeconds = false) {
    if (!(date instanceof Date)) {
        return "";
    }
    const hours = date.getHours().toString().padStart(2, "0");
    const minutes = date.getMinutes().toString().padStart(2, "0");
    if (includeSeconds) {
        const seconds = date.getSeconds().toString().padStart(2, "0");
        return `${hours}:${minutes}:${seconds}`;
    }
    return `${hours}:${minutes}`;
}
function timeStringToDate(timeString, baseDate) {
    try {
        const [hours, minutes, seconds = 0] = timeString.split(":").map(Number);
        if (isNaN(hours) ||
            isNaN(minutes) ||
            isNaN(seconds) ||
            hours < 0 ||
            hours > 24 ||
            minutes < 0 ||
            minutes > 60 ||
            seconds < 0 ||
            seconds > 60) {
            return undefined;
        }
        // Try to preserve the Date part of the Date object as long as it is valid
        const date = baseDate instanceof Date && !isNaN(baseDate.getTime())
            ? new Date(baseDate)
            : new Date();
        date.setHours(hours, minutes, seconds, 0);
        return date;
    }
    catch (_a) {
        return undefined;
    }
}

function useInputTimeActions({ onChange, value, inputRef, onFocus, onBlur, onKeyDown, onClick, onMouseDown, onMouseUp, onPointerDown, onPointerUp, }) {
    function handleChangeEvent(event) {
        handleChange(event.target.value);
    }
    function handleChange(newValue) {
        onChange === null || onChange === void 0 ? void 0 : onChange(timeStringToDate(newValue, value));
    }
    function handleBlur(event) {
        onBlur === null || onBlur === void 0 ? void 0 : onBlur(event);
        if (inputRef === null || inputRef === void 0 ? void 0 : inputRef.current) {
            if (!inputRef.current.checkValidity()) {
                inputRef.current.value = "";
                inputRef.current.valueAsDate = null;
                // Remove validation error. This is mainly needed for Safari
                inputRef.current.setCustomValidity("");
            }
        }
    }
    function handleClear() {
        var _a;
        // Clear the value and refocus without triggering blur event
        onChange === null || onChange === void 0 ? void 0 : onChange(undefined);
        (_a = inputRef === null || inputRef === void 0 ? void 0 : inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
    }
    function handleFocus(event) {
        onFocus === null || onFocus === void 0 ? void 0 : onFocus(event);
    }
    function handleKeyDown(event) {
        onKeyDown === null || onKeyDown === void 0 ? void 0 : onKeyDown(event);
    }
    function handleClick(event) {
        onClick === null || onClick === void 0 ? void 0 : onClick(event);
    }
    function handleMouseDown(event) {
        onMouseDown === null || onMouseDown === void 0 ? void 0 : onMouseDown(event);
    }
    function handleMouseUp(event) {
        onMouseUp === null || onMouseUp === void 0 ? void 0 : onMouseUp(event);
    }
    function handlePointerDown(event) {
        onPointerDown === null || onPointerDown === void 0 ? void 0 : onPointerDown(event);
    }
    function handlePointerUp(event) {
        onPointerUp === null || onPointerUp === void 0 ? void 0 : onPointerUp(event);
    }
    return {
        handleChangeEvent,
        handleChange,
        handleBlur,
        handleClear,
        handleFocus,
        handleKeyDown,
        handleClick,
        handleMouseDown,
        handleMouseUp,
        handlePointerDown,
        handlePointerUp,
    };
}

function InputTimeRebuilt(_a) {
    var _b;
    var { value, onChange, readOnly, autoComplete, inputRef, step = 60 } = _a, props = __rest(_a, ["value", "onChange", "readOnly", "autoComplete", "inputRef", "step"]);
    const { internalRef, mergedRef, wrapperRef } = useInputTimeRefs(inputRef);
    const generatedId = useId();
    const id = props.id || generatedId;
    const { handleChangeEvent, handleChange, handleBlur, handleClear, handleFocus, handleKeyDown, handleClick, handleMouseDown, handleMouseUp, handlePointerDown, handlePointerUp, } = useInputTimeActions({
        onChange,
        value,
        readOnly,
        disabled: props.disabled,
        inputRef: internalRef,
        onFocus: props.onFocus,
        onBlur: props.onBlur,
        onKeyDown: props.onKeyDown,
        onClick: props.onClick,
        onMouseDown: props.onMouseDown,
        onMouseUp: props.onMouseUp,
        onPointerDown: props.onPointerDown,
        onPointerUp: props.onPointerUp,
    });
    const { setTypedTime } = useTimePredict({
        value,
        handleChange,
    });
    // Kept outside the useInputTimeActions hook to avoid circular dependency via setTypedTime and handleChange
    function handleKeyUp(event) {
        var _a;
        (_a = props.onKeyUp) === null || _a === void 0 ? void 0 : _a.call(props, event);
        if (props.disabled || readOnly)
            return;
        !isNaN(parseInt(event.key, 10)) && setTypedTime(prev => prev + event.key);
    }
    const dataAttrs = filterDataAttributes(props);
    const descriptionIdentifier = `descriptionUUID--${id}`;
    const descriptionVisible = props.description && !props.inline;
    const isInvalid = Boolean(props.error || props.invalid);
    return (React__default.createElement(FormFieldWrapper, { disabled: props.disabled, size: props.size, align: props.align, inline: props.inline, name: props.name, error: props.error || "", identifier: id, descriptionIdentifier: descriptionIdentifier, invalid: props.invalid, description: props.description, clearable: (_b = props.clearable) !== null && _b !== void 0 ? _b : "never", onClear: handleClear, type: "time", readonly: readOnly, placeholder: props.placeholder, value: dateToTimeString(value, step % 60 !== 0), wrapperRef: wrapperRef },
        React__default.createElement("input", Object.assign({ ref: mergedRef, type: "time", name: props.name, className: formFieldStyles.input, id: id, disabled: props.disabled, readOnly: readOnly, autoComplete: autoComplete, autoFocus: props.autoFocus, required: props.required, max: props.max, min: props.min, step: step, value: dateToTimeString(value, step % 60 !== 0), onChange: handleChangeEvent, onBlur: handleBlur, onFocus: handleFocus, onKeyDown: handleKeyDown, onKeyUp: handleKeyUp, onClick: handleClick, onMouseDown: handleMouseDown, onMouseUp: handleMouseUp, onPointerDown: handlePointerDown, onPointerUp: handlePointerUp, "data-testid": "ATL-InputTime-input", "aria-label": props["aria-label"], "aria-describedby": descriptionVisible ? descriptionIdentifier : props["aria-describedby"], "aria-invalid": isInvalid ? true : undefined, "aria-required": props["aria-required"] }, dataAttrs))));
}
function useInputTimeRefs(inputRef) {
    const internalRef = useRef(null);
    const mergedRef = mergeRefs([internalRef, inputRef]);
    const wrapperRef = useRef(null);
    return { internalRef, mergedRef, wrapperRef };
}

function InputTime$1(_a) {
    var { defaultValue, value, onChange } = _a, params = __rest(_a, ["defaultValue", "value", "onChange"]);
    const ref = useRef(null);
    const wrapperRef = useRef(null);
    const { setTypedTime } = useTimePredict({ value, handleChange });
    const fieldProps = omit(Object.assign(Object.assign(Object.assign({ onChange: handleChange }, (defaultValue && { defaultValue: dateToTimeString(defaultValue) })), (!defaultValue && { value: dateToTimeString(value) })), params), ["version"]);
    return (React__default.createElement(FormField, Object.assign({ inputRef: ref, type: "time" }, fieldProps, { onBlur: handleBlur, onKeyUp: e => {
            var _a;
            (_a = fieldProps.onKeyUp) === null || _a === void 0 ? void 0 : _a.call(fieldProps, e);
            !isNaN(parseInt(e.key, 10)) && setTypedTime(prev => prev + e.key);
        }, wrapperRef: wrapperRef })));
    function handleChange(newValue) {
        onChange === null || onChange === void 0 ? void 0 : onChange(timeStringToDate(newValue, value));
    }
    function handleBlur() {
        var _a;
        (_a = params.onBlur) === null || _a === void 0 ? void 0 : _a.call(params);
        // Time inputs doesn't clear the typed value when it's invalid. This should
        // force it to reset the input value when the typed value is invalid.
        if (ref.current) {
            if (!ref.current.checkValidity()) {
                ref.current.value = "";
            }
        }
    }
}

function isNewInputTimeProps(props) {
    return props.version === 2;
}
function InputTime(props) {
    if (isNewInputTimeProps(props)) {
        return React__default.createElement(InputTimeRebuilt, Object.assign({}, props));
    }
    else {
        return React__default.createElement(InputTime$1, Object.assign({}, props));
    }
}

export { InputTime };
