{% include "generic/exam_candidate_headers.html" %}

<div class="card mb-2 bg-dark text-light border-0">
    <div class="card-body py-2">
        <div class="d-flex justify-content-between align-items-start mb-2">
            <div>
                <h5 class="card-title mb-0">CID Users</h5>
                {% if groups %}
                    <div class="small text-muted mt-1">Users from the following groups are available:
                        {% for group in groups %}
                            <a href='{{group.get_absolute_url}}' class="badge bg-light text-dark border ms-1">{{group}}</a>
                        {% endfor %}
                    </div>
                {% else %}
                    <div class="small text-muted mt-1">This exam is not associated with any user groups. <a href="{% url exam.app_name|add:':exam_groups_edit' exam.pk %}">Edit and add a group</a> to enable user management.</div>
                {% endif %}
            </div>
            <div class="btn-group">
                {% if current_cid_users or available_cid_users %}
                    <button class="btn btn-sm btn-outline-light toggle-all-btn">Toggle all</button>
                {% endif %}
                <button class="btn btn-sm btn-outline-success add-all-btn" data-bulk-url="{% url exam.app_name|add:':exam_cid_bulk_edit' exam.pk %}">Add all</button>
                <button class="btn btn-sm btn-outline-danger remove-all-btn" data-bulk-url="{% url exam.app_name|add:':exam_cid_bulk_edit' exam.pk %}">Remove all</button>
            </div>
        </div>

        <div class="d-flex align-items-center mb-2">
            <div class="me-auto small text-muted">Sort by:</div>
            <div class="btn-group btn-group-sm" role="group" aria-label="Sort">
                <button type="button" class="btn btn-sm btn-outline-light sort-btn" data-key="cid">CID <span class="sort-caret"></span></button>
                <button type="button" class="btn btn-sm btn-outline-light sort-btn" data-key="name">Name <span class="sort-caret"></span></button>
                <button type="button" class="btn btn-sm btn-outline-light sort-btn" data-key="email">Email <span class="sort-caret"></span></button>
            </div>
        </div>

        {% include 'generic/partials/cid_user_list.html' with exam=exam current_cid_users=current_cid_users available_cid_users=available_cid_users %}
    </div>
</div>

<script>
    document.addEventListener('DOMContentLoaded', function(){
    // Bulk Add / Remove using a single HTMX POST
        const bulkForm = document.createElement('form');
        bulkForm.id = 'bulk-cid-form';
        bulkForm.className = 'd-none';
        bulkForm.setAttribute('hx-target', '#cid-users-wrap');
        bulkForm.setAttribute('hx-swap', 'outerHTML');
        const bulkInput = document.createElement('input'); bulkInput.type='hidden'; bulkInput.name='bulk_pks'; bulkInput.id='bulk_pks_input'; bulkForm.appendChild(bulkInput);
        const bulkAddInput = document.createElement('input'); bulkAddInput.type='hidden'; bulkAddInput.name='add'; bulkAddInput.id='bulk_add_input'; bulkForm.appendChild(bulkAddInput);
    // include csrf token
        const csrfTokenInput = document.createElement('input'); csrfTokenInput.type='hidden'; csrfTokenInput.name='csrfmiddlewaretoken'; csrfTokenInput.value='{{ csrf_token }}'; bulkForm.appendChild(csrfTokenInput);
        document.body.appendChild(bulkForm);

        function setLoading(btn, loading){
            if(!btn) return;
            try{
                if(loading){
                    btn.dataset._origDisabled = btn.disabled ? '1' : '0';
                    btn.disabled = true;
                    const spinner = document.createElement('span');
                    spinner.className = 'btn-spinner spinner-border spinner-border-sm ms-2';
                    spinner.setAttribute('role','status');
                    spinner.setAttribute('aria-hidden','true');
                    btn.appendChild(spinner);
                } else {
                    if(typeof btn.dataset._origDisabled !== 'undefined'){
                        btn.disabled = btn.dataset._origDisabled === '1';
                        delete btn.dataset._origDisabled;
                    } else {
                        btn.disabled = false;
                    }
                    const s = btn.querySelector('.btn-spinner'); if(s) s.remove();
                }
            }catch(e){ /* ignore */ }
        }

        function submitBulk(url, pks, add, triggeringButton){
            // prefer HTMX if available
            if (window.htmx && typeof htmx.submit === 'function'){
                if(triggeringButton) setLoading(triggeringButton, true);
                bulkForm.setAttribute('hx-post', url);
                bulkInput.value = JSON.stringify(pks);
                if (add === null) {
                    bulkAddInput.disabled = true;
                } else {
                    bulkAddInput.disabled = false;
                    bulkAddInput.value = add ? 'true' : 'false';
                }
                htmx.submit(bulkForm);
                return;
            }

            // fallback: use fetch and swap the list manually
            if(triggeringButton) setLoading(triggeringButton, true);
            const fd = new FormData();
            fd.append('bulk_pks', JSON.stringify(pks));
            if (add !== null) fd.append('add', add ? 'true' : 'false');
            const csrf = document.querySelector('input[name="csrfmiddlewaretoken"]').value;
            const headers = { 'X-CSRFToken': csrf };
            fetch(url, { method: 'POST', body: fd, headers: headers, credentials: 'same-origin' })
                .then(resp => resp.text().then(text => ({ resp, text })))
                .then(({ resp, text }) => {
                    // swap the list
                    try{
                        const parser = new DOMParser();
                        const doc = parser.parseFromString(text, 'text/html');
                        // Prefer replacing the whole wrapper (contains counts + list)
                        const newWrap = doc.querySelector('#cid-users-wrap');
                        if (newWrap){
                            const oldWrap = document.querySelector('#cid-users-wrap');
                            if (oldWrap) oldWrap.replaceWith(newWrap);
                        } else {
                            const newList = doc.querySelector('#cid-users-list');
                            if (newList){
                                const old = document.querySelector('#cid-users-list');
                                if (old) old.replaceWith(newList);
                            }
                        }
                    }catch(e){ console.error('bulk swap failed', e); }
                    // dispatch HX-Trigger equivalent if present
                    const trigger = resp.headers.get('HX-Trigger');
                    if (trigger){
                        try{ const detail = JSON.parse(trigger); for(const k in detail){ document.body.dispatchEvent(new CustomEvent(k, { detail: detail[k] })); } }catch(e){}
                    }
                }).catch(err => { console.error('bulk request failed', err); })
                .finally(()=>{ if(triggeringButton) setLoading(triggeringButton, false); });
        }

        document.querySelectorAll('.add-all-btn').forEach(function(btn){
            btn.addEventListener('click', function(){
                const url = btn.dataset.bulkUrl;
                const items = document.querySelectorAll('#cid-users-list li:not(.current)');
                const pks = Array.from(items).map(i => i.dataset.pk).filter(Boolean).map(Number);
                if (!pks.length) return;
                submitBulk(url, pks, true, btn);
            });
        });
        document.querySelectorAll('.remove-all-btn').forEach(function(btn){
            btn.addEventListener('click', function(){
                if(!confirm('Remove all selected users from the exam?')) return;
                const url = btn.dataset.bulkUrl;
                const items = document.querySelectorAll('#cid-users-list li.current');
                const pks = Array.from(items).map(i => i.dataset.pk).filter(Boolean).map(Number);
                if (!pks.length) return;
                submitBulk(url, pks, false, btn);
            });
        });

        // Toggle all: send mixed per-item {pk, add} payload so server can apply add/remove per item
        document.querySelectorAll('.toggle-all-btn').forEach(function(btn){
            btn.addEventListener('click', function(){
                const url = btn.dataset.bulkUrl || document.querySelector('.add-all-btn')?.dataset.bulkUrl;
                // build mixed list: for each li include {pk, add}
                const items = Array.from(document.querySelectorAll('#cid-users-list li')).map(function(li){
                    return { pk: Number(li.dataset.pk), add: !li.classList.contains('current') };
                });
                if (!items.length) return;
                submitBulk(url, items, null, btn);
            });
        });

        // HTMX: listen for server HX-Trigger to show toasts for toggles and update counts
        document.body.addEventListener('cid_toggled', function(evt){
            try {
                const detail = evt.detail || {};
                const added = detail.added;
                if (window.toastr) {
                    if (added) toastr.info(`User added to exam`);
                    else toastr.info(`User removed from exam`);
                }
                // Update counts for single-item toggles (bulk swaps replace the whole wrapper)
                const addedEl = document.getElementById('cid-count-added');
                const availEl = document.getElementById('cid-count-available');
                if (addedEl && availEl){
                    let addedCount = parseInt(addedEl.textContent || '0') || 0;
                    let availCount = parseInt(availEl.textContent || '0') || 0;
                    if (added){ addedCount += 1; availCount = Math.max(0, availCount - 1); }
                    else { addedCount = Math.max(0, addedCount - 1); availCount += 1; }
                    addedEl.textContent = String(addedCount);
                    availEl.textContent = String(availCount);
                }
            } catch(e) { /* ignore */ }
        });

        // HTMX: clear loading indicators when requests finish
        document.body.addEventListener('htmx:afterRequest', function(evt){
            try{
                const elt = (evt.detail && evt.detail.elt) || null;
                let btn = null;
                if(elt){
                    if(elt.tagName === 'BUTTON') btn = elt;
                    else if(elt.tagName === 'FORM') btn = elt.querySelector('button[type=submit]');
                    else btn = elt.closest && elt.closest('button') ? elt.closest('button') : null;
                }
                if(btn) setLoading(btn, false);
            }catch(e){ }
        });

        // Delegate toggle button clicks so newly-inserted items work
        document.body.addEventListener('click', function(e){
            const btn = e.target.closest && e.target.closest('.toggle-btn');
            if(!btn) return;
            // find enclosing form
            const form = btn.closest('form');
            if(!form) return;
            e.preventDefault();
            setLoading(btn, true);
            if(window.htmx && typeof htmx.submit === 'function'){
                htmx.submit(form);
                return;
            }

            // fallback: send via fetch and swap the nearest li with returned HTML
            (async function(){
                try{
                    const url = form.action || form.getAttribute('hx-post');
                    const fd = new FormData(form);
                    const csrf = document.querySelector('input[name="csrfmiddlewaretoken"]')?.value;
                    const headers = { 'HX-Request': 'true' };
                    if(csrf) headers['X-CSRFToken'] = csrf;

                    const resp = await fetch(url, { method: 'POST', body: fd, headers: headers, credentials: 'same-origin' });
                    const text = await resp.text();
                    // try to parse HTML and replace the closest li
                    try{
                        const parser = new DOMParser();
                        const doc = parser.parseFromString(text, 'text/html');
                        // the server should return the li outerHTML
                        const newLi = doc.querySelector('li');
                        const oldLi = form.closest('li');
                        if(newLi && oldLi){ oldLi.replaceWith(newLi); }
                        // dispatch any HX-Trigger header events
                        const trigger = resp.headers.get('HX-Trigger');
                        if (trigger){ try{ const detail = JSON.parse(trigger); for(const k in detail){ document.body.dispatchEvent(new CustomEvent(k, { detail: detail[k] })); } }catch(e){} }
                    }catch(err){ console.error('toggle parse/swap failed', err); }
                }catch(err){ console.error('toggle request failed', err); }
                finally{ setLoading(btn, false); }
            })();
        });

    // Sorting: pure JS implementation
        let currentSort = { key: 'cid', dir: 'asc' };
        function updateCarets(){
            document.querySelectorAll('.sort-btn').forEach(function(b){
                const key = b.dataset.key;
                const caret = b.querySelector('.sort-caret');
                if (caret) caret.textContent = (currentSort.key === key) ? (currentSort.dir === 'asc' ? ' ▲' : ' ▼') : '';
            });
        }
        function sortList(key){
            const ul = document.getElementById('cid-users-list');
            const items = Array.from(ul.children);
            const dir = (currentSort.key === key && currentSort.dir === 'asc') ? 'desc' : 'asc';
            items.sort(function(a,b){
                const va = (a.dataset[key] || '').toString().toLowerCase();
                const vb = (b.dataset[key] || '').toString().toLowerCase();
                if (va < vb) return dir === 'asc' ? -1 : 1;
                if (va > vb) return dir === 'asc' ? 1 : -1;
                return 0;
            });
            items.forEach(function(it){ ul.appendChild(it); });
            currentSort = { key: key, dir: dir };
            updateCarets();
        }
        document.querySelectorAll('.sort-btn').forEach(function(b){ b.addEventListener('click', function(){ sortList(b.dataset.key); }); });
        updateCarets();
    });
</script>

<style>
    /* Compact, dark-mode adjustments */
    .card.bg-dark { background-color: #111 !important; }
    .card-body { padding: .5rem !important; }
    .list-group-item { padding: .35rem .5rem; font-size: .88rem; border-left: 0; border-right: 0; }
    .list-group-item.current { background: rgba(255,255,255,0.03); border-left: 4px solid rgba(13,110,253,0.25); }
    .list-group-item .small { display:block; color: #bfc6cf; }
    .btn-group .btn { padding: .25rem .45rem; font-size: .8rem; }
    .btn-outline-light { color: #e9ecef; border-color: rgba(233,236,239,0.12); }
    .btn-outline-light:hover { color:#fff; background: rgba(233,236,239,0.06); }
</style>