import { __assign, __rest } from "tslib";
import { CanonicalizeLocaleList, CanonicalizeTimeZoneName, IsValidTimeZoneName, OrdinaryHasInstance, SupportedLocales, ToNumber, defineProperty, invariant, } from '@formatjs/ecma402-abstract';
import { FormatDateTime } from './abstract/FormatDateTime';
import { FormatDateTimeRange } from './abstract/FormatDateTimeRange';
import { FormatDateTimeRangeToParts } from './abstract/FormatDateTimeRangeToParts';
import { FormatDateTimeToParts } from './abstract/FormatDateTimeToParts';
import { InitializeDateTimeFormat } from './abstract/InitializeDateTimeFormat';
import { parseDateTimeSkeleton } from './abstract/skeleton';
import { DATE_TIME_PROPS } from './abstract/utils';
import links from './data/links';
import getInternalSlots from './get_internal_slots';
import { unpack } from './packer';
var UPPERCASED_LINKS = Object.keys(links).reduce(function (all, l) {
    all[l.toUpperCase()] = links[l];
    return all;
}, {});
var RESOLVED_OPTIONS_KEYS = [
    'locale',
    'calendar',
    'numberingSystem',
    'dateStyle',
    'timeStyle',
    'timeZone',
    'hourCycle',
    'weekday',
    'era',
    'year',
    'month',
    'day',
    'hour',
    'minute',
    'second',
    'timeZoneName',
];
var formatDescriptor = {
    enumerable: false,
    configurable: true,
    get: function () {
        if (typeof this !== 'object' ||
            !OrdinaryHasInstance(DateTimeFormat, this)) {
            throw TypeError('Intl.DateTimeFormat format property accessor called on incompatible receiver');
        }
        var internalSlots = getInternalSlots(this);
        // eslint-disable-next-line @typescript-eslint/no-this-alias
        var dtf = this;
        var boundFormat = internalSlots.boundFormat;
        if (boundFormat === undefined) {
            // https://tc39.es/proposal-unified-intl-numberformat/section11/numberformat_diff_out.html#sec-number-format-functions
            boundFormat = function (date) {
                var x;
                if (date === undefined) {
                    x = Date.now();
                }
                else {
                    x = Number(date);
                }
                return FormatDateTime(dtf, x, {
                    getInternalSlots: getInternalSlots,
                    localeData: DateTimeFormat.localeData,
                    tzData: DateTimeFormat.tzData,
                    getDefaultTimeZone: DateTimeFormat.getDefaultTimeZone,
                });
            };
            try {
                // https://github.com/tc39/test262/blob/master/test/intl402/NumberFormat/prototype/format/format-function-name.js
                Object.defineProperty(boundFormat, 'name', {
                    configurable: true,
                    enumerable: false,
                    writable: false,
                    value: '',
                });
            }
            catch (e) {
                // In older browser (e.g Chrome 36 like polyfill-fastly.io)
                // TypeError: Cannot redefine property: name
            }
            internalSlots.boundFormat = boundFormat;
        }
        return boundFormat;
    },
};
try {
    // https://github.com/tc39/test262/blob/master/test/intl402/NumberFormat/prototype/format/name.js
    Object.defineProperty(formatDescriptor.get, 'name', {
        configurable: true,
        enumerable: false,
        writable: false,
        value: 'get format',
    });
}
catch (e) {
    // In older browser (e.g Chrome 36 like polyfill-fastly.io)
    // TypeError: Cannot redefine property: name
}
export var DateTimeFormat = function (locales, options) {
    // Cannot use `new.target` bc of IE11 & TS transpiles it to something else
    if (!this || !OrdinaryHasInstance(DateTimeFormat, this)) {
        return new DateTimeFormat(locales, options);
    }
    InitializeDateTimeFormat(this, locales, options, {
        tzData: DateTimeFormat.tzData,
        uppercaseLinks: UPPERCASED_LINKS,
        availableLocales: DateTimeFormat.availableLocales,
        relevantExtensionKeys: DateTimeFormat.relevantExtensionKeys,
        getDefaultLocale: DateTimeFormat.getDefaultLocale,
        getDefaultTimeZone: DateTimeFormat.getDefaultTimeZone,
        getInternalSlots: getInternalSlots,
        localeData: DateTimeFormat.localeData,
    });
    /** IMPL START */
    var internalSlots = getInternalSlots(this);
    var dataLocale = internalSlots.dataLocale;
    var dataLocaleData = DateTimeFormat.localeData[dataLocale];
    invariant(dataLocaleData !== undefined, "Cannot load locale-dependent data for ".concat(dataLocale, "."));
    /** IMPL END */
};
// Static properties
defineProperty(DateTimeFormat, 'supportedLocalesOf', {
    value: function supportedLocalesOf(locales, options) {
        return SupportedLocales(DateTimeFormat.availableLocales, CanonicalizeLocaleList(locales), options);
    },
});
defineProperty(DateTimeFormat.prototype, 'resolvedOptions', {
    value: function resolvedOptions() {
        if (typeof this !== 'object' ||
            !OrdinaryHasInstance(DateTimeFormat, this)) {
            throw TypeError('Method Intl.DateTimeFormat.prototype.resolvedOptions called on incompatible receiver');
        }
        var internalSlots = getInternalSlots(this);
        var ro = {};
        for (var _i = 0, RESOLVED_OPTIONS_KEYS_1 = RESOLVED_OPTIONS_KEYS; _i < RESOLVED_OPTIONS_KEYS_1.length; _i++) {
            var key = RESOLVED_OPTIONS_KEYS_1[_i];
            var value = internalSlots[key];
            if (key === 'hourCycle') {
                var hour12 = value === 'h11' || value === 'h12'
                    ? true
                    : value === 'h23' || value === 'h24'
                        ? false
                        : undefined;
                if (hour12 !== undefined) {
                    ro.hour12 = hour12;
                }
            }
            if (DATE_TIME_PROPS.indexOf(key) > -1) {
                if (internalSlots.dateStyle !== undefined ||
                    internalSlots.timeStyle !== undefined) {
                    value = undefined;
                }
            }
            if (value !== undefined) {
                ro[key] = value;
            }
        }
        return ro;
    },
});
defineProperty(DateTimeFormat.prototype, 'formatToParts', {
    value: function formatToParts(date) {
        if (date === undefined) {
            date = Date.now();
        }
        else {
            date = ToNumber(date);
        }
        return FormatDateTimeToParts(this, date, {
            getInternalSlots: getInternalSlots,
            localeData: DateTimeFormat.localeData,
            tzData: DateTimeFormat.tzData,
            getDefaultTimeZone: DateTimeFormat.getDefaultTimeZone,
        });
    },
});
defineProperty(DateTimeFormat.prototype, 'formatRangeToParts', {
    value: function formatRangeToParts(startDate, endDate) {
        var dtf = this;
        if (typeof dtf !== 'object') {
            throw new TypeError();
        }
        if (startDate === undefined || endDate === undefined) {
            throw new TypeError('startDate/endDate cannot be undefined');
        }
        var x = ToNumber(startDate);
        var y = ToNumber(endDate);
        return FormatDateTimeRangeToParts(dtf, x, y, {
            getInternalSlots: getInternalSlots,
            localeData: DateTimeFormat.localeData,
            tzData: DateTimeFormat.tzData,
            getDefaultTimeZone: DateTimeFormat.getDefaultTimeZone,
        });
    },
});
defineProperty(DateTimeFormat.prototype, 'formatRange', {
    value: function formatRange(startDate, endDate) {
        var dtf = this;
        if (typeof dtf !== 'object') {
            throw new TypeError();
        }
        if (startDate === undefined || endDate === undefined) {
            throw new TypeError('startDate/endDate cannot be undefined');
        }
        var x = ToNumber(startDate);
        var y = ToNumber(endDate);
        return FormatDateTimeRange(dtf, x, y, {
            getInternalSlots: getInternalSlots,
            localeData: DateTimeFormat.localeData,
            tzData: DateTimeFormat.tzData,
            getDefaultTimeZone: DateTimeFormat.getDefaultTimeZone,
        });
    },
});
var DEFAULT_TIMEZONE = 'UTC';
DateTimeFormat.__setDefaultTimeZone = function (timeZone) {
    if (timeZone !== undefined) {
        timeZone = String(timeZone);
        if (!IsValidTimeZoneName(timeZone, {
            zoneNamesFromData: Object.keys(DateTimeFormat.tzData),
            uppercaseLinks: UPPERCASED_LINKS,
        })) {
            throw new RangeError('Invalid timeZoneName');
        }
        timeZone = CanonicalizeTimeZoneName(timeZone, {
            zoneNames: Object.keys(DateTimeFormat.tzData),
            uppercaseLinks: UPPERCASED_LINKS,
        });
    }
    else {
        timeZone = DEFAULT_TIMEZONE;
    }
    DateTimeFormat.__defaultTimeZone = timeZone;
};
DateTimeFormat.relevantExtensionKeys = ['nu', 'ca', 'hc'];
DateTimeFormat.__defaultTimeZone = DEFAULT_TIMEZONE;
DateTimeFormat.getDefaultTimeZone = function () { return DateTimeFormat.__defaultTimeZone; };
DateTimeFormat.__addLocaleData = function __addLocaleData() {
    var data = [];
    for (var _i = 0; _i < arguments.length; _i++) {
        data[_i] = arguments[_i];
    }
    var _loop_1 = function (d, locale) {
        var dateFormat = d.dateFormat, timeFormat = d.timeFormat, dateTimeFormat = d.dateTimeFormat, formats = d.formats, intervalFormats = d.intervalFormats, rawData = __rest(d, ["dateFormat", "timeFormat", "dateTimeFormat", "formats", "intervalFormats"]);
        var processedData = __assign(__assign({}, rawData), { dateFormat: {
                full: parseDateTimeSkeleton(dateFormat.full),
                long: parseDateTimeSkeleton(dateFormat.long),
                medium: parseDateTimeSkeleton(dateFormat.medium),
                short: parseDateTimeSkeleton(dateFormat.short),
            }, timeFormat: {
                full: parseDateTimeSkeleton(timeFormat.full),
                long: parseDateTimeSkeleton(timeFormat.long),
                medium: parseDateTimeSkeleton(timeFormat.medium),
                short: parseDateTimeSkeleton(timeFormat.short),
            }, dateTimeFormat: {
                full: parseDateTimeSkeleton(dateTimeFormat.full).pattern,
                long: parseDateTimeSkeleton(dateTimeFormat.long).pattern,
                medium: parseDateTimeSkeleton(dateTimeFormat.medium).pattern,
                short: parseDateTimeSkeleton(dateTimeFormat.short).pattern,
            }, formats: {} });
        var _loop_2 = function (calendar) {
            processedData.formats[calendar] = Object.keys(formats[calendar]).map(function (skeleton) {
                return parseDateTimeSkeleton(skeleton, formats[calendar][skeleton], intervalFormats[skeleton], intervalFormats.intervalFormatFallback);
            });
        };
        for (var calendar in formats) {
            _loop_2(calendar);
        }
        var minimizedLocale = new Intl.Locale(locale)
            .minimize()
            .toString();
        DateTimeFormat.localeData[locale] = DateTimeFormat.localeData[minimizedLocale] = processedData;
        DateTimeFormat.availableLocales.add(locale);
        DateTimeFormat.availableLocales.add(minimizedLocale);
        if (!DateTimeFormat.__defaultLocale) {
            DateTimeFormat.__defaultLocale = minimizedLocale;
        }
    };
    for (var _a = 0, data_1 = data; _a < data_1.length; _a++) {
        var _b = data_1[_a], d = _b.data, locale = _b.locale;
        _loop_1(d, locale);
    }
};
Object.defineProperty(DateTimeFormat.prototype, 'format', formatDescriptor);
DateTimeFormat.__defaultLocale = '';
DateTimeFormat.localeData = {};
DateTimeFormat.availableLocales = new Set();
DateTimeFormat.getDefaultLocale = function () {
    return DateTimeFormat.__defaultLocale;
};
DateTimeFormat.polyfilled = true;
DateTimeFormat.tzData = {};
DateTimeFormat.__addTZData = function (d) {
    DateTimeFormat.tzData = unpack(d);
};
try {
    if (typeof Symbol !== 'undefined') {
        Object.defineProperty(DateTimeFormat.prototype, Symbol.toStringTag, {
            value: 'Intl.DateTimeFormat',
            writable: false,
            enumerable: false,
            configurable: true,
        });
    }
    Object.defineProperty(DateTimeFormat.prototype.constructor, 'length', {
        value: 1,
        writable: false,
        enumerable: false,
        configurable: true,
    });
}
catch (e) {
    // Meta fix so we're test262-compliant, not important
}
