/**
 * @typedef {'Y' | 'N'} ExactFigure
 * @typedef {ExactFigure | 'indeterminate'} Figure
 * @typedef {Record<string, Figure>} LanguageFigureMapping
 */

/**
 * @param {string} lang
 * @return {HTMLButtonElement}
 */
function createButton(lang) {
    const btn = document.createElement('button');
    btn.type = 'button';
    btn.dataset.lang = lang;
    btn.innerText = lang;

    return btn;
}

/**
 * @param {string} lang
 * @param {string|undefined} namePrefix
 * @return {HTMLInputElement}
 */
function createInput(lang, namePrefix = undefined) {
    const ipt = document.createElement('input');
    ipt.type = 'hidden';
    ipt.name = `${namePrefix}[${lang}]`;
    ipt.dataset.lang = lang;

    return ipt;
}

/**
 * @param {Figure} figure
 * @param {ExactFigure|undefined} parentFigure
 * @return {string}
 */
function resolveFigureClassName(figure, parentFigure = undefined) {
    switch (figure) {
        case 'Y': return 'btn-success';
        case 'N': return 'btn-danger';
        case 'indeterminate':
            if (!parentFigure) {
                throw new Error('Parent figure is undefined');
            }
            return `${resolveFigureClassName(parentFigure)}-indeterminate`;
    }
}

export class WpjTranslationsFigure {
    /** @type {ExactFigure} */
    #parentFigure;

    /** @type {LanguageFigureMapping} */
    #initialState;

    /** @type {string} */
    inputNamePrefix;

    /** @type {HTMLButtonElement[]} */
    buttons = [];

    /** @type {Map<string, HTMLInputElement>} */
    inputs = new Map();

    /**
     * @param {string[]} languages
     * @param {string} inputNamePrefix
     * @param {ExactFigure} parentFigure
     */
    constructor(languages, inputNamePrefix, parentFigure = 'Y') {
        this.#initialState = Object.fromEntries(languages.map(l => [l, 'indeterminate']));
        this.inputNamePrefix = inputNamePrefix;
        this.#parentFigure = parentFigure;
    }

    /**
     * @param {HTMLElement} target
     */
    render(target) {
        for (const lang of this.languages) {
            const btn = createButton(lang);
            const ipt = createInput(lang, this.inputNamePrefix);
            ipt.value = this.#initialState[lang] ?? 'indeterminate';

            btn.insertAdjacentElement('beforeend', ipt);
            btn.addEventListener('click', this.handleValueChange.bind(this));

            this.buttons.push(btn);
            this.inputs.set(lang, ipt);

            target.insertAdjacentElement('beforeend', btn);
        }

        this.applyStyles();
    }

    /** @param {MouseEvent} ev */
    handleValueChange(ev) {
        const lang = ev.currentTarget.dataset.lang;
        const ipt = this.inputs.get(lang);

        switch (ipt.value) {
            case 'Y': ipt.value = 'N'; break;
            case 'N': ipt.value = 'indeterminate'; break;
            case 'indeterminate': ipt.value = 'Y'; break;
        }

        ipt.dispatchEvent(new Event('change'));
        window.somethingChanged = true;
        this.applyStyles();
    }

    applyStyles() {
        for (const btn of this.buttons) {
            const lang = btn.dataset.lang;
            const input = this.inputs.get(lang);
            if (!input) {
                return;
            }

            btn.className = 'btn ' + resolveFigureClassName(input.value, this.parentFigure);
        }
    }

    /** @param {ExactFigure} figure */
    set parentFigure(figure) {
        this.#parentFigure = figure;
        this.applyStyles();
    }

    get parentFigure() {
        return this.#parentFigure;
    }

    get languages() {
        return Object.keys(this.#initialState);
    }

    /** @param {string} language */
    valueOf(language) {
        return this.inputs.get(language)?.value;
    }

    get values() {
        return Object.fromEntries([...this.inputs.entries()].map(([k, v]) => [k, v.value]));
    }

    /**
     * @param {LanguageFigureMapping} state
     */
    setState(state) {
        for (const lang in state) {
            if (!(lang in this.#initialState)) {
                continue;
            }

            this.#initialState[lang] = state[lang];
        }
    }

    toggleParentFigure() {
        return this.parentFigure = this.parentFigure === 'Y' ? 'N' :'Y';
    }
}

$(() => {
    wpj.TranslationsFigure = WpjTranslationsFigure;
});
