/**
 * Slate Forms — public-form logic engine.
 *
 * Progressive enhancement for advanced field behaviour on the live form:
 *   - Conditional visibility  (fields with [data-showif])
 *   - Calculated fields       (fields with [data-formula])   — Phase 3
 *   - Multi-step / wizard      (.forms-steps)                — Phase 2
 *
 * No dependencies. Degrades gracefully: with JS off, every field is shown
 * (the server still skips validation for unmet conditions).
 */
(function () {
    'use strict';

    var form = document.querySelector('.forms-public-card form') || document.querySelector('form');
    if (!form) return;

    var doAnimate  = form.getAttribute('data-animate')  !== '0';
    var doValidate = form.getAttribute('data-validate') !== '0';

    function esc(name) {
        return (window.CSS && CSS.escape) ? CSS.escape(name) : String(name).replace(/"/g, '\\"');
    }

    // Current value of a named field (handles radio groups + checkboxes).
    function fieldValue(name) {
        var els = form.querySelectorAll('[name="' + esc(name) + '"]');
        if (!els.length) return '';
        var el = els[0];
        if (el.type === 'radio') {
            var c = form.querySelector('[name="' + esc(name) + '"]:checked');
            return c ? c.value : '';
        }
        if (el.type === 'checkbox') return el.checked ? (el.value || '1') : '';
        return el.value || '';
    }

    // ── Conditional visibility ──────────────────────────────────
    function conditionMet(c) {
        var cur = String(fieldValue(c.field) || '').trim();
        var v   = String(c.value || '').trim();
        switch (c.op) {
            case 'eq':     return cur === v;
            case 'ne':     return cur !== v;
            case 'filled': return cur !== '';
            case 'empty':  return cur === '';
            case 'gt':     return parseFloat(cur) >  parseFloat(v);
            case 'lt':     return parseFloat(cur) <  parseFloat(v);
        }
        return true;
    }

    var conditional = [];
    Array.prototype.forEach.call(form.querySelectorAll('[data-showif]'), function (el) {
        var c; try { c = JSON.parse(el.getAttribute('data-showif')); } catch (e) { return; }
        if (c && c.field) conditional.push({ el: el, cond: c });
    });

    function applyConditions() {
        conditional.forEach(function (item) {
            var show = conditionMet(item.cond);
            item.el.hidden = !show;
            // Disabled inputs aren't submitted or natively validated, so a
            // hidden required field can never block the submit.
            Array.prototype.forEach.call(item.el.querySelectorAll('input, select, textarea'), function (inp) {
                inp.disabled = !show;
            });
        });
    }

    // ── Calculated fields ───────────────────────────────────────
    var calcInputs = Array.prototype.slice.call(form.querySelectorAll('input[data-formula]'));

    function evalFormula(formula) {
        // Replace {field} refs with numeric values, then evaluate a guarded
        // arithmetic expression (only digits and + - * / ( ) remain).
        var expr = String(formula || '').replace(/\{([a-z0-9_]+)\}/gi, function (_, n) {
            var v = parseFloat(fieldValue(n)); return isFinite(v) ? v : 0;
        });
        if (!/^[0-9+\-*/().\s]*$/.test(expr) || expr.trim() === '') return null;
        try {
            var r = Function('"use strict";return (' + expr + ')')();
            return (typeof r === 'number' && isFinite(r)) ? r : null;
        } catch (e) { return null; }
    }

    function runCalc() {
        calcInputs.forEach(function (inp) {
            var r = evalFormula(inp.getAttribute('data-formula'));
            var val = (r === null) ? '' : (Math.round(r * 100) / 100);
            inp.value = val;
            var out = inp.parentNode && inp.parentNode.querySelector('[data-calc-out]');
            if (out) out.textContent = (val === '' ? '—' : val);
        });
    }

    // ── Wire up ─────────────────────────────────────────────────
    function recompute() {
        applyConditions();
        runCalc();
    }
    form.addEventListener('input', recompute);
    form.addEventListener('change', recompute);

    // ── Multi-step wizard (directional slide + staggered entrance) ─
    var wrap = form.querySelector('[data-forms-steps]');
    if (wrap) {
        var stepsEls  = Array.prototype.slice.call(wrap.querySelectorAll('[data-forms-step]'));
        var total     = stepsEls.length;
        var backBtn   = wrap.querySelector('[data-forms-back]');
        var nextBtn   = wrap.querySelector('[data-forms-next]');
        var submitBtn = wrap.querySelector('[data-forms-submit]');
        var numEl     = wrap.querySelector('[data-forms-stepnum]');
        var titleEl   = wrap.querySelector('[data-forms-steptitle]');
        var progress  = wrap.querySelector('[data-forms-progress]');
        var railItems = Array.prototype.slice.call(wrap.querySelectorAll('[data-rail]'));
        var cur = 0;

        var animate = function (el, dir) {
            if (!doAnimate) return;
            el.classList.remove('anim-next', 'anim-prev');
            void el.offsetWidth;                        // force reflow → restart animation
            el.classList.add(dir === 'prev' ? 'anim-prev' : 'anim-next');
            Array.prototype.forEach.call(el.children, function (c, k) {
                c.style.animationDelay = Math.min(k * 45, 340) + 'ms';
            });
        };

        var showStep = function (i, dir) {
            cur = Math.max(0, Math.min(total - 1, i));
            stepsEls.forEach(function (s, j) { s.hidden = (j !== cur); });
            if (backBtn)   backBtn.hidden   = (cur === 0);
            if (nextBtn)   nextBtn.hidden   = (cur === total - 1);
            if (submitBtn) submitBtn.hidden = (cur !== total - 1);
            if (numEl)     numEl.textContent = (cur + 1);
            if (titleEl)   titleEl.textContent = stepsEls[cur].getAttribute('data-step-title') || '';
            if (progress)  progress.style.width = Math.round(((cur + 1) / total) * 100) + '%';
            railItems.forEach(function (it, j) {
                it.classList.toggle('is-active', j === cur);
                it.classList.toggle('is-done', j < cur);
            });
            animate(stepsEls[cur], dir);
            recompute();
            try { wrap.scrollIntoView({ block: 'nearest', behavior: 'smooth' }); } catch (e) {}
        };

        var stepValid = function (i) {
            var ok = true;
            var inputs = stepsEls[i].querySelectorAll('input, select, textarea');
            for (var k = 0; k < inputs.length; k++) {
                if (inputs[k].disabled) continue;
                if (typeof inputs[k].checkValidity === 'function' && !inputs[k].checkValidity()) {
                    if (ok && inputs[k].reportValidity) inputs[k].reportValidity();
                    markTouched(inputs[k]);
                    ok = false;
                }
            }
            return ok;
        };

        if (nextBtn) nextBtn.addEventListener('click', function () { if (stepValid(cur)) showStep(cur + 1, 'next'); });
        if (backBtn) backBtn.addEventListener('click', function () { showStep(cur - 1, 'prev'); });
        railItems.forEach(function (it, j) { it.addEventListener('click', function () { if (j < cur) showStep(j, 'prev'); }); });

        var errStep = -1;
        stepsEls.forEach(function (s, j) { if (errStep === -1 && s.querySelector('.field-error')) errStep = j; });
        showStep(errStep === -1 ? 0 : errStep, 'next');
    }

    // ── Inline validation (live green-check / red as you type) ──
    function vState(inp) {
        if (inp.disabled) return null;
        var v = (inp.value || '').trim();
        if (v === '') return inp.required ? 'invalid' : 'neutral';
        var t = (inp.getAttribute('type') || inp.tagName.toLowerCase());
        if (t === 'email')  return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v) ? 'valid' : 'invalid';
        if (t === 'url')    return /^https?:\/\/.+/i.test(v) ? 'valid' : 'invalid';
        if (t === 'number') return (!isNaN(parseFloat(v)) && isFinite(v)) ? 'valid' : 'invalid';
        if (t === 'tel')    return /^[0-9+()\-.\s]{4,40}$/.test(v) ? 'valid' : 'invalid';
        return 'valid';
    }
    function vMsg(inp) {
        if ((inp.value || '').trim() === '') return 'This field is required.';
        var t = inp.getAttribute('type');
        if (t === 'email')  return 'Enter a valid email address.';
        if (t === 'url')    return 'Enter a valid URL (https://…).';
        if (t === 'number') return 'Enter a number.';
        if (t === 'tel')    return 'Enter a valid phone number.';
        return 'Please check this field.';
    }
    function applyState(inp) {
        var field = inp.closest && inp.closest('.field');
        if (!field) return;
        var st = vState(inp);
        field.classList.remove('is-valid', 'is-invalid');
        var err = field.querySelector('.js-err');
        if (st === 'valid') {
            field.classList.add('is-valid');
        } else if (st === 'invalid') {
            field.classList.add('is-invalid');
            if (!err) { err = document.createElement('div'); err.className = 'field-error js-err'; field.appendChild(err); }
            err.textContent = vMsg(inp);
            return;
        }
        if (err && err.parentNode) err.parentNode.removeChild(err);
    }
    function markTouched(inp) { if (!doValidate) return; inp.setAttribute('data-touched', '1'); applyState(inp); }

    if (doValidate) {
        Array.prototype.forEach.call(form.querySelectorAll('.field input, .field textarea, .field select'), function (inp) {
            var t = inp.type;
            if (t === 'checkbox' || t === 'radio' || t === 'hidden' || t === 'range' || t === 'file' || t === 'submit' || t === 'button') return;
            inp.addEventListener('blur',  function () { markTouched(inp); });
            inp.addEventListener('input', function () { if (inp.getAttribute('data-touched')) applyState(inp); });
        });
    }

    recompute();
})();
