<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container-fluid">
    <div class="wrapper">
        <div class="content"></div>
        <h2 class="text-center mt-4 mb-3">Manage Enrollment & Withdrawal</h2>
        <?php if (!empty($missingYear)): ?>
            <div class="alert alert-warning d-flex align-items-center" role="alert">
                <div>
                    Current school year is not configured. This page is read-only until configured in
                    <a href="<?= site_url('configuration/configuration_view') ?>" class="alert-link">Add/Edit Configuration</a> (key: <code>school_year</code>).
                </div>
            </div>
        <?php endif; ?>
        <?php if (empty($isCurrentYear)): ?>
            <div class="text-center mb-2">
                <span class="badge bg-secondary">Read-only (Past Year)</span>
            </div>
        <?php endif; ?>
        <!-- Display success and error messages -->
        <?php if (session()->getFlashdata('success')): ?>
            <div class="alert alert-success">
                <?= session()->getFlashdata('success') ?>
            </div>
        <?php endif; ?>
        <?php if (session()->getFlashdata('error')): ?>
            <div class="alert alert-danger">
                <?= session()->getFlashdata('error') ?>
            </div>
        <?php endif; ?>
        <form action="<?= base_url('admin/enroll_withdrawal_handler') ?>" method="post">
            <?= csrf_field() ?>

            <div class="table-responsive">
                <table id="enrollmentTable" class="table table-bordered table-striped mt-4 align-middle">
                    <thead>
                        <tr>
                            <th>Registration Date</th>
                            <th>Parent/Guardian</th>
                            <th>Student Name</th>
                            <th>New Student</th>
                            <th>Removed (Prior Years)</th>
                            <th>Current Class</th>
                            <th>Actual Status</th>
                            <th>Update Enrollment Status</th>
                            <th>Assign Class</th>
                        </tr>
                    </thead>
                    <tbody>
                        <?php if (!empty($students)): ?>
                            <?php foreach ($students as $student): ?>
                                <?php $sid = (int)($student['student_id'] ?? $student['id'] ?? 0); ?>
                                <?php $pid = (int)($student['parent_id'] ?? ($student['guardian_id'] ?? 0)); ?>
                                <tr data-student-id="<?= $sid ?>" data-parent-id="<?= $pid ?>">
                                    <td data-order="<?= !empty($student['registration_date']) ? local_date($student['registration_date'], 'Y-m-d') : '' ?>">
                                        <?= !empty($student['registration_date']) ? local_date($student['registration_date'], 'm-d-Y') : '-' ?>
                                    </td>

                                    <!-- Parent/Guardian (clickable for Family Card) -->
                                    <td>
                                        <?php if ($pid): ?>
                                          <a href="#" class="text-decoration-none" data-family-guardian-id="<?= (int)$pid ?>">
                                            <?= esc($student['parent_label'] ?? 'Unknown Parent') ?>
                                          </a>
                                        <?php else: ?>
                                          <?= esc($student['parent_label'] ?? 'Unknown Parent') ?>
                                        <?php endif; ?>
                                    </td>

                                    <!-- Student (clickable for Family Card) -->
                                    <td>
                                        <?php if ($sid): ?>
                                          <a href="#" class="text-decoration-none" data-family-student-id="<?= (int)$sid ?>">
                                            <?= esc($student['firstname']) ?> <?= esc($student['lastname']) ?>
                                          </a>
                                        <?php else: ?>
                                          <?= esc($student['firstname']) ?> <?= esc($student['lastname']) ?>
                                        <?php endif; ?>
                                    </td>

                                    <!-- New Student -->
                                    <td>
                                        <?php if (($student['new_student'] ?? 'Unknown') === 'Yes'): ?>
                                            <span class="badge bg-warning text-dark">Yes</span>
                                        <?php elseif (($student['new_student'] ?? 'Unknown') === 'No'): ?>
                                            <span class="badge bg-success text-light">No</span>
                                        <?php else: ?>
                                            <span class="badge bg-light text-dark">Unknown</span>
                                        <?php endif; ?>
                                    </td>

                                    <!-- Removed Previous Year -->
                                    <td>
                                        <?php if (($student['removed_previous_year'] ?? 'No') === 'Yes'): ?>
                                            <span class="badge bg-danger text-light">Yes</span>
                                        <?php else: ?>
                                            <span class="badge bg-secondary">No</span>
                                        <?php endif; ?>
                                    </td>

                                    <!-- Class -->
                                    <td><?= esc($student['class_section'] ?? 'Class not Assigned') ?></td>

                                    <!-- Enrollment Status -->
                                    <td>
                                        <?php
                                        $status = $student['enrollment_status'] ?? 'not enrolled';
                                        switch ($status) {
                                            case 'admission under review':
                                                echo '<span class="badge bg-primary">admission under review</span>';
                                                break;
                                            case 'payment pending':
                                                echo '<span class="badge bg-warning text-dark">payment pending</span>';
                                                break;
                                            case 'enrolled':
                                                echo '<span class="badge bg-success">enrolled</span>';
                                                break;
                                            case 'withdraw under review':
                                                echo '<span class="badge bg-warning text-dark">withdraw under review</span>';
                                                break;
                                            case 'refund pending':
                                                echo '<span class="badge bg-info text-dark">refund pending</span>';
                                                break;
                                            case 'withdrawn':
                                                echo '<span class="badge bg-danger">withdrawn</span>';
                                                break;
                                            case 'waitlist':
                                                echo '<span class="badge bg-secondary">waitlist</span>';
                                                break;
                                            case 'denied':
                                                echo '<span class="badge bg-dark">denied</span>';
                                                break;
                                            default:
                                                echo '<span class="badge bg-light text-dark">not enrolled</span>';
                                        }
                                        ?>
                                    </td>

                                    <!-- Update Enrollment -->
                                    <td>
                                        <select <?= empty($isCurrentYear) ? 'disabled' : '' ?>
                                            name="enrollment_status[<?= $sid ?>]"
                                            class="form-control enrollment-status"
                                            data-student-id="<?= $sid ?>"
                                            data-parent-id="<?= $pid ?>"
                                            data-prev="<?= esc($student['enrollment_status'] ?? '') ?>">
                                            <option value="admission under review" <?= ($student['enrollment_status'] ?? '') === 'admission under review' ? 'selected' : '' ?>>admission under review</option>
                                            <option value="payment pending" <?= ($student['enrollment_status'] ?? '') === 'payment pending' ? 'selected' : '' ?>>payment pending</option>
                                            <option value="enrolled" <?= ($student['enrollment_status'] ?? '') === 'enrolled' ? 'selected' : '' ?>>enrolled</option>
                                            <option value="withdraw under review" <?= ($student['enrollment_status'] ?? '') === 'withdraw under review' ? 'selected' : '' ?>>withdraw under review</option>
                                            <option value="refund pending" <?= ($student['enrollment_status'] ?? '') === 'refund pending' ? 'selected' : '' ?>>refund pending</option>
                                            <option value="withdrawn" <?= ($student['enrollment_status'] ?? '') === 'withdrawn' ? 'selected' : '' ?>>withdrawn</option>
                                            <option value="waitlist" <?= ($student['enrollment_status'] ?? '') === 'waitlist' ? 'selected' : '' ?>>waitlist</option>
                                            <option value="denied" <?= ($student['enrollment_status'] ?? '') === 'denied' ? 'selected' : '' ?>>denied</option>
                                        </select>

                                    </td>
                                    <!-- Assign Class (only for 'admission under review') -->
                                    <td>
                                        <?php $isUnderReview = (strtolower($student['enrollment_status'] ?? '') === 'admission under review'); ?>
                                        <?php if ($isUnderReview): ?>
                                            <select class="form-select form-select-sm assign-class-select" <?= empty($isCurrentYear) ? 'disabled' : '' ?>
                                                    data-student-id="<?= $sid ?>"
                                                    data-parent-id="<?= $pid ?>">
                                                <option value="">— Select class section —</option>
                                                <?php if (!empty($classes) && is_array($classes)): ?>
                                                    <?php foreach ($classes as $class_): ?>
                                                        <?php
                                                            $val  = $class_['class_section_id'] ?? $class_['id'];
                                                            $text = $class_['class_section_name'] ?? ($class_['name'] ?? 'Section ' . $val);
                                                        ?>
                                                        <option value="<?= esc($val) ?>"><?= esc($text) ?></option>
                                                    <?php endforeach; ?>
                                                <?php endif; ?>
                                            </select>
                                        <?php else: ?>
                                            <span class="text-muted">—</span>
                                        <?php endif; ?>
                                    </td>
                                </tr>
                            <?php endforeach; ?>
                        <?php else: ?>
                            <tr>
                                <td colspan="9">No students available.</td>
                            </tr>
                        <?php endif; ?>
                    </tbody>
                </table>
            </div>
            <!-- Centered action buttons under the table -->
            <div class="d-flex justify-content-center gap-2 my-3">
                <button type="button" id="bulkSaveGenerateButton" class="btn btn-success btn-lg" <?= empty($isCurrentYear) ? 'disabled title="Editing disabled for non-current year"' : '' ?> >
                    Save &amp; Generate Invoice
                </button>
                <span id="bulkProgress" class="small text-muted d-none">Processing…</span>
            </div>
        </form>
    </div>
</div>

<?= $this->endSection() ?>
<?= $this->section('scripts') ?>
<style>
    /* === Modal Styling === */
    .custom-modal {
        border-radius: 12px;
        overflow: hidden;
        animation: fadeInUp 0.35s ease;
    }

    /* Gradient Header */
    .custom-modal-header {
        background: linear-gradient(135deg, #2d89ecff, #2161a7ff);
        color: #fff;
        border-bottom: none;
        padding: 1rem 1.5rem;
    }

    .custom-modal-header .modal-title {
        font-size: 1.1rem;
        letter-spacing: 0.5px;
    }

    .custom-modal-footer {
        background-color: #f8f9fa;
        border-top: none;
        padding: 1rem 1.5rem;
    }

    /* Buttons hover effect */
    .custom-modal-footer .btn-primary {
        transition: all 0.2s ease;
    }

    .custom-modal-footer .btn-primary:hover {
        background-color: #2670c0ff;
        transform: translateY(-1px);
    }

    .custom-modal-footer .btn-outline-secondary:hover {
        background-color: #e9ecef;
    }

    /* Smooth show animation */
    @keyframes fadeInUp {
        from {
            transform: translateY(40px);
            opacity: 0;
        }

        to {
            transform: translateY(0);
            opacity: 1;
        }
    }
    /* Toast container is provided by Bootstrap 5 classes when created */
</style>
<script>
    document.addEventListener('DOMContentLoaded', function() {
        // Do NOT auto-submit when status dropdown changes; just mark desired value
        document.addEventListener('change', function(e) {
            const el = e.target;
            if (!(el instanceof HTMLSelectElement)) return;
            if (!el.name || !el.name.startsWith('enrollment_status[')) return;

            const prev = (el.getAttribute('data-prev') || '').trim().toLowerCase();
            const next = (el.value || '').trim().toLowerCase();

            if (next === prev) {
                el.removeAttribute('data-desired');
                el.classList.remove('pending-status-select');
            } else {
                el.setAttribute('data-desired', next);
                el.classList.add('pending-status-select');
            }
        }, false);
    });
</script>
<script>
    // Bootstrap 5 Toast utility for this page
    (function(){
        function ensureContainer(){
            let c = document.getElementById('toast-container');
            if (!c) {
                c = document.createElement('div');
                c.id = 'toast-container';
                c.className = 'toast-container position-fixed top-0 end-0 p-3';
                c.style.zIndex = '1080';
                document.body.appendChild(c);
            }
            return c;
        }
        window.showToast = function(msg, ok=true, opts={}){
            const c = ensureContainer();
            const el = document.createElement('div');
            el.className = 'toast align-items-center text-bg-' + (ok ? 'success' : 'danger') + ' border-0';
            el.setAttribute('role','alert'); el.setAttribute('aria-live','assertive'); el.setAttribute('aria-atomic','true');
            el.innerHTML = '<div class="d-flex">'
                + '<div class="toast-body">' + (msg || (ok ? 'Saved' : 'Action failed')) + '</div>'
                + '<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>'
                + '</div>';
            c.appendChild(el);
            try {
                const t = new bootstrap.Toast(el, { delay: opts.delay ?? 3000, autohide: true });
                el.addEventListener('hidden.bs.toast', () => el.remove());
                t.show();
            } catch (_){ setTimeout(() => el.remove(), 3000); }
        }
    })();

    $(function() {
        const $tbl = $('#enrollmentTable');

        // Helper: compute offset if a fixed/sticky navbar exists to prevent overlap
        function getFixedHeaderOffset() {
            let total = 0;
            const stack = [];
            const header = document.querySelector('header.navbar.sticky-top, header.navbar.fixed-top');
            if (header) stack.push(header);
            const mgmt = document.getElementById('navbarManagement');
            if (mgmt && (mgmt.classList.contains('sticky-top') || mgmt.classList.contains('fixed-top'))) stack.push(mgmt);
            document.querySelectorAll('.navbar.sticky-top, .navbar.fixed-top').forEach(el => { if (!stack.includes(el)) stack.push(el); });
            stack.forEach(el => { const h = el.offsetHeight || el.getBoundingClientRect().height || 0; total += Math.max(0, Math.round(h)); });
            return total;
        }

        // Lightweight loaders for optional plugin assets
        function loadScript(src, id) {
            return new Promise((resolve, reject) => {
                if (id && document.getElementById(id)) return resolve();
                const s = document.createElement('script');
                if (id) s.id = id;
                s.src = src;
                s.onload = resolve;
                s.onerror = reject;
                document.head.appendChild(s);
            });
        }
        function loadCss(href, id) {
            return new Promise((resolve) => {
                if (id && document.getElementById(id)) return resolve();
                const l = document.createElement('link');
                if (id) l.id = id;
                l.rel = 'stylesheet';
                l.href = href;
                l.onload = resolve;
                document.head.appendChild(l);
            });
        }

        // If already initialized, destroy cleanly (prevents duplicate headers)
        if ($.fn.DataTable.isDataTable($tbl)) {
            $tbl.DataTable().clear().destroy();
            $tbl.find('thead th')
                .removeClass('sorting sorting_asc sorting_desc sorting_asc_disabled sorting_desc_disabled');
        }

        // Helper: strip HTML (for badges) so search/sort use plain text
        const stripHtml = (d) => {
            if (d == null) return '';
            const div = document.createElement('div');
            div.innerHTML = d;
            return (div.textContent || div.innerText || '').trim();
        };

        function initDT() {
            $tbl.DataTable({
            pageLength: 100,
            lengthMenu: [10, 25, 50, 100],

            // 🔽 Default: sort by Registration Date (col 0) newest first
            order: [
                [0, 'desc']
            ],

            // Top row: length (left) + search (right)
            dom: "<'row mb-2'<'col-sm-6'l><'col-sm-6'f>>" + "t" +
                "<'row mt-2'<'col-sm-5'i><'col-sm-7'p>>",

            // Disable sort/search on interactive columns (status select, assign select)
            columnDefs: [
                { targets: [7, 8], orderable: false, searchable: false },
                { targets: [0, 1, 2, 3, 4, 5, 6], render: function(data, type) {
                    if (type === 'filter' || type === 'sort' || type === 'type') {
                        return stripHtml(data);
                    }
                    return data;
                }}
            ],
            fixedHeader: {
                header: true,
                headerOffset: getFixedHeaderOffset()
            }
        });
        }

        // Ensure FixedHeader plugin is present before init
        const hasFH = $.fn.dataTable && $.fn.dataTable.FixedHeader;
        if (hasFH) {
            initDT();
        } else {
            // Load FixedHeader (and its Bootstrap CSS) then init
            Promise.all([
                loadScript('https://cdn.jsdelivr.net/npm/datatables.net-fixedheader@3.4.0/js/dataTables.fixedHeader.min.js', 'dt-fixedheader'),
                loadCss('https://cdn.jsdelivr.net/npm/datatables.net-fixedheader-bs5@3.4.0/css/fixedHeader.bootstrap5.min.css', 'dt-fixedheader-css')
            ]).then(() => initDT()).catch(() => initDT()); // proceed even if CDN fails
        }

        // Prevent the surrounding <form> from submitting when pressing Enter in the DT search box
        $(document).on('keydown keyup', '.dataTables_filter input', function(e) {
            if (e.key === 'Enter') e.preventDefault();
        });

        // No API hydration here; server renders rows.
    });
</script>
<script>
    /* ===== CSRF bootstrap ===== */
    const CSRF_TOKEN_NAME = '<?= csrf_token() ?>';
    let CSRF_HASH = '<?= csrf_hash() ?>';
    const CSRF_COOKIE_NAME = <?= json_encode(config('Security')->csrfCookieName ?? (config('Security')->cookieName ?? 'csrf_cookie_name')) ?>;

    function getCookie(n) {
        return document.cookie.split(';')
            .map(v => v.trim())
            .find(v => v.startsWith(n + '='))?.split('=')[1] || '';
    }

    /* ===== DataTable ref (if present) ===== */
    const $tbl = $('#enrollmentTable');
    const dt = $.fn.DataTable && $.fn.DataTable.isDataTable($tbl) ? $tbl.DataTable() : null;

    /* Column index helpers by header text (robust to reordering) */
    function findColIndexByHeader(text) {
        let idx = -1;
        $('#enrollmentTable thead th').each(function(i) {
            if ($(this).text().trim() === text) idx = i;
        });
        return idx;
    }
    const CLASS_COL_INDEX = findColIndexByHeader('Current Class'); // was index 4
    const STATUS_COL_INDEX = findColIndexByHeader('Actual Status'); // was index 5
    const STATUS_SELECT_COL_INDEX = findColIndexByHeader('Update Enrollment Status');
    const ASSIGN_COL_INDEX = findColIndexByHeader('Assign Class');

    /* ===== Helpers ===== */
    function norm(v) {
        return (v || '').trim().toLowerCase().replace(/[_\s]+/g, ' ');
    }

    function showRowToast(tr, msg, ok = true) {
        if (!tr) return;
        const $tr = $(tr);
        $tr.addClass(ok ? 'table-success' : 'table-danger');
        $tr.attr('title', msg || '');
        setTimeout(() => { $tr.removeClass('table-success table-danger'); $tr.removeAttr('title'); }, 1500);
    }

    function badgeForStatusJs(status) {
        const s = norm(status);
        switch (s) {
            case 'admission under review': return '<span class="badge bg-primary">admission under review</span>';
            case 'payment pending':        return '<span class="badge bg-warning text-dark">payment pending</span>';
            case 'enrolled':               return '<span class="badge bg-success">enrolled</span>';
            case 'withdraw under review':  return '<span class="badge bg-warning text-dark">withdraw under review</span>';
            case 'refund pending':         return '<span class="badge bg-info text-dark">refund pending</span>';
            case 'withdrawn':              return '<span class="badge bg-danger">withdrawn</span>';
            case 'waitlist':               return '<span class="badge bg-secondary">waitlist</span>';
            case 'denied':                 return '<span class="badge bg-dark">denied</span>';
            default:                       return '<span class="badge bg-light text-dark">not enrolled</span>';
        }
    }

    /* Update CSRF like your class-assignment view */
    function updateCsrfFromResponse($form, res) {
        if (!res) return;
        let name = res.csrfTokenName || null;
        let hash = res.csrfHash || null;
        if (!hash) {
            ($form && $form.find ? $form.find('input[type="hidden"]') : $('input[type="hidden"]')).each(function() {
                const nm = this.name;
                if (nm && res[nm]) {
                    name = nm;
                    hash = res[nm];
                    return false;
                }
            });
        }
        // Fallback: cookie rotate after POST
        if (!hash) {
            const c = decodeURIComponent(getCookie(CSRF_COOKIE_NAME));
            if (c) hash = c;
        }
        if (name && hash) {
            CSRF_HASH = hash; // keep JS in sync
            const $fld = $form && $form.find ? $form.find('input[name="' + name + '"]') : $('input[name="' + name + '"]');
            if ($fld && $fld.length) $fld.val(hash);
            else ($form && $form.prepend ? $form.prepend($('<input>', {
                type: 'hidden',
                name,
                value: hash
            })) : document.body.insertAdjacentHTML('beforeend', `<input type="hidden" name="${name}" value="${hash}">`));
        }
    }

    /* Track the row and the select that triggered the change */
    let triggerSelect = null;
    let rowNode = null;

    /** AJAX: batch update multiple students' statuses at once */
    async function updateEnrollmentStatusesBatch(updates) {
        if (!updates || !updates.length) return;
        const url = '<?= site_url('admin/enroll_withdrawal_handler') ?>';
        const body = new URLSearchParams();
        updates.forEach(u => {
            if (u && u.studentId && u.status) body.append(`enrollment_status[${u.studentId}]`, u.status);
        });
        body.append(CSRF_TOKEN_NAME, CSRF_HASH);
        const resp = await fetch(url, {
            method: 'POST',
            headers: { 'Accept': 'application/json,text/html', 'X-Requested-With': 'XMLHttpRequest' },
            body
        });
        const ct = resp.headers.get('content-type') || '';
        if (ct.includes('application/json')) {
            const json = await resp.json().catch(() => ({}));
            updateCsrfFromResponse(null, json);
            if (json?.ok === false) throw new Error(json?.message || 'Status update failed.');
        } else if (!resp.ok) {
            throw new Error('Status update failed.');
        }
        // Fallback: update CSRF from cookie after POST
        updateCsrfFromResponse(null, {});
    }

    /* Intercept change on the status select (do NOT submit immediately) */
    document.addEventListener('change', async (e) => {
        const el = e.target;
        if (!(el instanceof HTMLSelectElement)) return;
        if (!el.name || !el.name.startsWith('enrollment_status[')) return;

        const prev = norm(el.getAttribute('data-prev') || '');
        const next = norm(el.value);
        // Mark desired status but do not submit; will process on bulk Save
        if (next === prev) {
            el.removeAttribute('data-desired');
            el.classList.remove('pending-status-select');
            return;
        }
        el.setAttribute('data-desired', next);
        el.classList.add('pending-status-select');
    }, true); // capture to pre-empt stray handlers

    /* Bulk Save & Generate button */
    document.getElementById('bulkSaveGenerateButton')?.addEventListener('click', async () => {
        const btn = document.getElementById('bulkSaveGenerateButton');
        const prog = document.getElementById('bulkProgress');
        if (!btn) return;
        btn.disabled = true;
        if (prog) prog.classList.remove('d-none');

        // Collect tasks: rows where status is 'admission under review' and a class is selected
        const assigns = Array.from(document.querySelectorAll('.assign-class-select'));
        const tasks = [];
        const parentSet = new Set();
        assigns.forEach(sel => {
            const tr = sel.closest('tr');
            const statusSelect = tr?.querySelector('select.enrollment-status');
            const prev = norm(statusSelect?.getAttribute('data-prev') || '');
            const desired = norm(statusSelect?.getAttribute('data-desired') || statusSelect?.value || '');
            const classId = sel.value;
            // Only include rows explicitly set to move to payment pending with a class selected
            if (prev === 'admission under review' && desired === 'payment pending' && classId) {
                const studentId = sel.getAttribute('data-student-id');
                const parentId  = sel.getAttribute('data-parent-id');
                const className = sel.options[sel.selectedIndex]?.text || '';
                tasks.push({ tr, studentId, parentId, classId, className });
                parentSet.add(parentId);
            }
        });

        // Collect standalone status updates (not AUR->payment pending)
        const statusTasks = [];
        const shouldInvoice = new Set(['payment pending','enrolled','withdrawn','refund pending','withdraw under review']);
        document.querySelectorAll('select.enrollment-status').forEach(se => {
            const tr = se.closest('tr');
            const prev = norm(se.getAttribute('data-prev') || '');
            const desired = norm(se.getAttribute('data-desired') || '');
            if (!desired || desired === prev) return;
            if (prev === 'admission under review' && desired === 'payment pending') return; // handled by tasks
            const parentId = se.getAttribute('data-parent-id') || tr?.getAttribute('data-parent-id') || '';
            statusTasks.push({ tr, studentId: se.getAttribute('data-student-id'), desired, parentId });
            if (parentId && shouldInvoice.has(desired)) parentSet.add(parentId);
        });

        if (tasks.length === 0 && statusTasks.length === 0) {
            if (prog) prog.classList.add('d-none');
            btn.disabled = false;
            alert('Nothing to process. Set desired statuses, and for payment pending also select a class. Then click Save & Generate Invoice.');
            return;
        }

        async function assignClass(t) {
            const body = new URLSearchParams();
            body.append('student_id', t.studentId);
            body.append('parent_id', t.parentId);
            body.append('class_section_id', t.classId);
            body.append(CSRF_TOKEN_NAME, CSRF_HASH);
            const resp = await fetch('<?= site_url('assign_class_student') ?>', {
                method: 'POST', headers: { 'Accept': 'application/json,text/html', 'X-Requested-With': 'XMLHttpRequest' }, body
            });
            let data = null; try { data = await resp.json(); } catch {}
            if (data) updateCsrfFromResponse(null, data);
            if (!resp.ok || (data && data.ok === false)) throw new Error(data?.message || 'Assign failed');
        }

        function setPaymentPendingUI(t) {
            if (dt) {
                const r = dt.row(t.tr);
                const rowData = r.data();
                rowData[STATUS_COL_INDEX] = '<span class="badge bg-warning text-dark">payment pending</span>';
                rowData[STATUS_SELECT_COL_INDEX] = buildStatusSelectHtml(t.studentId, t.parentId, 'payment pending');
                rowData[ASSIGN_COL_INDEX] = '<span class="text-muted">—</span>';
                r.data(rowData).draw(false);
            } else {
                const statusBadgeCell = $(t.tr).find('td').eq(STATUS_COL_INDEX);
                statusBadgeCell.html('<span class="badge bg-warning text-dark">payment pending</span>');
                const statusSelect = t.tr.querySelector('select.enrollment-status');
                if (statusSelect) { statusSelect.value = 'payment pending'; statusSelect.setAttribute('data-prev', 'payment pending'); }
                const cells = t.tr.querySelectorAll('td');
                const assignCell = cells[ASSIGN_COL_INDEX];
                if (assignCell) assignCell.innerHTML = '<span class="text-muted">—</span>';
            }
        }

        async function updateClassCell(t) {
            if (dt && CLASS_COL_INDEX >= 0) {
                const r = dt.row(t.tr);
                const rowData = r.data();
                rowData[CLASS_COL_INDEX] = $('<div>').text(t.className).html();
                r.data(rowData).draw(false);
            } else {
                $(t.tr).find('td').eq(CLASS_COL_INDEX).text(t.className);
            }
        }

        const errors = [];
        const changedIds = new Set();
        const batchUpdates = [];
        for (const t of tasks) {
            try {
                await assignClass(t);
                await updateClassCell(t);
                setPaymentPendingUI(t);
                // clear desired on status select
                const sel = t.tr.querySelector('select.enrollment-status');
                if (sel) { sel.removeAttribute('data-desired'); sel.classList.remove('pending-status-select'); }
                showRowToast(t.tr, 'Saved.');
                changedIds.add(String(t.studentId));
                batchUpdates.push({ studentId: t.studentId, status: 'payment pending' });
            } catch (e) {
                errors.push({ t, error: e });
                showRowToast(t.tr, e?.message || 'Failed', false);
            }
        }

        // Apply standalone status changes
        for (const s of statusTasks) {
            try {
                if (dt) {
                    const r = dt.row(s.tr);
                    const rowData = r.data();
                    rowData[STATUS_COL_INDEX] = badgeForStatusJs(s.desired);
                    rowData[STATUS_SELECT_COL_INDEX] = buildStatusSelectHtml(s.studentId, s.tr.getAttribute('data-parent-id') || '', s.desired);
                    if (norm(s.desired) === 'admission under review') {
                        // leave current assign select as-is
                    } else {
                        rowData[ASSIGN_COL_INDEX] = '<span class="text-muted">—</span>';
                    }
                    r.data(rowData).draw(false);
                } else {
                    const statusBadgeCell = $(s.tr).find('td').eq(STATUS_COL_INDEX);
                    statusBadgeCell.html(badgeForStatusJs(s.desired));
                    const statusSelect = s.tr.querySelector('select.enrollment-status');
                    if (statusSelect) {
                        statusSelect.value = s.desired;
                        statusSelect.setAttribute('data-prev', s.desired);
                        statusSelect.removeAttribute('data-desired');
                        statusSelect.classList.remove('pending-status-select');
                    }
                    if (norm(s.desired) !== 'admission under review') {
                        const cells = s.tr.querySelectorAll('td');
                        const assignCell = cells[ASSIGN_COL_INDEX];
                        if (assignCell) assignCell.innerHTML = '<span class="text-muted">—</span>';
                    }
                }
                showRowToast(s.tr, 'Status updated.');
                changedIds.add(String(s.studentId));
                batchUpdates.push({ studentId: s.studentId, status: s.desired });
            } catch (e) {
                errors.push({ s, error: e });
                showRowToast(s.tr, e?.message || 'Failed', false);
            }
        }

        // Perform one batch status update to trigger grouped emails per parent
        try {
            await updateEnrollmentStatusesBatch(batchUpdates);
        } catch (e) {
            errors.push({ error: e });
        }

        // Generate one invoice per parent AFTER statuses are updated (so admission_status is accepted)
        for (const pid of parentSet) {
            try {
                const body = new URLSearchParams();
                body.append('parent_id', pid);
                body.append(CSRF_TOKEN_NAME, CSRF_HASH);
                const resp2 = await fetch('<?= site_url('api/invoices/generate') ?>', { method: 'POST', headers: { 'Accept': 'application/json,text/html', 'X-Requested-With': 'XMLHttpRequest' }, body });
                if (resp2.headers.get('content-type')?.includes('application/json')) {
                    const j = await resp2.json().catch(() => ({}));
                    updateCsrfFromResponse(null, j);
                }
                // Fallback: refresh CSRF from cookie in case token rotated
                updateCsrfFromResponse(null, {});
            } catch (_) { /* non-fatal */ }
        }

        if (prog) prog.classList.add('d-none');
        btn.disabled = false;
        if (errors.length) {
            showToast(`${errors.length} row(s) failed. Hover red rows for details.`, false);
        } else if (changedIds.size > 0) {
            showToast(`${changedIds.size} row(s) updated successfully.`);
        } else {
            showToast('No changes applied.', false);
        }

        // Final refresh from API for changed rows to ensure full consistency
        await refreshRowsByIds(Array.from(changedIds));
    });
</script>
    <script>
    // API endpoint for refresh (not initial hydration)
    const ENROLL_API_ENDPOINT = <?= json_encode(site_url('api/admin/enrollment-withdrawal')) ?>;
    const SELECTED_YEAR = <?= json_encode((string)($selectedYear ?? $currentYear ?? '')) ?>;
    const IS_CURRENT_YEAR = <?= json_encode((bool)($isCurrentYear ?? (isset($selectedYear,$currentYear) ? ((string)$selectedYear === (string)$currentYear) : true))) ?>;
    const enrollApiUrl = () => SELECTED_YEAR ? (ENROLL_API_ENDPOINT + '?schoolYear=' + encodeURIComponent(SELECTED_YEAR)) : ENROLL_API_ENDPOINT;

    function buildStatusSelectHtml(studentId, parentId, current) {
        const opts = [
            'admission under review','payment pending','enrolled','withdraw under review','refund pending','withdrawn','waitlist','denied'
        ];
        let html = `<select name="enrollment_status[${studentId}]" class="form-control enrollment-status" ${IS_CURRENT_YEAR ? '' : 'disabled'} data-student-id="${studentId}" data-parent-id="${parentId}" data-prev="${esc(current)}">`;
        for (const o of opts) {
            const sel = (norm(current) === norm(o)) ? ' selected' : '';
            html += `<option value="${esc(o)}"${sel}>${esc(o)}</option>`;
        }
        html += `</select>`;
        return html;
    }

    function buildAssignSelectHtml(studentId, parentId, classes) {
        let html = `<select class=\"form-select form-select-sm assign-class-select\" ${IS_CURRENT_YEAR ? '' : 'disabled'} data-student-id=\"${studentId}\" data-parent-id=\"${parentId}\">`;
        html += `<option value=\"\">— Select class section —</option>`;
        (classes || []).forEach(c => {
            const val  = c.class_section_id ?? c.id;
            const text = c.class_section_name ?? (`Section ${val}`);
            html += `<option value=\"${esc(String(val))}\">${esc(String(text))}</option>`;
        });
        html += `</select>`;
        return html;
    }

    async function refreshRowsByIds(studentIds) {
        if (!studentIds || !studentIds.length) return;
        try {
            const res = await fetch(enrollApiUrl(), { credentials: 'same-origin' });
            const data = await res.json();
            if (!res.ok || data?.error) return;
            const byId = {};
            (data.students || []).forEach(st => { byId[String(st.id || st.student_id)] = st; });

            for (const id of studentIds) {
                const st = byId[String(id)];
                if (!st) continue;
                const tr = document.querySelector(`tr[data-student-id=\"${id}\"]`);
                if (!tr) continue;
                if (dt) {
                    const r = dt.row(tr);
                    const rowData = r.data();
                    // Update class and status
                    rowData[CLASS_COL_INDEX] = esc(st.class_section || 'Class not Assigned');
                    rowData[STATUS_COL_INDEX] = badgeForStatusJs(st.enrollment_status || '');
                    rowData[STATUS_SELECT_COL_INDEX] = buildStatusSelectHtml(id, tr.getAttribute('data-parent-id') || '', st.enrollment_status || '');
                    if (norm(st.enrollment_status || '') === 'admission under review') {
                        rowData[ASSIGN_COL_INDEX] = buildAssignSelectHtml(id, tr.getAttribute('data-parent-id') || '', data.classes || []);
                    } else {
                        rowData[ASSIGN_COL_INDEX] = '<span class="text-muted">—</span>';
                    }
                    r.data(rowData).draw(false);
                } else {
                    // Fallback DOM updates
                    $(tr).find('td').eq(CLASS_COL_INDEX).text(st.class_section || 'Class not Assigned');
                    $(tr).find('td').eq(STATUS_COL_INDEX).html(badgeForStatusJs(st.enrollment_status || ''));
                }
            }
        } catch (_) { /* ignore */ }
    }
    </script>
<?= $this->endSection() ?>
