// api-actions.js — generic "tables over an API collection" layer.
//
// When the injected #table-context carries an `apiActions` block, this turns
// the otherwise read-only table into a managed collection backed by a REST
// endpoint, WITHOUT touching the file-save/row-ops machinery (which is bound
// to <dir>/*.yaml row files). It drives create + per-row delete against the
// configured URLs and reloads on success (the server re-renders the fresh
// list). First consumer: the self-service token page at /.tokens.
//
//   apiActions: {
//     create:    { url, title?, fields:[{name,label,placeholder?,type?}], secretField?, secretLabel? },
//     deleteRow: { urlTemplate (with {id}), label?, confirm? }   // {id} ← row's data-url
//   }
(function (app) {
    'use strict';

    function ctxObj() {
        return (app && app.context) || {};
    }
    function cfg() {
        return ctxObj().apiActions || null;
    }
    // Active when the table is an API collection (apiActions) OR a read-only
    // server-injected view (readOnly) — either way the file-model toolbar
    // buttons (+ Add row / Save) don't apply and are hidden.
    function active() {
        return !!(cfg() || ctxObj().readOnly);
    }

    function el(tag, attrs, text) {
        var e = document.createElement(tag);
        if (attrs) Object.keys(attrs).forEach(function (k) { e.setAttribute(k, attrs[k]); });
        if (text != null) e.textContent = text;
        return e;
    }

    // ── Create ────────────────────────────────────────────────────────────
    var createMounted = false;
    function mountCreate(c) {
        if (createMounted) return;
        var bar = document.querySelector('.table-toolbar__left') || document.getElementById('table-toolbar');
        if (!bar) return;
        // The native "+ Add row" posts to the form-create file endpoint, which
        // doesn't apply to an API collection — hide it; this button replaces it.
        var native = document.getElementById('table-add-row');
        if (native) native.hidden = true;
        var btn = el('button', { type: 'button', class: 'btn btn-primary btn-sm', id: 'api-create-btn' }, '+ ' + (c.title || 'New'));
        btn.addEventListener('click', function () { openCreate(c); });
        bar.appendChild(btn);
        createMounted = true;
    }

    function openCreate(c) {
        var overlay = el('div', { class: 'api-modal__overlay' });
        var modal = el('div', { class: 'api-modal' });
        modal.appendChild(el('h2', { class: 'api-modal__title' }, c.title || 'New'));
        var form = el('form', { class: 'api-modal__form' });
        var inputs = {};
        (c.fields || []).forEach(function (f) {
            var lab = el('label', { class: 'api-modal__field' });
            lab.appendChild(el('span', null, (f.label || f.name) + (f.required ? ' *' : '')));
            var inp = el('input', { type: f.type || 'text' });
            if (f.placeholder) inp.setAttribute('placeholder', f.placeholder);
            if (f.required) inp.required = true;
            inputs[f.name] = inp;
            lab.appendChild(inp);
            form.appendChild(lab);
        });
        var err = el('div', { class: 'api-modal__err', hidden: 'hidden' });
        form.appendChild(err);
        var actions = el('div', { class: 'api-modal__actions' });
        var cancel = el('button', { type: 'button', class: 'btn btn-secondary btn-sm' }, 'Cancel');
        var submit = el('button', { type: 'submit', class: 'btn btn-primary btn-sm' }, 'Create');
        actions.appendChild(cancel); actions.appendChild(submit);
        form.appendChild(actions);
        modal.appendChild(form);
        overlay.appendChild(modal);
        document.body.appendChild(overlay);
        var firstInput = form.querySelector('input');
        if (firstInput) firstInput.focus();

        function close() { if (overlay.parentNode) overlay.parentNode.removeChild(overlay); }
        cancel.addEventListener('click', close);
        overlay.addEventListener('click', function (e) { if (e.target === overlay) close(); });

        form.addEventListener('submit', function (e) {
            e.preventDefault();
            err.hidden = true;
            var missing = (c.fields || []).filter(function (f) { return f.required && !inputs[f.name].value.trim(); });
            if (missing.length) {
                err.textContent = 'Required: ' + missing.map(function (f) { return f.label || f.name; }).join(', ');
                err.hidden = false;
                return;
            }
            var body = {};
            (c.fields || []).forEach(function (f) {
                var v = inputs[f.name].value.trim();
                if (!v) return;
                // Date fields → RFC3339 so the Go time.Time decoder accepts them.
                body[f.name] = (f.type === 'date') ? new Date(v + 'T00:00:00').toISOString() : v;
            });
            // Constant fields the server requires but the user doesn't set
            // (e.g. project create's parent="/").
            if (c.fixed) Object.keys(c.fixed).forEach(function (k) { body[k] = c.fixed[k]; });
            submit.disabled = true;
            fetch(c.url, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                credentials: 'same-origin',
                body: JSON.stringify(body)
            }).then(function (r) {
                return r.text().then(function (t) { return { ok: r.ok, status: r.status, text: t }; });
            }).then(function (res) {
                if (!res.ok) {
                    submit.disabled = false;
                    err.textContent = 'Create failed: ' + res.status + ' ' + res.text;
                    err.hidden = false;
                    return;
                }
                close();
                var secret = '';
                if (c.secretField) {
                    try { secret = (JSON.parse(res.text) || {})[c.secretField] || ''; } catch (_e) { /* ignore */ }
                }
                if (secret) showSecret(c.secretLabel || 'New secret (shown once):', secret);
                else location.reload();
            }).catch(function (e2) {
                submit.disabled = false;
                err.textContent = 'Create failed: ' + (e2 && e2.message ? e2.message : e2);
                err.hidden = false;
            });
        });
    }

    function showSecret(label, secret) {
        var overlay = el('div', { class: 'api-modal__overlay' });
        var modal = el('div', { class: 'api-modal' });
        modal.appendChild(el('p', { class: 'api-modal__secret-label' }, label));
        var box = el('div', { class: 'api-modal__secret' }, secret);
        modal.appendChild(box);
        var actions = el('div', { class: 'api-modal__actions' });
        var copy = el('button', { type: 'button', class: 'btn btn-secondary btn-sm' }, 'Copy');
        copy.addEventListener('click', function () {
            if (navigator.clipboard) navigator.clipboard.writeText(secret);
            copy.textContent = 'Copied';
        });
        var done = el('button', { type: 'button', class: 'btn btn-primary btn-sm' }, 'Done');
        done.addEventListener('click', function () { location.reload(); });
        actions.appendChild(copy); actions.appendChild(done);
        modal.appendChild(actions);
        overlay.appendChild(modal);
        document.body.appendChild(overlay);
    }

    // ── Per-row delete ──────────────────────────────────────────────────────
    function ensureRowDelete(d) {
        var tbody = document.querySelector('#table-root tbody');
        if (!tbody) return;
        var trs = tbody.querySelectorAll('tr');
        for (var i = 0; i < trs.length; i++) {
            var tr = trs[i];
            if (tr.querySelector('.api-revoke')) continue;
            var id = tr.getAttribute('data-url');
            if (!id) continue;
            var cell = tr.lastElementChild;
            if (!cell) continue;
            var b = el('button', { type: 'button', class: 'btn btn-secondary btn-sm api-revoke' }, d.label || 'Delete');
            (function (rowId) {
                b.addEventListener('click', function () { revoke(d, rowId); });
            })(id);
            cell.appendChild(b);
        }
    }

    function revoke(d, id) {
        if (d.confirm && !window.confirm(d.confirm)) return;
        var url = d.urlTemplate.replace('{id}', encodeURIComponent(id));
        fetch(url, { method: 'DELETE', credentials: 'same-origin' }).then(function (r) {
            if (r.ok || r.status === 204) location.reload();
            else r.text().then(function (t) { window.alert('Delete failed: ' + r.status + ' ' + t); });
        }).catch(function (e) { window.alert('Delete failed: ' + (e && e.message ? e.message : e)); });
    }

    // Suppress the file-model toolbar affordances that don't apply to an API
    // collection: native "+ Add row" (posts to the form-create file endpoint)
    // and "Save" (flushes dirty row files). Re-run each tick in case main.js
    // toggles them after us.
    function hideNative() {
        // Use inline display:none, not the [hidden] attr — the .btn display
        // rule overrides [hidden] and the buttons would stay visible.
        ['table-add-row', 'table-save'].forEach(function (id) {
            var b = document.getElementById(id);
            if (b) b.style.display = 'none';
        });
    }

    // Per-row navigation: clicking a row opens its data-url (the project /
    // subtree it represents) — used by the profile "Effective access" table.
    // Clicks on inner controls (buttons/links/inputs) are left alone.
    function ensureRowNav() {
        var tbody = document.querySelector('#table-root tbody');
        if (!tbody) return;
        var trs = tbody.querySelectorAll('tr');
        for (var i = 0; i < trs.length; i++) {
            var tr = trs[i];
            if (tr.getAttribute('data-nav') === '1') continue;
            var url = tr.getAttribute('data-url');
            if (!url) continue;
            tr.setAttribute('data-nav', '1');
            tr.style.cursor = 'pointer';
            (function (target) {
                // Capture phase: fire before the tables editor's per-cell
                // click handlers (which would otherwise swallow the click on
                // read-only rows). Inner controls (buttons/links/inputs) still
                // opt out.
                tr.addEventListener('click', function (e) {
                    if (e.target.closest('button, a, input')) return;
                    window.location.href = target;
                }, true);
            })(url);
        }
    }

    function tick() {
        if (!active()) return;
        hideNative();
        var c = cfg();
        if (!c) return; // read-only view: native buttons hidden, nothing more
        if (c.create) mountCreate(c.create);
        if (c.deleteRow) ensureRowDelete(c.deleteRow);
        if (c.rowNav) ensureRowNav();
    }

    function start() {
        // app.context is set asynchronously by main.js (await context.load()).
        // Poll until it's present, then run once + observe the tbody so the
        // per-row buttons survive sort/filter re-renders.
        var tries = 0;
        var iv = setInterval(function () {
            if (active() || tries++ > 60) {
                clearInterval(iv);
                if (!active()) return;
                tick();
                var tbody = document.querySelector('#table-root tbody');
                if (tbody && window.MutationObserver) {
                    new MutationObserver(function () { tick(); }).observe(tbody, { childList: true });
                }
            }
        }, 100);
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', start);
    } else {
        start();
    }
})(window.tablesApp = window.tablesApp || {});
