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

/**
 * Progressively enhances the student-navigation header (selector +
 * previous/next + position counter) shared by the Évaluations, Stages and
 * CAPP data-entry screens: without this module every control is a real
 * link/GET form and already works (full page load). With it, switching
 * student re-renders only the [data-region="kid-grid-student"] fragment via
 * the plugin's read-only web service, so the screen never scrolls back to
 * the top of the page.
 *
 * Every click/select-change here goes through local_kid/unsaved_guard's
 * guardIfDirty() first: with a clean form it behaves exactly as before
 * (immediate AJAX swap); with unsaved changes it opens the "Modifications
 * non enregistrées" popup instead, and only swaps if the user explicitly
 * discards. Both modules are always loaded together by the page controller
 * (evaluations.php/stages.php/capp.php), unsaved_guard's init() first, so
 * its dirty-tracking is already armed before any of these listeners fire.
 *
 * @module     local_kid/student_nav
 * @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 {guardIfDirty, rewatch} from 'local_kid/unsaved_guard';

const SELECTORS = {
    REGION: '[data-region="kid-grid-student"]',
    NAV: '[data-region="kid-student-nav"]',
    LINK: 'a[data-direction]',
    FORM: '[data-role="kid-student-nav-form"]',
    SELECT: '[data-role="kid-student-nav-select"]',
};

/**
 * Fetches the fragment for another student and swaps it in place.
 *
 * @param {Element} region the [data-region="kid-grid-student"] container
 * @param {String} screen
 * @param {Number} itemid
 * @param {Number} studentid
 * @param {String} url the equivalent full-page URL, for history.pushState
 * @returns {Promise}
 */
const navigate = (region, screen, itemid, studentid, url) => {
    region.setAttribute('aria-busy', 'true');
    // core/ajax returns a jQuery Deferred promise, which has no .finally()
    // (jQuery.Deferred only exposes .always() for that) — using .finally()
    // here throws synchronously as soon as the chain is built, so the
    // aria-busy attribute (and the CSS dimming it drives) is set but the
    // cleanup below never actually gets attached, leaving the "loading"
    // look stuck after the swap completes.
    return Ajax.call([{
        methodname: 'local_kid_get_grid_student_view',
        args: {screen, itemid, studentid},
    }])[0]
    .then((data) => {
        region.innerHTML = data.html;
        if (url) {
            window.history.pushState({}, '', url);
        }
        bindRegion(region);
        return data;
    })
    .catch(Notification.exception)
    .always(() => region.removeAttribute('aria-busy'));
};

/**
 * Binds the prev/next links and the selector form found in one region to
 * {@see navigate} instead of letting them perform a full page load. Called
 * once at page load and again after every swap, since the previous
 * listeners were attached to elements that just got replaced.
 *
 * @param {Element} region
 */
const bindRegion = (region) => {
    // Re-arms local_kid/unsaved_guard on this region's (possibly
    // just-replaced) form before anything below can navigate away from it —
    // see that module's rewatch() doc comment.
    rewatch(region);

    const nav = region.querySelector(SELECTORS.NAV);
    if (!nav) {
        return;
    }
    const screen = nav.dataset.screen;
    const itemid = parseInt(nav.dataset.itemid, 10);
    const itemparam = nav.dataset.itemparam;

    nav.querySelectorAll(SELECTORS.LINK).forEach((link) => {
        if (link.classList.contains('disabled')) {
            return;
        }
        link.addEventListener('click', (e) => {
            e.preventDefault();
            const studentid = parseInt(link.dataset.studentid, 10);
            const url = link.getAttribute('href');
            guardIfDirty(url, () => navigate(region, screen, itemid, studentid, url), link);
        });
    });

    const form = nav.querySelector(SELECTORS.FORM);
    const select = nav.querySelector(SELECTORS.SELECT);
    if (!form || !select) {
        return;
    }

    const goToSelected = () => {
        const studentid = parseInt(select.value, 10);
        if (!studentid) {
            return;
        }
        const url = new URL(form.getAttribute('action'), window.location.href);
        url.searchParams.set(itemparam, itemid);
        url.searchParams.set('studentid', studentid);
        guardIfDirty(url.toString(), () => navigate(region, screen, itemid, studentid, url.toString()), select);
    };

    form.addEventListener('submit', (e) => {
        e.preventDefault();
        goToSelected();
    });
    select.addEventListener('change', goToSelected);
};

/**
 * Entry point, called once by the page controller (evaluations.php,
 * stages.php, capp.php) when a student is selected.
 */
export const init = () => {
    document.querySelectorAll(SELECTORS.REGION).forEach(bindRegion);
};
