//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------

WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");

const constructors = [Intl.Collator, Intl.NumberFormat, Intl.DateTimeFormat];

if (WScript.Platform.INTL_LIBRARY === "icu") {
    constructors.push(Intl.PluralRules);
}

testRunner.runTests([
    {
        name: "OSS-Fuzz #6657: stress uloc_forLanguageTag status code and parsed length on duplicate variant subtags",
        body() {
            function test(Ctor, locale) {
                assert.throws(() => new Ctor(locale), RangeError, `new Intl.${Ctor.name}("${locale}") should throw`);
            }

            function testWithVariants(variants) {
                for (const Ctor of constructors) {
                    for (let i = 0; i < variants.length; ++i) {
                        for (let k = 0; k < variants.length; ++k) {
                            for (let m = 0; m < variants.length; ++m) {
                                test(Ctor, `und-${variants[i]}-${variants[k]}-${variants[m]}`);
                                test(Ctor, `en-${variants[i]}-${variants[k]}-${variants[m]}`);
                                test(Ctor, `de-DE-${variants[i]}-${variants[k]}-${variants[m]}`);
                                test(Ctor, `zh-Hans-CN-${variants[i]}-${variants[k]}-${variants[m]}`);
                            }

                            test(Ctor, `und-${variants[i]}-${variants[k]}`);
                            test(Ctor, `en-${variants[i]}-${variants[k]}`);
                            test(Ctor, `de-DE-${variants[i]}-${variants[k]}`);
                            test(Ctor, `zh-Hans-CN-${variants[i]}-${variants[k]}`);
                        }
                    }
                }
            }

            if (WScript.Platform.INTL_LIBRARY === "icu") {
                testWithVariants(["gregory", "GREGORY", "gregORY"]);
                testWithVariants(["invalid", "INVALID", "invaLID"]);
            }
        }
    },
    {
        name: "OSS-Fuzz #7950: Have option getters redefine themselves",
        body() {
            if (WScript.Platform.INTL_LIBRARY === "winglob") {
                return;
            }

            function test(callback, optionName, optionValue, shouldCallSecondGetter) {
                const options = {};
                let calledSecondGetter = false;
                Object.defineProperty(options, optionName, {
                    get() {
                        Object.defineProperty(options, optionName, {
                            get() {
                                calledSecondGetter = true;
                                return undefined;
                            }
                        });

                        return optionValue;
                    },
                    configurable: true
                });

                callback(options);
                assert.areEqual(shouldCallSecondGetter, calledSecondGetter, "Second getter behavior was incorrect");
            }

            test((options) => assert.areEqual(1, new Intl.Collator("en-US", options).compare("A", "a")), "sensitivity", "case", false);
            test((options) => assert.areEqual(-1, new Intl.Collator("en-US", options).compare("A", "B")), "sensitivity", "case", false);
            test((options) => assert.areEqual(0, new Intl.Collator("en-US", options).compare("a", "\u00E2")), "sensitivity", "case", false);
            test((options) => assert.areEqual("1000", new Intl.NumberFormat("en-US", options).format(1000)), "useGrouping", false, false);
            test((options) => assert.areEqual("$1.50", new Intl.NumberFormat("en-US", Object.assign(options, { currency: "USD" })).format(1.5)), "style", "currency", false);

            // This was the original bug - at present, all browsers format the string as "" because the value returned by the second getter dictates format selection
            test((options) => assert.areEqual("", new Intl.DateTimeFormat("en-US", options).format()), "year", "numeric", true);
            test((options) => assert.areEqual("", new Intl.DateTimeFormat("en-US", options).format()), "minute", "numeric", true);
        }
    },
    {
        name: "Intl.FallbackSymbol behavior",
        body() {
            if (WScript.Platform.INTL_LIBRARY === "winglob") {
                return;
            }

            function testFallbackSymbol(Ctor, shouldHaveFallbackSymbol) {
                const objNew = new Ctor();
                const objCall = Ctor.call(objNew);
                const symbols = Object.getOwnPropertySymbols(objCall);
                assert.isTrue(objCall instanceof Ctor, `The given object should be an instance of ${Ctor.name}`);
                assert.areEqual(0, Object.getOwnPropertyNames(objCall).length, "Incorrect number of OwnPropertyNames");
                if (shouldHaveFallbackSymbol) {
                    assert.areEqual(1, symbols.length, "Incorrect number of OwnPropertySymbols");
                    const fallbackSymbol = symbols[0];
                    assert.areEqual("Symbol(Intl.FallbackSymbol)", fallbackSymbol.toString(), "Unexpected symbol description");
                    assert.areEqual("object", typeof objCall[fallbackSymbol], "objCall[fallbackSymbol] should be an object");
                    assert.isTrue(objCall[fallbackSymbol] instanceof Ctor, `objCall[fallbackSymbol] should be an instance of ${Ctor.name}`);

                    assert.throws(() => Ctor.call(objNew), TypeError, "Should not be able to legacy-construct an already-legacy-constructed Intl object (using original non-legacy new object)", "Cannot modify non-writable property 'Intl.FallbackSymbol'");
                    assert.throws(() => Ctor.call(objCall), TypeError, "Should not be able to legacy-construct an already-legacy-constructed Intl object (using legacy .call() object", "Cannot modify non-writable property 'Intl.FallbackSymbol'");

                    assert.areEqual(objNew, objCall, "Object created with .call should return `this`");
                } else {
                    assert.areEqual(0, symbols.length, "Incorrect number of OwnPropertySymbols");
                }
            }

            // only NumberFormat and DateTimeFormat should have Intl.FallbackSymbol behavior. PluralRules has no legacy construction behavior.
            testFallbackSymbol(Intl.Collator, false);
            testFallbackSymbol(Intl.NumberFormat, true);
            testFallbackSymbol(Intl.DateTimeFormat, true);
            assert.throws(() => Intl.PluralRules.call(new Intl.PluralRules()), TypeError, "Intl.PluralRules requires `new`");
        }
    }
], { verbose: false });
