import { I18nLoadedEvent } from "./i18nLoadedEvent.js";

/**
 * @class Translator - Manages loading and applying translations for the application.
 * @property {string} prefferedLanguage - The preferred language code.
 *
 * Expects translation files to be structured as JSON files for each language, more specifically:
 * <key>: <value>,
 * <key.attribute>: <value>,
 * <category>: {
 *    <subkey>: <subvalue>
 * }
 * to be referenced in HTML as:
 * <element data-i18n="key"></element>
 * <element data-i18n="category.subkey"></element>
 *
 * (Attributes behave as though they are subkeys.)
 */

class Translator {
    constructor() {
        this.setPreferredLanguage(this.determinePreferredLanguage());
    }

    /**
     * Determine the preferred language of the visitor.
     * @returns {string}
     */
    determinePreferredLanguage() {
        const queryLanguage = new URLSearchParams(window.location.search).get('lang');
        const storedLanguage = localStorage.getItem('preferredLanguage');
        const browserLanguages = navigator.languages;
        const browserLanguage = navigator.language;
        const usedLanguage = queryLanguage || storedLanguage || browserLanguages[0] || browserLanguage;
        return usedLanguage.split('-')[0];
    }

    /**
     * Set the preferred language and reload translations.
     * @param language {string} - Language code to set as preferred.
     */
    setPreferredLanguage(language) {
        if (!language) return;

        if (language === this.prefferedLanguage) {
            return;
        }

        this.prefferedLanguage = language;

        // Add language as query parameter
        const url = new URL(location.href);
        url.searchParams.set('lang', language);
        history.replaceState({}, '', url.toString());

        // Load language
        localStorage.setItem('preferredLanguage', language);
        this.load(language);
        this.updateLanguageTag();
    }

    /**
     * Load the translation file for the preferred language.
     * @param language {string|null} - Optional language code to override the preferred language.
     * @param rootElement {Node} - The root element to apply translations to.
     * @returns {Promise<void>}
     */
    async load(language = null, rootElement = document) {
        try {
            console.log(`Loading translations for language '${this.prefferedLanguage}' in '${rootElement}'`);
            const i18n = await fetch(`/i18n/${this.prefferedLanguage}.json`);
            console.debug(`i18n response: ${i18n.status} ${i18n.statusText}`);
            const translations = await i18n.json();
            this.translate(translations, rootElement);

            window.dispatchEvent(new I18nLoadedEvent(translations));
        } catch (error) {
            console.error(`Error loading translations for language: ${this.prefferedLanguage}`, error);
        }
    }

    translate(translations, rootElement) {
        rootElement.querySelectorAll('[data-i18n]')
            .forEach(element => this.translateElement(element, translations));
    }

    translateElement(element, translations) {
        const keys = element.dataset.i18n.split('.');
        const elementTranslations = keys.reduce((obj, key) => key in obj ? obj[key] : {}, translations);

        // No translation found
        if (!elementTranslations) {
            if (element.innerHTML.trim() === '') {
                element.innerHTML = `[i18n:${element.dataset.i18n}]`;
            }

            console.warn(`No translation found for key '${element.dataset.i18n}'`);
            return;
        }

        console.debug(`Translating '${element.dataset.i18n}': '${JSON.stringify(elementTranslations)}'`);

        // Simple string translation
        if (typeof elementTranslations === 'string') {
            element.innerHTML = elementTranslations;
            return;
        }

        // Attribute translations
        for (const [attribute, value] of Object.entries(elementTranslations)) {
            if (attribute === 'innerHTML') {
                element.innerHTML = value;
                continue;
            }

            console.debug(`Translating attribute '${attribute}': '${value}'`);
            element.setAttribute(attribute, value);
        }
    }

    updateLanguageTag() {
        if (document.documentElement.lang !== this.prefferedLanguage) {
            document.documentElement.lang = this.prefferedLanguage;
        }
    }
}

const translator = new Translator();

export { translator };
