// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.

/**
 * Auto-opens the side-by-side conflict resolution view when the grid page
 * was re-rendered after a rejected save (entry_conflict_service::detect()
 * found a real edit-vs-edit conflict on at least one comment field) — the
 * conflict data travels as a JSON blob in [data-region="kid-grid-student"]'s
 * data-conflict attribute (see eval_grid_page.php/stage_grid_page.php/
 * capp_grid_page.php's conflictjson + eval_grid.mustache and its two
 * siblings). Shared by all three domains, one module.
 *
 * Reuses core/modal (see amd/src/unsaved_guard.js's doc comment for why)
 * for the same accessible-by-default focus trap / Escape / backdrop-click /
 * return-focus behaviour, rather than the plugin's own ad hoc Comparateur
 * drawer pattern.
 *
 * Each conflicting field gets its own card with two resolution buttons;
 * resolving a card never re-submits by itself — only once every card is
 * resolved does the grid form silently re-submit itself (with a refreshed
 * expectedtimemodified/origin-value baseline that is now guaranteed to
 * match, since the last thing written to each field was either the fresh
 * DB value or an edit layered on top of it), completing the save the user
 * originally asked for.
 *
 * @module     local_kid/conflict_view
 * @copyright  2026 INSP
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
import Modal from 'core/modal';
import {get_strings as getStrings} from 'core/str';

const SELECTORS = {
    REGION: '[data-region="kid-grid-student"]',
};

/**
 * @param {String} text
 * @returns {String} HTML-escaped
 */
const esc = (text) => {
    const div = document.createElement('div');
    div.textContent = text ?? '';
    return div.innerHTML;
};

/**
 * @param {String} id a stable DOM-safe suffix (competency id, or 'global')
 * @param {Object} data {label, mine, db, author, authortime}
 * @param {String} subtitleauthor pre-built "modifié par X" string, or ''
 * @param {String} subtitlenoauthor pre-built fallback string
 * @param {String} colmine "Votre saisie"
 * @param {String} coldb "Version en base"
 * @param {String} btnmine "Conserver ma version"
 * @param {String} btndb "Garder la version en base"
 * @returns {String} HTML for one conflict card
 */
const renderCard = (id, data, subtitleauthor, subtitlenoauthor, colmine, coldb, btnmine, btndb) => {
    const authorline = data.author
        ? (data.authortime ? `${data.author} — ${data.authortime}` : data.author)
        : null;
    const subtitle = authorline
        ? subtitleauthor.replace('{$a}', esc(authorline))
        : subtitlenoauthor;
    return `
        <div class="kid-conflict-card" data-conflict-id="${esc(id)}">
            <h3 class="kid-conflict-card__title">${esc(data.label)}</h3>
            <p class="kid-conflict-card__subtitle">${subtitle}</p>
            <div class="kid-conflict-card__cols">
                <div class="kid-conflict-card__col">
                    <p class="kid-conflict-card__collabel">${esc(colmine)}</p>
                    <div class="kid-conflict-card__text">${esc(data.mine)}</div>
                </div>
                <div class="kid-conflict-card__col kid-conflict-card__col--db">
                    <p class="kid-conflict-card__collabel">${esc(coldb)}</p>
                    <div class="kid-conflict-card__text">${esc(data.db)}</div>
                </div>
            </div>
            <div class="kid-conflict-card__actions">
                <button type="button" class="btn btn-primary btn-sm" data-conflict-action="mine">${esc(btnmine)}</button>
                <button type="button" class="btn btn-secondary btn-sm" data-conflict-action="db">${esc(btndb)}</button>
            </div>
        </div>
    `;
};

/**
 * @param {Element} form the grid <form>
 * @param {String} fieldname e.g. results[12][commentaire] or commentaireglobal
 *   — always one of our own field names (never user input), safe to embed
 *   directly in the attribute-selector's quoted value.
 * @returns {Element|null}
 */
const field = (form, fieldname) => form.querySelector(`[name="${fieldname}"]`);

export const init = () => {
    const region = document.querySelector(SELECTORS.REGION);
    // Not a plain 'form' selector — see the matching comment in
    // amd/src/unsaved_guard.js's init().
    const form = region ? region.querySelector('[data-region="kid-entry-form"] form') : null;
    if (!region || !form || !region.dataset.conflict) {
        return;
    }

    let conflict;
    try {
        conflict = JSON.parse(region.dataset.conflict);
    } catch (e) {
        return;
    }

    const pending = new Set();
    Object.keys(conflict.competencies || {}).forEach((id) => pending.add(`competency:${id}`));
    if (conflict.global) {
        pending.add('global');
    }
    if (pending.size === 0) {
        return;
    }

    const stringkeys = [
        'conflict_modal_title', 'conflict_card_subtitle_author', 'conflict_card_subtitle_noauthor',
        'conflict_col_mine', 'conflict_col_db', 'conflict_btn_keepmine', 'conflict_btn_keepdb',
    ].map((key) => ({key, component: 'local_kid'}));

    getStrings(stringkeys).then(([title, subauthor, subnoauthor, colmine, coldb, btnmine, btndb]) => {
        let body = '';
        Object.entries(conflict.competencies || {}).forEach(([id, data]) => {
            body += renderCard(`competency:${id}`, data, subauthor, subnoauthor, colmine, coldb, btnmine, btndb);
        });
        if (conflict.global) {
            body += renderCard('global', conflict.global, subauthor, subnoauthor, colmine, coldb, btnmine, btndb);
        }

        return Modal.create({
            title,
            body,
            large: true,
            show: true,
            removeOnClose: true,
        });
    }).then((modal) => {
        const resolveCard = (cardid, keepmine) => {
            if (cardid === 'global') {
                // Naming convention shared with entry_grid_form.php/
                // capp_grid_form.php: the origin echo is always "orig" +
                // the field name ('commentaireglobal' -> 'origcommentaireglobal',
                // 'appreciationglobale' -> 'origappreciationglobale').
                const fieldname = conflict.globalfield;
                if (keepmine === false) {
                    field(form, fieldname).value = conflict.global.db;
                }
                field(form, `orig${fieldname}`).value = conflict.global.db;
            } else {
                const id = cardid.split(':')[1];
                if (keepmine === false) {
                    field(form, `results[${id}][commentaire]`).value = conflict.competencies[id].db;
                }
                field(form, `results[${id}][origcommentaire]`).value = conflict.competencies[id].db;
            }
            pending.delete(cardid);

            const card = modal.getBody()[0].querySelector(`[data-conflict-id="${cardid}"]`);
            card?.remove();

            if (pending.size === 0) {
                const expected = field(form, 'expectedtimemodified');
                if (expected) {
                    expected.value = conflict.freshtimemodified;
                }
                modal.hide();
                form.requestSubmit();
            }
        };

        modal.getRoot().on('click', '[data-conflict-action]', (e) => {
            const card = e.target.closest('[data-conflict-id]');
            if (!card) {
                return;
            }
            resolveCard(card.dataset.conflictId, e.target.dataset.conflictAction === 'mine');
        });
    }).catch(() => {
        // Nothing to fall back to here — the form still shows the user's
        // sticky submission and the plain "Enregistrer" button, they can
        // just retry the save manually if the modal itself failed to build.
    });
};
