// cspell:ignore DDTHH
import dayjs from 'dayjs';
import tz from 'dayjs/plugin/timezone';
import utc from 'dayjs/plugin/utc';
import { cap } from '../../utils';
import { CALENDAR_TYPES } from './Input/local';
import { getEndOfWeek, getQuarterForDateTimeZone, getStartOfWeek, getWeek, getWeekYear } from './Input/utils';
dayjs.extend(utc);
dayjs.extend(tz);
const unitsInMs = {
    year: 24 * 60 * 60 * 1000 * 365,
    month: (24 * 60 * 60 * 1000 * 365) / 12,
    day: 24 * 60 * 60 * 1000,
    hour: 60 * 60 * 1000,
    minute: 60 * 1000,
    second: 1000
};
const timeUnitsOrdered = [
    'year',
    'month',
    'day',
    'hour',
    'minute',
    'second'
];
const dateShort = {
    dateStyle: 'short'
};
const dateMedium = {
    dateStyle: 'medium'
};
const dateLong = {
    dateStyle: 'long'
};
const dateNumeric = {
    year: 'numeric',
    month: 'numeric',
    day: 'numeric'
};
const timeShort = {
    timeStyle: 'short'
};
const timeMedium = {
    timeStyle: 'medium'
};
const timeLong = {
    timeStyle: 'medium'
};
const timeNumeric = {
    hour: 'numeric',
    minute: 'numeric'
};
const dateTimeShort = {
    ...dateShort,
    ...timeShort
};
const dateTimeMedium = {
    ...dateMedium,
    ...timeShort
};
const dateTimeLong = {
    ...dateLong,
    ...timeMedium
};
const dateTimeNumeric = {
    ...dateNumeric,
    ...timeNumeric
};
const timeLongNumeric = {
    ...timeNumeric,
    second: 'numeric'
};
const dateTimeLongNumeric = {
    ...dateNumeric,
    ...timeLongNumeric
};
const formatMapping = {
    date: {
        short: dateShort,
        medium: dateMedium,
        long: dateLong,
        numeric: dateNumeric,
        'long-numeric': dateNumeric
    },
    datetime: {
        short: dateTimeShort,
        medium: dateTimeMedium,
        long: dateTimeLong,
        numeric: dateTimeNumeric,
        'long-numeric': dateTimeLongNumeric
    },
    datetime12: {
        short: { ...dateTimeShort, hour12: true },
        medium: { ...dateTimeMedium, hour12: true },
        long: { ...dateTimeLong, hour12: true },
        numeric: { ...dateTimeNumeric, hour12: true },
        'long-numeric': { ...dateTimeLongNumeric, hour12: true }
    },
    datetime24: {
        short: { ...dateTimeShort, hour12: false },
        medium: { ...dateTimeMedium, hour12: false },
        long: { ...dateTimeLong, hour12: false },
        numeric: { ...dateTimeNumeric, hour12: false },
        'long-numeric': { ...dateTimeLongNumeric, hour12: false }
    },
    time: {
        short: timeShort,
        medium: timeMedium,
        long: timeLong,
        numeric: timeNumeric,
        'long-numeric': timeLongNumeric
    },
    time12: {
        short: { ...timeShort, hour12: true },
        medium: { ...timeMedium, hour12: true },
        long: { ...timeLong, hour12: true },
        numeric: { ...timeNumeric, hour12: true },
        'long-numeric': { ...timeLongNumeric, hour12: true }
    },
    time24: {
        short: { ...timeShort, hour12: false },
        medium: { ...timeMedium, hour12: false },
        long: { ...timeLong, hour12: false },
        numeric: { ...timeNumeric, hour12: false },
        'long-numeric': { ...timeLongNumeric, hour12: false }
    },
    month: {
        short: { month: 'short' },
        medium: { month: 'short' },
        long: { month: 'long' },
        numeric: { month: 'numeric' },
        'long-numeric': { month: 'numeric' }
    },
    monthyear: {
        short: { year: 'numeric', month: 'short' },
        medium: { year: 'numeric', month: 'short' },
        long: { year: 'numeric', month: 'long' },
        numeric: { year: 'numeric', month: 'numeric' },
        'long-numeric': { year: 'numeric', month: 'numeric' }
    },
    week: {
        short: { month: 'short', day: 'numeric' },
        medium: { month: 'short', day: 'numeric' },
        long: { month: 'long', day: 'numeric' },
        numeric: { month: 'numeric', day: 'numeric' },
        'long-numeric': { month: 'numeric', day: 'numeric' }
    },
    year: {
        short: { year: 'numeric' },
        medium: { year: 'numeric' },
        long: { year: 'numeric' },
        numeric: { year: '2-digit' },
        'long-numeric': { year: '2-digit' }
    },
    relative: {
        short: { style: 'long', numeric: 'auto' },
        medium: { style: 'long', numeric: 'auto' },
        long: { style: 'long', numeric: 'auto' },
        numeric: { style: 'short', numeric: 'auto' },
        'long-numeric': { style: 'short', numeric: 'auto' }
    }
};
const dateTimeFormatCache = new Map();
export function getIntlDateTimeFormatFromCache(locale, options = {}) {
    const sortedOptions = JSON.stringify(options, Object.keys(options).sort());
    const key = `${locale}${sortedOptions}`;
    let formatter = dateTimeFormatCache.get(key);
    if (!formatter) {
        formatter = new Intl.DateTimeFormat(locale, options);
        dateTimeFormatCache.set(key, formatter);
    }
    return formatter;
}
const relativeTimeFormatCache = new Map();
const getIntlRelativeTimeFormatFromCache = (locale, options = {}) => {
    const sortedOptions = JSON.stringify(options, Object.keys(options).sort());
    const key = `${locale}${sortedOptions}`;
    let formatter = relativeTimeFormatCache.get(key);
    if (!formatter) {
        formatter = new Intl.RelativeTimeFormat(locale, options);
        relativeTimeFormatCache.set(key, formatter);
    }
    return formatter;
};
const displayNamesCache = {};
export function formatDateTime(value, { t, locale, format, variant, clockFormat, calendarType = CALENDAR_TYPES.ISO_8601, timeZone }) {
    const getFormattedDate = (date, dateVariant) => {
        const resolvedVariant = dateVariant === 'time' || dateVariant === 'datetime'
            ? `${dateVariant}${clockFormat ?? ''}`
            : dateVariant;
        const resolvedOptions = formatMapping[resolvedVariant][format];
        return getIntlDateTimeFormatFromCache(locale, {
            ...resolvedOptions,
            timeZone
        }).format(date);
    };
    const relativeTimeFormatter = (relativeValue, unit, enforceNumeric) => {
        const resolvedOptions = {
            ...formatMapping.relative[format],
            numeric: enforceNumeric ? 'always' : 'auto'
        };
        return getIntlRelativeTimeFormatFromCache(locale, resolvedOptions).format(relativeValue, unit);
    };
    const getRelativeTime = (date) => {
        const now = dayjs();
        const targetDate = dayjs(date);
        // Deliberately reduce precision to 1 minute, handles "now" cases
        const isWithinMinute = Math.abs(targetDate.diff(now, 'millisecond')) < 60000;
        if (isWithinMinute) {
            return relativeTimeFormatter(0, 'second', false);
        }
        // Find the most appropriate unit by checking dayjs diff for each unit
        for (const unit of timeUnitsOrdered) {
            const diff = targetDate.diff(now, unit);
            if (Math.abs(diff) >= 1) {
                return relativeTimeFormatter(diff, unit, true);
            }
        }
        // Fallback to seconds if no unit has a difference >= 1
        const secondsDiff = targetDate.diff(now, 'second');
        return relativeTimeFormatter(secondsDiff, 'second', true);
    };
    const getFormattedWeek = (date) => {
        displayNamesCache[locale] ??= new Intl.DisplayNames(locale, {
            style: 'long',
            type: 'dateTimeField'
        });
        const displayNames = displayNamesCache[locale];
        const startDate = getFormattedDate(getStartOfWeek(date, calendarType, timeZone), 'week');
        const endDate = getFormattedDate(getEndOfWeek(date, calendarType, timeZone), 'week');
        return `${cap(displayNames.of('weekOfYear'))} ${getWeek(date, calendarType, timeZone)}, ${getWeekYear(date, calendarType, timeZone)} (${startDate} - ${endDate})`;
    };
    const getFormattedQuarterYear = (date) => {
        const formattedYear = getFormattedDate(date, 'year');
        const quarterNumber = getQuarterForDateTimeZone(date, timeZone);
        return `${t(`date_quarter_q${quarterNumber}`, [formattedYear])}`;
    };
    switch (variant) {
        case 'relative':
            return getRelativeTime(value);
        case 'week':
            return getFormattedWeek(value);
        case 'quarteryear':
            return getFormattedQuarterYear(value);
        default:
            return getFormattedDate(value, variant);
    }
}
const dayInMs = 24 * 60 * 60 * 1000;
const hourInMs = 60 * 60 * 1000;
const minuteInMs = 60 * 1000;
const secondInMs = 1000;
const numberFormatCache = {};
export function formatDuration(valueInMs, { locale, significantUnits, unitDisplay = 'narrow', maxPrecision = 'second' }) {
    numberFormatCache[locale] ??= {};
    const sign = valueInMs < 0 ? '-' : '';
    const absMs = Math.abs(valueInMs);
    const durationParts = [
        { unit: 'day', value: Math.floor(absMs / dayInMs) },
        { unit: 'hour', value: Math.floor((absMs % dayInMs) / hourInMs) },
        { unit: 'minute', value: Math.floor((absMs % hourInMs) / minuteInMs) },
        { unit: 'second', value: Math.floor((absMs % minuteInMs) / secondInMs) },
        { unit: 'millisecond', value: absMs % secondInMs }
    ];
    const result = [];
    for (let i = 0; result.length < significantUnits && i < durationParts.length; i += 1) {
        const { value, unit } = durationParts[i];
        if (value > 0 || (unit === maxPrecision && result.length === 0)) {
            numberFormatCache[locale][unit] ??= {};
            numberFormatCache[locale][unit][unitDisplay] ??= new Intl.NumberFormat(locale, {
                style: 'unit',
                unit,
                unitDisplay
            });
            result.push(numberFormatCache[locale][unit][unitDisplay].format(value));
        }
        if (durationParts[i].unit === maxPrecision)
            break;
    }
    return `${sign}${result.join(' ')}`;
}
/**
 * @returns Current date and time value adjusted for local time zone.
 */
export const getAdjustedUTCDateTime = () => {
    const now = new Date();
    now.setTime(now.getTime() - now.getTimezoneOffset() * 60000);
    return now;
};
/**
 * @param timeZone - IANA timezone string (e.g. "America/New_York")
 * @returns Current date and time for the given timezone, with wall-clock time
 *          encoded in UTC fields — matching the convention used by DateTime inputs.
 */
export const getAdjustedUTCDateTimeForTimezone = (timeZone) => {
    try {
        // eslint-disable-next-line no-new
        new Intl.DateTimeFormat('en-US', { timeZone });
    }
    catch {
        return getAdjustedUTCDateTime();
    }
    return new Date(`${dayjs().tz(timeZone).format('YYYY-MM-DDTHH:mm:ss.SSS')}Z`);
};
//# sourceMappingURL=utils.js.map