// 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/>.

/**
 * Grouped ("actions groupées") selection bar shared by the Évaluations,
 * Stages and CAPP récapitulatif tables — a checkbox column + "tout
 * sélectionner" + one dropdown of actions (download fiche PDF/DOCX,
 * publish, hide, and — CAPP only — the session "livret" PDF) applied to
 * every checked student at once, modelled on Moodle's own assignment
 * grading "With selected..." pattern. Replaces the old per-student and
 * whole-promotion publish links entirely: "publier toute la promotion" is
 * now just "tout sélectionner" + "Publier".
 *
 * Publish/hide go through the plugin's own AJAX write function
 * (local_kid_bulk_publish_action) and patch just the affected rows'
 * "Publié" cell in place — no page reload. Downloads (fiche ZIP and the
 * CAPP livret alike) can't travel through core/ajax (the response is a
 * binary file, not JSON), so they build and submit a real hidden <form>
 * POST instead (pages/bulk_fiche_zip.php or pages/capp_rapport.php) —
 * the browser handles the resulting Content-Disposition download without
 * leaving the page or losing the current selection.
 *
 * @module     local_kid/bulk_actions
 * @copyright  2026 INSP
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
import Ajax from 'core/ajax';
import Notification from 'core/notification';
import {get_string as getString, get_strings as getStrings} from 'core/str';

const SELECTORS = {
    BAR: '[data-region="kid-bulk-actions"]',
    RESULT: '[data-region="kid-bulk-result"]',
    TABLE: '[data-region="kid-selectable-table"]',
    SELECT_ALL: '[data-role="kid-select-all"]',
    SELECT_ROW: '[data-role="kid-select-row"]',
    ACTION_SELECT: '[data-role="kid-bulk-select"]',
    APPLY: '[data-role="kid-bulk-apply"]',
    COUNT: '[data-role="kid-bulk-count"]',
};

/**
 * @param {Element} table
 * @returns {Number[]} checked row studentids
 */
const getSelectedIds = (table) => Array.from(table.querySelectorAll(SELECTORS.SELECT_ROW))
    .filter((checkbox) => checkbox.checked)
    .map((checkbox) => parseInt(checkbox.value, 10));

/**
 * Enables the Appliquer button only once a student is checked AND an
 * action is chosen, and refreshes the "N sélectionné(s)" counter.
 *
 * @param {Element} bar
 * @param {Element} table
 */
const refreshApplyState = (bar, table) => {
    const count = getSelectedIds(table).length;
    const select = bar.querySelector(SELECTORS.ACTION_SELECT);
    bar.querySelector(SELECTORS.APPLY).disabled = count === 0 || !select.value;

    const countEl = bar.querySelector(SELECTORS.COUNT);
    getString('bulk_actions_count', 'local_kid', count)
        .then((str) => {
            countEl.textContent = str;
            return str;
        })
        .catch(Notification.exception);
};

/**
 * Builds and submits a real form POST so the browser handles the ZIP
 * download natively, instead of trying to route a binary response through
 * core/ajax.
 *
 * @param {String} action absolute or wwwroot-relative POST target
 * @param {Object} fields plain field name => value
 * @param {Number[]} studentids
 */
const submitFormDownload = (action, fields, studentids) => {
    const form = document.createElement('form');
    form.method = 'post';
    form.action = action;
    form.style.display = 'none';

    const addField = (name, value) => {
        const input = document.createElement('input');
        input.type = 'hidden';
        input.name = name;
        input.value = value;
        form.appendChild(input);
    };

    Object.keys(fields).forEach((name) => addField(name, fields[name]));
    studentids.forEach((id) => addField('studentids[]', String(id)));

    document.body.appendChild(form);
    form.submit();
    form.remove();
};

/**
 * @param {Element} bar
 * @param {String} format 'pdf'|'docx'
 * @param {Number[]} studentids
 */
const submitDownload = (bar, format, studentids) => {
    submitFormDownload(M.cfg.wwwroot + '/local/kid/pages/bulk_fiche_zip.php', {
        sesskey: bar.dataset.sesskey,
        screen: bar.dataset.screen,
        itemid: bar.dataset.itemid,
        format,
    }, studentids);
};

/**
 * CAPP-only "livret" PDF (page de garde + Bilan général + fiches) — same
 * hidden-<form>-POST mechanism as submitDownload(), different target and
 * fields (sessionid instead of screen/itemid/format; mode is derived
 * server-side from the selection, see capp_rapport_builder::resolve_selection()).
 *
 * @param {Element} bar
 * @param {Number[]} studentids
 */
const submitCappRapportDownload = (bar, studentids) => {
    submitFormDownload(M.cfg.wwwroot + '/local/kid/pages/capp_rapport.php', {
        sesskey: bar.dataset.sesskey,
        sessionid: bar.dataset.itemid,
    }, studentids);
};

/**
 * Patches every affected row's "Publié" cell in place from the AJAX
 * response, without touching rows that weren't part of this batch.
 *
 * @param {Element} table
 * @param {Array} entries [{studentid, published}]
 * @returns {Promise}
 */
const patchRows = (table, entries) => {
    if (!entries.length) {
        return Promise.resolve();
    }
    return getStrings([
        {key: 'published_yes', component: 'local_kid'},
        {key: 'published_no', component: 'local_kid'},
    ]).then(([publishedyes, publishedno]) => {
        entries.forEach((entry) => {
            const row = table.querySelector(`tr[data-studentid="${entry.studentid}"]`);
            const cell = row && row.querySelector('[data-role="kid-published-cell"]');
            if (!cell) {
                return;
            }
            cell.innerHTML = '';
            const badge = document.createElement('span');
            badge.className = 'fr-badge ' + (entry.published ? 'fr-badge--success' : '');
            badge.textContent = entry.published ? publishedyes : publishedno;
            cell.appendChild(badge);
        });
        return entries;
    });
};

/**
 * Renders the "N dossiers publiés · M ignorés (non finalisés) : ..."
 * summary message after a publish/hide batch.
 *
 * @param {Element} bar
 * @param {String} action 'publish'|'hide'
 * @param {Object} data the AJAX response
 * @returns {Promise}
 */
const renderResult = (bar, action, data) => {
    const resultkey = action === 'publish' ? 'bulk_result_publish' : 'bulk_result_hide';
    const promise = data.ignoredcount > 0
        ? getString(resultkey + '_ignored', 'local_kid', {
            done: data.donecount,
            ignored: data.ignoredcount,
            names: data.ignorednames.join(', '),
        })
        : getString(resultkey, 'local_kid', data.donecount);

    return promise.then((message) => {
        bar.querySelector(SELECTORS.RESULT).textContent = message;
        return message;
    });
};

/**
 * Publishes or hides the selected entries via AJAX, then patches the
 * affected rows and the result message — no page reload (spec: "mise à
 * jour sans retour en haut de page").
 *
 * @param {Element} bar
 * @param {Element} table
 * @param {String} action 'publish'|'hide'
 * @param {Number[]} studentids
 * @returns {Promise}
 */
const submitPublishAction = (bar, table, action, studentids) => {
    bar.setAttribute('aria-busy', 'true');
    // core/ajax returns a jQuery Deferred promise, which has no .finally()
    // (only .always()) — see amd/src/student_nav.js for the same pattern.
    return Ajax.call([{
        methodname: 'local_kid_bulk_publish_action',
        args: {
            screen: bar.dataset.screen,
            itemid: parseInt(bar.dataset.itemid, 10),
            studentids,
            action,
        },
    }])[0]
    .then((data) => patchRows(table, data.entries).then(() => data))
    .then((data) => renderResult(bar, action, data))
    .catch(Notification.exception)
    .always(() => bar.removeAttribute('aria-busy'));
};

/**
 * @param {Element} bar
 * @param {Element} table
 */
const bindTable = (bar, table) => {
    const selectAll = table.querySelector(SELECTORS.SELECT_ALL);
    if (selectAll) {
        selectAll.addEventListener('change', () => {
            table.querySelectorAll(SELECTORS.SELECT_ROW).forEach((checkbox) => {
                checkbox.checked = selectAll.checked;
            });
            refreshApplyState(bar, table);
        });
    }
    table.querySelectorAll(SELECTORS.SELECT_ROW).forEach((checkbox) => {
        checkbox.addEventListener('change', () => {
            if (!checkbox.checked && selectAll) {
                selectAll.checked = false;
            }
            refreshApplyState(bar, table);
        });
    });
};

/**
 * @param {Element} bar
 */
const bindBar = (bar) => {
    // Each résumé screen renders exactly one selectable table per page
    // load, so a single page-wide lookup is safe.
    const table = document.querySelector(SELECTORS.TABLE);
    if (!table) {
        return;
    }
    bindTable(bar, table);

    const select = bar.querySelector(SELECTORS.ACTION_SELECT);
    select.addEventListener('change', () => refreshApplyState(bar, table));

    bar.querySelector(SELECTORS.APPLY).addEventListener('click', () => {
        const studentids = getSelectedIds(table);
        const action = select.value;
        if (!studentids.length || !action) {
            return;
        }
        if (action === 'download_pdf') {
            submitDownload(bar, 'pdf', studentids);
        } else if (action === 'download_docx') {
            submitDownload(bar, 'docx', studentids);
        } else if (action === 'export_capp_rapport') {
            submitCappRapportDownload(bar, studentids);
        } else if (action === 'publish' || action === 'hide') {
            submitPublishAction(bar, table, action, studentids);
        }
    });

    refreshApplyState(bar, table);
};

/**
 * Entry point, called once by the page controller (evaluations.php,
 * stages.php, capp.php) when the résumé (whole-promotion) view is shown.
 */
export const init = () => {
    document.querySelectorAll(SELECTORS.BAR).forEach(bindBar);
};
