var __assign = (this && this.__assign) || function () {
    __assign = Object.assign || function(t) {
        for (var s, i = 1, n = arguments.length; i < n; i++) {
            s = arguments[i];
            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                t[p] = s[p];
        }
        return t;
    };
    return __assign.apply(this, arguments);
};
var __rest = (this && this.__rest) || function (s, e) {
    var t = {};
    for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
        t[p] = s[p];
    if (s != null && typeof Object.getOwnPropertySymbols === "function")
        for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
            if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
                t[p[i]] = s[p[i]];
        }
    return t;
};
import { Clear } from '@procore/core-icons/dist';
import { isSameDay } from 'date-fns';
import React from 'react';
import { useEventListener } from '../../effects/EventListener';
import { useDateTime } from '../../state/DateTime';
import { useI18nContext } from '../../state/I18n';
import { maxMonth, maxYear, minYear, normalizeNewDate, } from '../../utils/CalendarHelpers';
import Button from '../Button';
import { StyledCalendar, StyledDateInput, StyledDateInputDelimiter, StyledDateInputIconContainer, StyledDateInputSegment, } from './DateInput.styles';
var segments = {
    day: {
        maxLength: 2,
        placeholder: 'dd',
    },
    month: {
        maxLength: 2,
        placeholder: 'mm',
    },
    year: {
        maxLength: 4,
        placeholder: 'yyyy',
    },
};
var dateInputLocales = {
    en: {
        segments: ['month', 'day', 'year'],
    },
    'en-CA': {
        segments: ['month', 'day', 'year'],
    },
    'fr-CA': {
        segments: ['day', 'month', 'year'],
    },
    'fr-FR': {
        segments: ['day', 'month', 'year'],
    },
    es: {
        segments: ['day', 'month', 'year'],
    },
    'es-ES': {
        segments: ['day', 'month', 'year'],
    },
    'en-AE': {
        segments: ['day', 'month', 'year'],
    },
    'en-AU': {
        segments: ['day', 'month', 'year'],
    },
    'en-GB': {
        segments: ['day', 'month', 'year'],
    },
    'en-SG': {
        segments: ['day', 'month', 'year'],
    },
    ko: {
        segments: ['year', 'month', 'day'],
    },
    'th-TH': {
        segments: ['day', 'month', 'year'],
    },
    pt: {
        segments: ['day', 'month', 'year'],
    },
    'is-IS': {
        segments: ['day', 'month', 'year'],
    },
    'de-DE': {
        delimiter: '.',
        segments: ['day', 'month', 'year'],
    },
    pseudo: {
        segments: ['day', 'month', 'year'],
    },
};
export function isValidYearRange(year) {
    return year > 1700 && year < 2122;
}
function noop() { }
function getLastDate(month, year) {
    return normalizeNewDate(year, Math.max(0, month), 0).getDate();
}
function getSegmentProps(onChangeSegment, type, dateInput, getAriaLabel) {
    var _a = segments[type], maxLength = _a.maxLength, placeholder = _a.placeholder;
    if (type === 'day') {
        return {
            'aria-label': getAriaLabel('day', dateInput.day),
            maxLength: maxLength,
            maxValue: getLastDate(dateInput.month, dateInput.year),
            minValue: 1,
            onChange: function (value) {
                dateInput.setDay(value);
                onChangeSegment(type, value);
            },
            placeholder: placeholder,
            value: dateInput.day,
        };
    }
    else if (type === 'month') {
        return {
            'aria-label': getAriaLabel('month', dateInput.month),
            maxLength: maxLength,
            maxValue: maxMonth,
            minValue: 1,
            onChange: function (value) {
                dateInput.setMonth(value);
                onChangeSegment(type, value);
            },
            placeholder: placeholder,
            value: dateInput.month,
        };
    }
    else {
        return {
            'aria-label': getAriaLabel('year', dateInput.year),
            maxLength: maxLength,
            maxValue: maxYear,
            minValue: minYear,
            onChange: function (value) {
                dateInput.setYear(value);
                onChangeSegment(type, value);
            },
            placeholder: placeholder,
            value: dateInput.year,
        };
    }
}
function setFocusTo(target) {
    return target && target.current && target.current.focus();
}
function focusTargetOrFirst() {
    var refs = [];
    for (var _i = 0; _i < arguments.length; _i++) {
        refs[_i] = arguments[_i];
    }
    return function handler(event) {
        if (event === void 0) { event = { target: null }; }
        var ref = refs.reduce(function (acc, ref) {
            return ref.current && ref.current === event.target ? ref : acc;
        }, refs[0]);
        setFocusTo(ref);
    };
}
function clampDay(day, month, year) {
    if (day > 0 && month > 0 && year) {
        var date = normalizeNewDate(year, month - 1, day);
        if (date.getMonth() !== month - 1) {
            return getLastDate(month, year);
        }
    }
    return day;
}
function useDateInput(_a) {
    var onChange = _a.onChange, value_ = _a.value, log = _a.log;
    var dateTime = useDateTime();
    // logging called too frequenlty if not in memo
    var value = React.useMemo(function () {
        return dateTime.shiftUtcToZonedTime(value_, log);
    }, [value_]);
    var _b = React.useState(value ? value.getDate() : -1), day = _b[0], setRawDay = _b[1];
    var _c = React.useState(value ? value.getMonth() + 1 : -1), month = _c[0], setRawMonth = _c[1];
    var _d = React.useState(value ? value.getFullYear() : -1), year = _d[0], setRawYear = _d[1];
    var hasValues = day >= 0 || month >= 0 || year >= 0;
    var isInvalid = day < 0 || month < 0 || year < 0;
    var setAll = function (day, month, year) {
        var clampedDay = clampDay(day, month, year);
        var date = clampedDay > 0 && month > 0 && year > 0
            ? normalizeNewDate(year, month - 1, clampedDay)
            : null;
        setRawDay(clampedDay);
        setRawMonth(month);
        setRawYear(year);
        // Call DateSelect.onChange API when a supported year range or deleted date
        // isValidYearRange protects shiftZonedTimeToUtc
        // when date is null, year should be -1. -1 means it was cleared, either by button or backspace key
        /**
         * - going from non-valid date to a valid date
         *    - is a valid year range
         * - going from one valid date to another, different, date
         *    - is a valid year range
         * - change to invalid date
         */
        var validDate = date && isValidYearRange(year);
        if ((!value && validDate) ||
            (value && validDate && !isSameDay(date, value)) ||
            (value && date === null)) {
            onChange(dateTime.shiftZonedTimeToUtc(date));
        }
    };
    var setDay = function (value) {
        setAll(value, month, year);
    };
    var setMonth = function (value) {
        setAll(day, value, year);
    };
    var setYear = function (value) {
        setAll(day, month, value);
    };
    var clear = function () {
        setAll(-1, -1, -1);
    };
    React.useEffect(function () {
        if (value) {
            setRawDay(value.getDate());
            setRawMonth(value.getMonth() + 1);
            setRawYear(value.getFullYear());
        }
        else if (value === undefined) {
            setRawDay(-1);
            setRawMonth(-1);
            setRawYear(-1);
        }
    }, [value]);
    return {
        clear: clear,
        day: day,
        hasValues: hasValues,
        month: month,
        setDay: setDay,
        setMonth: setMonth,
        setYear: setYear,
        year: year,
    };
}
function getSegmentText(_a) {
    var maxLength = _a.maxLength, placeholder = _a.placeholder, type = _a.type, value = _a.value;
    if (value < 0) {
        return placeholder;
    }
    if (type === 'year' && !isValidYearRange(value)) {
        return "".concat(value, "____").slice(0, 4);
    }
    return String(value).padStart(maxLength, '0');
}
var DateSegment = React.forwardRef(function DateSegment(_a, ref) {
    var _b = _a.disabled, disabled = _b === void 0 ? false : _b, maxLength = _a.maxLength, maxValue = _a.maxValue, minValue = _a.minValue, nextRef = _a.nextRef, _c = _a.onChange, onChange = _c === void 0 ? function (value) { } : _c, placeholder = _a.placeholder, prevRef = _a.prevRef, _d = _a.tabIndex, tabIndex = _d === void 0 ? 0 : _d, type = _a.type, _e = _a.value, value = _e === void 0 ? 0 : _e, props = __rest(_a, ["disabled", "maxLength", "maxValue", "minValue", "nextRef", "onChange", "placeholder", "prevRef", "tabIndex", "type", "value"]);
    var _f = React.useState(''), keyBuffer = _f[0], setKeyBuffer = _f[1];
    var contains = function (key, keys) { return keys.indexOf(key) >= 0; };
    var onKeyDown = function (event) {
        event.stopPropagation();
        var key = event.key;
        if (contains(key, ['Up', 'ArrowUp', 'Down', 'ArrowDown', 'Backspace'])) {
            event.preventDefault();
        }
        if (contains(key, ['Up', 'ArrowUp', 'Down', 'ArrowDown']) &&
            type === 'year' &&
            value === -1) {
            onChange(new Date().getFullYear());
        }
        else if (contains(key, ['Up', 'ArrowUp'])) {
            onChange(value + 1 > maxValue ? minValue : Math.max(1, value + 1));
        }
        else if (contains(key, ['Down', 'ArrowDown'])) {
            onChange(value - 1 < minValue ? maxValue : value - 1);
        }
        else if (contains(key, ['Left', 'ArrowLeft'])) {
            setFocusTo(prevRef);
        }
        else if (contains(key, ['Right', 'ArrowRight'])) {
            setFocusTo(nextRef);
        }
        else if (contains(key, ['Backspace', 'Delete'])) {
            setKeyBuffer('');
            onChange(-1);
            // the segment is currently empty, go to the previous segment
            if (value === -1) {
                setFocusTo(prevRef);
            }
        }
        else if (!isNaN(parseInt(key, 10))) {
            if (keyBuffer.length === 0) {
                // current buffer is empty, initialize it
                setKeyBuffer(key);
                onChange(parseInt(key, 10));
            }
            else {
                // current buffer has text, add to it
                var newBuffer = keyBuffer + key;
                setKeyBuffer(newBuffer);
                onChange(Math.min(parseInt(newBuffer, 10), maxValue));
            }
        }
    };
    React.useEffect(function () {
        if (keyBuffer.length >= maxLength ||
            (keyBuffer.length === 1 &&
                ((type === 'day' && value > 3) || (type === 'month' && value > 1)))) {
            setFocusTo(nextRef);
            // Needed for year to reset as it does not move focus
            setKeyBuffer('');
        }
    });
    return (React.createElement(StyledDateInputSegment, __assign({ ref: ref }, props, { "data-placeholder": value < 0, disabled: disabled, isYear: type === 'year', onBlur: function () {
            if (type === 'year' && value !== -1 && value < 100) {
                onChange(2000 + value);
            }
            setKeyBuffer('');
        }, onKeyDown: onKeyDown, tabIndex: disabled ? -1 : tabIndex }), getSegmentText({ value: value, placeholder: placeholder, type: type, maxLength: maxLength })));
});
export var DateInput = React.forwardRef(function DateInput(_a, ref) {
    var clearRef = _a.clearRef, disabled = _a.disabled, _b = _a.error, error = _b === void 0 ? false : _b, _c = _a.segmentRefs, segmentRefs = _c === void 0 ? {} : _c, variant = _a.variant, _d = _a.onChange, onChange = _d === void 0 ? noop : _d, _e = _a.onChangeSegment, onChangeSegment = _e === void 0 ? noop : _e, _f = _a.onClear, onClear = _f === void 0 ? noop : _f, _g = _a.tabIndex, tabIndex = _g === void 0 ? 0 : _g, value = _a.value, props = __rest(_a, ["clearRef", "disabled", "error", "segmentRefs", "variant", "onChange", "onChangeSegment", "onClear", "tabIndex", "value"]);
    var I18n = useI18nContext();
    var dateInputRef = ref;
    var segment1Ref = segmentRefs.segmentOne || React.createRef();
    var segment2Ref = segmentRefs.segmentTwo || React.createRef();
    var segment3Ref = segmentRefs.segmentThree || React.createRef();
    var _h = dateInputLocales[I18n.locale] || dateInputLocales['en'], _j = _h.delimiter, delimiter = _j === void 0 ? '/' : _j, segments = _h.segments;
    var dateTime = useDateTime();
    var dateInput = useDateInput({
        onChange: onChange,
        value: value,
        log: 'Display date',
    });
    var onClickClear = function (e) {
        setFocusTo(segment1Ref);
        dateInput.clear();
        onClear(e);
    };
    useEventListener({
        event: 'click',
        handler: focusTargetOrFirst(segment1Ref, segment2Ref, segment3Ref),
        scope: dateInputRef,
    });
    var getSegmentAriaLabel = function (type, value) {
        return value && value !== -1
            ? I18n.t(type, {
                value: value,
                scope: 'core.dateInput.segment.ariaLabel.withValue',
            })
            : I18n.t(type, {
                scope: 'core.dateInput.segment.ariaLabel.withoutValue',
            });
    };
    return (React.createElement(StyledDateInput, __assign({ "aria-label": value && isValidYearRange(dateInput.year)
            ? dateTime.format(dateTime.newDate(dateInput.year, 
            // @ts-ignore
            dateInput.month - 1, dateInput.day), 'weekday-date')
            : undefined, disabled: disabled || variant === 'disabled', error: error ||
            variant === 'error' ||
            // -1 means placeholder yyyy is shown
            (!isValidYearRange(dateInput.year) && dateInput.year !== -1), ref: dateInputRef }, props),
        React.createElement(DateSegment, __assign({ disabled: disabled, nextRef: segment2Ref, ref: segment1Ref, tabIndex: tabIndex, type: segments[0] }, getSegmentProps(onChangeSegment, segments[0], dateInput, getSegmentAriaLabel))),
        React.createElement(StyledDateInputDelimiter, { "aria-hidden": true, visible: Boolean(value) }, delimiter),
        React.createElement(DateSegment, __assign({ disabled: disabled, nextRef: segment3Ref, prevRef: segment1Ref, ref: segment2Ref, tabIndex: tabIndex, type: segments[1] }, getSegmentProps(onChangeSegment, segments[1], dateInput, getSegmentAriaLabel))),
        React.createElement(StyledDateInputDelimiter, { "aria-hidden": true, visible: Boolean(value) }, delimiter),
        React.createElement(DateSegment, __assign({ disabled: disabled, prevRef: segment2Ref, ref: segment3Ref, tabIndex: tabIndex, type: segments[2] }, getSegmentProps(onChangeSegment, segments[2], dateInput, getSegmentAriaLabel))),
        React.createElement(StyledDateInputIconContainer, null, dateInput.hasValues ? (React.createElement(Button, { "aria-label": I18n.t('core.dateInput.clearButton.ariaLabel'), onClick: onClickClear, ref: clearRef, size: "sm", variant: "tertiary", icon: React.createElement(Clear, { size: "sm" }), tabIndex: -1 })) : (React.createElement(StyledCalendar, null)))));
});
DateInput.displayName = 'DateInput';
export default DateInput;
//# sourceMappingURL=DateInput.js.map