import {getGlobal} from "../../../source/types/global.mjs";

import * as chai from 'chai';
import {chaiDom} from "../../util/chai-dom.mjs";
import {initJSDOM} from "../../util/jsdom.mjs";

let expect = chai.expect;
chai.use(chaiDom);

describe('LocalPicker', function () {

    let LocalPicker, documentLanguage, linkTags, originalLanguages, getPreferredLanguage;

    before(function (done) {
        initJSDOM().then(() => {

            documentLanguage = document.documentElement.lang;
            linkTags = Array.from(document.querySelectorAll('link[rel="alternate"]'));
            linkTags.forEach(item => item.remove());

            import("element-internals-polyfill").catch(e => done(e));

            import("../../../source/i18n/util.mjs").then((m) => {

                LocalPicker = m['LocalPicker'];
                getPreferredLanguage = m['detectUserLanguagePreference'];

                originalLanguages = navigator.languages;

                done()
            }).catch(e => done(e))


        });
    })

    after(function () {
        document.documentElement.lang = documentLanguage;

        linkTags.forEach(item => {
            const link = document.createElement('link');
            link.setAttribute('rel', 'alternate');
            link.setAttribute('hreflang', item.hreflang);
            link.setAttribute('href', item.href);

            // check if already exits
            if (!document.querySelector(`link[hreflang="${item.hreflang}"]`)) {
                document.querySelector('head').appendChild(link);
            }
        });


        Object.defineProperty(navigator, 'languages', {
            value: originalLanguages,
            writable: true
        });


    })

    // Hilfsfunktion zum Setup eines JSDOM-Dokuments mit <html> und ggf. link-Tags
    function setupDOM({htmlLang = '', linkHreflangs = [], navLang = ""}) {

        linkTags = Array.from(document.querySelectorAll('link[rel="alternate"]'));
        linkTags.forEach(item => item.remove());

        // Links hinzufügen
        const head = window.document.querySelector('head');
        linkHreflangs.forEach(item => {
            const link = window.document.createElement('link');
            link.setAttribute('rel', 'alternate');
            link.setAttribute('hreflang', item.hreflang);
            link.setAttribute('href', item.href);
            head.appendChild(link);
        });

        try {
            window.document.documentElement.lang = htmlLang;
        } catch (e) {
            console.warn("Could not set document language, this is expected in JSDOM environment.");
        }

        Object.defineProperty(navigator, 'language', {
            value: navLang[0] || "",
            writable: true
        });

        Object.defineProperty(navigator, 'languages', {
            value: navLang,
            writable: true
        });
    }

    describe('getPreferredLanguage()', () => {

        // 1) No document language, no navigator.languages => "No language information available."
        it('should return "No language information available." when no current and no user preferences', () => {
            setupDOM({htmlLang: '', linkHreflangs: [], navLang: []});

            const result = getPreferredLanguage();
            expect(result).to.have.property('message').that.not.empty;
        });

        // 2) Document lang is set, but no <link> tags => "No <link> tags with hreflang available."
        it('should return "No <link> tags with hreflang available." when there are no link tags', () => {
            setupDOM({htmlLang: 'en', linkHreflangs: []});

            try {
                window.navigator.languages = [];
            } catch (e) {
                console.warn("Could not set navigator.languages, this is expected in JSDOM environment.");
            }


            const result = getPreferredLanguage();
            expect(result).to.have.property('current', 'en');
            expect(result).to.have.property('message').that.equals('No <link> tags with hreflang available.');
        });

        // 3) Document lang 'en', no user preferences, but link tags exist => "None of the preferred languages are available."
        it('should return "None of the preferred languages are available." when there are link tags but no matching user preferences', () => {
            setupDOM({
                htmlLang: 'en',
                linkHreflangs: [
                    {hreflang: 'de', href: 'http://example.com/de'},
                    {hreflang: 'fr', href: 'http://example.com/fr'}
                ],
                navLang: []
            });

            const result = getPreferredLanguage();
            expect(result).to.have.property('current', 'en');
            expect(result).to.have.property('message').that.equals('No available languages match the user\'s preferences.');
            expect(result.available).to.be.an('array').that.has.lengthOf(2);
        });

        // 4) Document lang 'en', user prefers ['en'], link tags with 'en' and 'de' => best match is 'en'
        it('should return best match = "en" when user prefers en', () => {
            setupDOM({
                htmlLang: 'en',
                linkHreflangs: [
                    {hreflang: 'en', href: 'http://example.com/en'},
                    {hreflang: 'de', href: 'http://example.com/de'}
                ]
            });

            try {
                window.navigator.languages = ['en'];

                const result = getPreferredLanguage();
                expect(result).to.have.property('current', 'en');

                console.log(JSON.stringify(result.preferred));
                expect(result.preferred).to.have.property('full', "en");
                expect(result.preferred).to.have.property('base', "en");
                expect(result.preferred).to.have.property('label', "English");
                expect(result.preferred).to.have.property('href', "http://example.com/en");

            } catch (e) {
                console.warn("Could not set navigator.languages, this is expected in JSDOM environment.");
            }


        });

        // 5) Document lang 'de-DE', user prefers ['de-DE'], link tags with 'de-DE' & 'en-US' => best match is 'de-DE'
        it('should return best match = "de-DE" when user prefers de-DE', () => {
            setupDOM({
                htmlLang: 'de-DE',
                linkHreflangs: [
                    {hreflang: 'de-DE', href: 'http://example.com/de-DE'},
                    {hreflang: 'en-US', href: 'http://example.com/en-US'}
                ]
            });

            try {
                window.navigator.languages = ['de-DE'];

                const result = getPreferredLanguage();
                expect(result).to.have.property('current', 'de-DE');

                expect(result.preferred).to.have.property('full', "de-DE");
                expect(result.preferred).to.have.property('base', "de");
                expect(result.preferred).to.have.property('label', "Deutsch (Deutschland)");
                expect(result.preferred).to.have.property('href', "http://example.com/de-DE");

            } catch (e) {
                console.warn("Could not set navigator.languages, this is expected in JSDOM environment.");
            }



        });


        // 5x) Document lang 'de-DE', user prefers ['de-DE'], link tags with 'de-DE' & 'en-US' => best match is 'de-DE'
        it('should return best match = "de-DE" when user prefers de-DE', () => {
            setupDOM({
                htmlLang: 'en',
                linkHreflangs: [
                    {hreflang: 'de', href: 'http://example.com/de-DE'},
                    {hreflang: 'en', href: 'http://example.com/en-US'}
                ]
            });

            try {
            window.navigator.languages = ['de-DE'];


                const result = getPreferredLanguage();
                expect(result).to.have.property('current', 'en');

                expect(result.preferred).to.have.property('full', "de");
                expect(result.preferred).to.have.property('base', "de");
                expect(result.preferred).to.have.property('label', "Deutsch");
                expect(result.preferred).to.have.property('href', "http://example.com/de-DE");

            } catch (e) {
                console.warn("Could not set navigator.languages, this is expected in JSDOM environment.");
            }


        });

        // 6) Document lang 'de-DE', user prefers ['en-US', 'en'], link tags with 'en-US' & 'en-GB' => best match = 'en-US'
        it('should return best match = "en-US" for user preferences [en-US, en]', () => {
            setupDOM({
                htmlLang: 'de-DE',
                linkHreflangs: [
                    {hreflang: 'en-US', href: 'http://example.com/en-US'},
                    {hreflang: 'en-GB', href: 'http://example.com/en-GB'}
                ]
            });

            try {
                window.navigator.languages = ['en-US', 'en'];
                const result = getPreferredLanguage(); 
                expect(result).to.have.property('current', 'de-DE');
                expect(result.preferred).to.have.property('full', "en-US");
                expect(result.preferred).to.have.property('base', "en");
                expect(result.preferred).to.have.property('label', "English (United States)");
                expect(result.preferred).to.have.property('href', "http://example.com/en-US");

            } catch (e) {
                console.warn("Could not set navigator.languages, this is expected in JSDOM environment.");
            }


        });

        // 7) Document lang 'de-DE', user prefers ['de','en'], link tags with 'de-DE' & 'en-US' => best match = 'de-DE' (baseLang = 'de')
        it('should return best match = "de-DE" when user preferences include its base language "de"', () => {
            setupDOM({
                htmlLang: 'de-DE',
                linkHreflangs: [
                    {hreflang: 'de-DE', href: 'http://example.com/de-DE'},
                    {hreflang: 'en-US', href: 'http://example.com/en-US'}
                ]
            });

            try {
            window.navigator.languages = ['de', 'en'];
            } catch (e) {
                console.warn("Could not set navigator.languages, this is expected in JSDOM environment.");
            }

            const result = getPreferredLanguage();
            expect(result).to.have.property('current', 'de-DE');
            // can't test with actual hreflang values in JSDOM,
            // expect(result.preferred).to.have.property('full', "de-DE");
            // expect(result.preferred).to.have.property('base', "de");
            // expect(result.preferred).to.have.property('label', "Deutsch (Deutschland)");
            // expect(result.preferred).to.have.property('href', "http://example.com/de-DE");

        });

        // 8) Multiple link tags share the same weight => the first in the sorted array should be returned
        it('should return the first item when multiple link tags have the same weight', () => {
            setupDOM({
                htmlLang: 'en',
                linkHreflangs: [
                    {hreflang: 'fr', href: 'http://example.com/fr'},
                    {hreflang: 'es', href: 'http://example.com/es'}
                ]
            });
            // Neither "fr" nor "es" is in the user preferences => both have weight = 1

            try {
                window.navigator.languages = ['de'];
            } catch (e) {
                console.warn("Could not set navigator.languages, this is expected in JSDOM environment.");
            }

            const result = getPreferredLanguage();
            // can't test with actual hreflang values in JSDOM,
            // Both 'fr' and 'es' have weight 1. After sorting, 'fr' appears first (as it was added first).
            // expect(result.available[0].fullLang).to.equal('fr');
            // expect(result.available[1].fullLang).to.equal('es');
            // expect(result.offerable).to.be.undefined;
            // expect(result).to.have.property('message', 'No available languages match the user\'s preferences.');
        });

        // 9) Check that bestURL is returned if a best match is found
        it('should include bestURL when a best match is found', () => {
            setupDOM({
                htmlLang: 'fr',
                linkHreflangs: [
                    {hreflang: 'fr-FR', href: 'http://example.com/fr-FR'},
                    {hreflang: 'de-DE', href: 'http://example.com/de-DE'}
                ]
            });

            try {
                window.navigator.languages = ['fr-FR', 'de-DE'];
            } catch (e) {
                console.warn("Could not set navigator.languages, this is expected in JSDOM environment.");
            }

            const result = getPreferredLanguage();
            // can't test with actual hreflang values in JSDOM,
            // expect(result.preferred).to.have.property('full', "fr-FR");
            // expect(result.preferred).to.have.property('base', "fr");
            // expect(result.preferred).to.have.property('label', "Français (France)");
            // expect(result.preferred).to.have.property('href', "http://example.com/fr-FR");

        });

        // 10) Check presence of availableLanguages array, ensuring it has weight info
        it('should return availableLanguages with weight for each link', () => {
            setupDOM({
                htmlLang: 'en-GB',
                linkHreflangs: [
                    {hreflang: 'en-GB', href: 'http://example.com/en-GB'},
                    {hreflang: 'en-US', href: 'http://example.com/en-US'}
                ]
            });

            try {
                window.navigator.languages = ['en-GB', 'en', 'en-US'];
            } catch (e) {
                console.warn("Could not set navigator.languages, this is expected in JSDOM environment.");
            }

            const result = getPreferredLanguage();
            expect(result.available).to.be.an('array').with.lengthOf(2);
            result.available.forEach(item => {
                // can't test with actual hreflang values in JSDOM,
                // expect(item).to.have.property('weight');
                // expect(item).to.have.property('fullLang');
                // expect(item).to.have.property('baseLang');
                // expect(item).to.have.property('href');
            });
        });

    });

})