/**
 * All-in-one interactive preview for questionnaire editing.
 * All visible questions on one scrollable page; branching updates visibility live.
 * Answers stay in memory only — nothing is saved to the server.
 */

const GLASS_LABELS = [
    { key: 'never_glass', label: 'Never' },
    { key: 'little_glass', label: 'A little' },
    { key: 'moderate_glass', label: 'Moderate' },
    { key: 'much_glass', label: 'Much' },
    { key: 'extreme_glass', label: 'Extreme' },
];

/** Sample codes shown in preview (read-only), mimicking the app after load/login. */
const PREVIEW_CLIENT_CODE = 'CLIENT-042';
const PREVIEW_COACH_CODE = 'coach.demo';

const MONTHS = [
    'January', 'February', 'March', 'April', 'May', 'June',
    'July', 'August', 'September', 'October', 'November', 'December',
];

function esc(s) {
    const d = document.createElement('div');
    d.textContent = s ?? '';
    return d.innerHTML;
}

function parseConfig(q) {
    try { return JSON.parse(q.configJson || '{}'); } catch { return {}; }
}

function localId(q) {
    return q.localId || (q.questionID || '').split('__').pop() || '';
}

function questionKey(q) {
    const cfg = parseConfig(q);
    return (q.questionKey || cfg.questionKey || '').trim();
}

function optionKeyOf(o) {
    if (o.optionKey) return o.optionKey;
    const dt = (o.defaultText || '').trim();
    return /^[a-zA-Z][a-zA-Z0-9_]*$/.test(dt) ? dt : dt;
}

function optionLabel(o) {
    return (o.labelGerman || '').trim() || o.defaultText || o.optionKey || '?';
}

function glassScaleOptions(q) {
    const opts = q.answerOptions || [];
    if (opts.length) {
        return opts.map(o => ({ key: optionKeyOf(o), label: optionLabel(o) }));
    }
    return GLASS_LABELS;
}

function valueSpinnerValues(config) {
    const range = config.range || { min: 0, max: 10 };
    const min = Number(range.min ?? 0);
    const max = Number(range.max ?? 10);
    const lo = Math.min(min, max);
    const hi = Math.max(min, max);
    const values = [];
    for (let v = lo; v <= hi; v++) values.push(v);
    return values;
}

function hasBranchingOptions(q) {
    const layout = q.type || '';
    const config = parseConfig(q);
    if (layout === 'radio_question') {
        return (q.answerOptions || []).some(o => o.nextQuestionId?.trim());
    }
    if (layout === 'value_spinner') {
        const opts = config.valueOptions || config.options || [];
        return opts.some(o => o.nextQuestionId?.trim()) || Boolean(config.nextQuestionId?.trim());
    }
    if (layout === 'string_spinner') {
        return Boolean(config.nextQuestionId?.trim() || config.otherNextQuestionId?.trim());
    }
    if (layout === 'multi_check_box_question') {
        return Boolean(config.nextQuestionId?.trim() || config.otherNextQuestionId?.trim());
    }
    const withQuestionNext = new Set([
        'free_text', 'date_spinner', 'slider_question', 'glass_scale_question',
        'client_coach_code_question',
    ]);
    return withQuestionNext.has(layout) && Boolean(config.nextQuestionId?.trim());
}

function resolveNextLocalId(q, answer) {
    const layout = q.type || '';
    const config = parseConfig(q);

    if (layout === 'radio_question') {
        const key = typeof answer === 'string' ? answer : '';
        const opt = (q.answerOptions || []).find(o => optionKeyOf(o) === key);
        return opt?.nextQuestionId?.trim() || null;
    }
    if (layout === 'value_spinner') {
        const num = parseInt(String(answer), 10);
        const branch = (config.valueOptions || config.options || [])
            .find(o => Number(o.value) === num);
        if (branch?.nextQuestionId?.trim()) return branch.nextQuestionId.trim();
        return config.nextQuestionId?.trim() || null;
    }
    if (layout === 'string_spinner') {
        const val = String(answer ?? '');
        const otherKey = config.otherOptionKey?.trim();
        const otherNext = config.otherNextQuestionId?.trim();
        if (otherKey && otherNext && val === otherKey) return otherNext;
        return config.nextQuestionId?.trim() || null;
    }
    if (layout === 'multi_check_box_question') {
        const keys = Array.isArray(answer) ? answer : [];
        const otherKey = config.otherOptionKey?.trim();
        const otherNext = config.otherNextQuestionId?.trim();
        if (otherKey && otherNext && keys.includes(otherKey)) return otherNext;
        return config.nextQuestionId?.trim() || null;
    }
    return config.nextQuestionId?.trim() || null;
}

function readAnswerFromBody(body, q) {
    if (!body) return null;
    const layout = q.type || '';
    const config = parseConfig(q);

    switch (layout) {
        case 'radio_question': {
            const checked = body.querySelector('input[type=radio]:checked');
            return checked ? checked.value : null;
        }
        case 'multi_check_box_question': {
            const boxes = body.querySelectorAll('input[type=checkbox]:checked');
            return [...boxes].map(el => el.value);
        }
        case 'value_spinner':
        case 'string_spinner': {
            const sel = body.querySelector('select.qp-select');
            const val = sel?.value ?? '';
            return val === '' ? null : val;
        }
        case 'slider_question': {
            const range = body.querySelector('input[type=range]');
            return range ? range.value : null;
        }
        case 'date_spinner': {
            const precision = config.precision || 'full';
            const year = body.querySelector('[data-part=year]')?.value;
            if (!year) return null;
            if (precision === 'year') return year;
            const month = body.querySelector('[data-part=month]')?.value || '01';
            if (precision === 'year_month') return `${year}-${month}`;
            const day = body.querySelector('[data-part=day]')?.value || '01';
            return `${year}-${month}-${day}`;
        }
        case 'free_text': {
            const text = body.querySelector('input[type=text], textarea')?.value?.trim();
            return text || null;
        }
        case 'glass_scale_question': {
            const symptoms = config.symptoms || [];
            if (!symptoms.length) return {};
            const result = {};
            for (const sym of symptoms) {
                const picked = body.querySelector(`input[data-symptom="${CSS.escape(sym)}"]:checked`);
                if (!picked) return null;
                result[sym] = picked.value;
            }
            return result;
        }
        case 'client_coach_code_question': {
            const client = body.querySelector('[data-field=client]')?.value?.trim();
            const coach = body.querySelector('[data-field=coach]')?.value?.trim();
            if (!client && !coach) return null;
            return { client_code: client || '', coach_code: coach || '' };
        }
        case 'last_page':
            return true;
        default:
            return null;
    }
}

function hasAnswerInUi(q, sectionEl) {
    const body = sectionEl?.querySelector('.qp-question-body');
    const layout = q.type || '';
    const config = parseConfig(q);
    const answer = readAnswerFromBody(body, q);

    switch (layout) {
        case 'radio_question':
            return answer !== null;
        case 'multi_check_box_question': {
            const min = config.minSelection || 0;
            const count = Array.isArray(answer) ? answer.length : 0;
            return count >= Math.max(min, 0) && (count > 0 || !q.isRequired);
        }
        case 'value_spinner':
        case 'string_spinner':
            return answer !== null && answer !== '';
        case 'slider_question':
            return answer !== null;
        case 'date_spinner': {
            const year = body?.querySelector('[data-part=year]')?.value;
            if (!year) return false;
            if (config.precision === 'year') return true;
            const month = body?.querySelector('[data-part=month]')?.value;
            if (!month) return false;
            if (config.precision === 'year_month') return true;
            return Boolean(body?.querySelector('[data-part=day]')?.value);
        }
        case 'free_text':
            return Boolean(answer);
        case 'glass_scale_question': {
            const symptoms = config.symptoms || [];
            if (!symptoms.length) return true;
            return answer !== null && typeof answer === 'object'
                && symptoms.every(s => answer[s]);
        }
        case 'client_coach_code_question':
            return Boolean(answer?.client_code && answer?.coach_code);
        case 'last_page':
            return true;
        default:
            return true;
    }
}

function branchAnswerFromSection(q, sectionEl) {
    if (hasBranchingOptions(q) && !hasAnswerInUi(q, sectionEl)) return null;
    const body = sectionEl?.querySelector('.qp-question-body');
    return readAnswerFromBody(body, q);
}

function visibleQuestionIndices(questions, sectionByIdx) {
    const visible = new Set();
    let i = 0;
    while (i < questions.length) {
        const q = questions[i];
        if (q.type === 'last_page') {
            i++;
            continue;
        }
        visible.add(i);
        const sectionEl = sectionByIdx.get(i);
        const answer = sectionEl ? branchAnswerFromSection(q, sectionEl) : null;
        const nextId = answer !== null ? resolveNextLocalId(q, answer) : null;

        if (nextId) {
            const isLastPageTarget = questions.some(
                qq => qq.type === 'last_page' && localId(qq) === nextId,
            );
            if (isLastPageTarget) break;
            const target = questions.findIndex(qq => localId(qq) === nextId);
            i = target >= 0 ? target : i + 1;
        } else if (hasBranchingOptions(q) && answer === null) {
            break;
        } else {
            i++;
        }
    }
    return visible;
}

function symptomRowLabel(q, symptomKey) {
    const rows = q.glassSymptoms || [];
    const row = rows.find(r => r.key === symptomKey);
    return (row?.labelDe || '').trim() || symptomKey;
}

function renderQuestionBody(q, sectionIdx) {
    const layout = q.type || '';
    const config = parseConfig(q);
    const qText = q.defaultText || questionKey(q) || 'Question';
    const prefix = `qp_${sectionIdx}`;

    let inner = '';
    if (config.noteBefore) inner += `<p class="qp-note">${esc(config.noteBefore)}</p>`;
    inner += `<p class="qp-question-text">${esc(qText)}</p>`;

    switch (layout) {
        case 'radio_question':
            inner += `<div class="qp-options">`;
            for (const o of (q.answerOptions || [])) {
                const key = optionKeyOf(o);
                inner += `<label class="qp-option"><input type="radio" name="${prefix}_radio" value="${esc(key)}"> ${esc(optionLabel(o))}</label>`;
            }
            inner += `</div>`;
            break;

        case 'multi_check_box_question':
            inner += `<div class="qp-options">`;
            for (const o of (q.answerOptions || [])) {
                const key = optionKeyOf(o);
                inner += `<label class="qp-option"><input type="checkbox" value="${esc(key)}"> ${esc(optionLabel(o))}</label>`;
            }
            if (config.otherOptionKey && config.otherNextQuestionId) {
                const ok = config.otherOptionKey;
                inner += `<label class="qp-option"><input type="checkbox" value="${esc(ok)}"> ${esc(ok)} (Other)</label>`;
            }
            inner += `</div>`;
            if (config.minSelection) {
                inner += `<p class="qp-hint">Select at least ${config.minSelection} option(s).</p>`;
            }
            break;

        case 'value_spinner': {
            const values = valueSpinnerValues(config);
            inner += `<select class="qp-select"><option value="">— choose —</option>`;
            for (const v of values) inner += `<option value="${v}">${v}</option>`;
            inner += `</select>`;
            break;
        }

        case 'slider_question': {
            const range = config.range || { min: 0, max: 100 };
            const min = Number(range.min ?? 0);
            const max = Number(range.max ?? 100);
            const step = Number(config.step ?? 1) || 1;
            inner += `
                <div class="qp-slider">
                    <input type="range" min="${min}" max="${max}" step="${step}" value="${min}">
                    <output class="qp-slider-value">${min}${config.unitLabel ? ' ' + esc(config.unitLabel) : ''}</output>
                </div>`;
            break;
        }

        case 'date_spinner': {
            const precision = config.precision || 'full';
            const years = [];
            const now = new Date().getFullYear();
            for (let yr = now + 1; yr >= 1900; yr--) years.push(yr);
            inner += `<div class="qp-date-row">`;
            if (precision === 'full') {
                inner += `<select data-part="day" class="qp-select qp-select-sm"><option value="">Day</option>`;
                for (let day = 1; day <= 31; day++) {
                    const ds = String(day).padStart(2, '0');
                    inner += `<option value="${ds}">${day}</option>`;
                }
                inner += `</select>`;
            }
            if (precision !== 'year') {
                inner += `<select data-part="month" class="qp-select qp-select-sm"><option value="">Month</option>`;
                for (let mi = 0; mi < 12; mi++) {
                    const ms = String(mi + 1).padStart(2, '0');
                    inner += `<option value="${ms}">${MONTHS[mi]}</option>`;
                }
                inner += `</select>`;
            }
            inner += `<select data-part="year" class="qp-select qp-select-sm"><option value="">Year</option>`;
            for (const yr of years) inner += `<option value="${yr}">${yr}</option>`;
            inner += `</select></div>`;
            break;
        }

        case 'string_spinner': {
            inner += `<select class="qp-select"><option value="">— choose —</option>`;
            for (const opt of (config.options || [])) {
                inner += `<option value="${esc(opt)}">${esc(opt)}</option>`;
            }
            if (config.otherOptionKey && config.otherNextQuestionId) {
                const ok = config.otherOptionKey;
                inner += `<option value="${esc(ok)}">${esc(ok)} (Other)</option>`;
            }
            inner += `</select>`;
            break;
        }

        case 'free_text':
            inner += `<input type="text" class="qp-text-input" maxlength="${config.maxLength || 500}">`;
            break;

        case 'glass_scale_question': {
            const symptoms = config.symptoms || [];
            const scaleOpts = glassScaleOptions(q);
            if (!symptoms.length) {
                inner += `<p class="qp-hint">Informational screen (no symptom rows).</p>`;
            } else {
                inner += `<div class="qp-glass-table"><table><thead><tr><th>Symptom</th>`;
                for (const so of scaleOpts) inner += `<th>${esc(so.label)}</th>`;
                inner += `</tr></thead><tbody>`;
                symptoms.forEach((sym, si) => {
                    inner += `<tr><td>${esc(symptomRowLabel(q, sym))}</td>`;
                    scaleOpts.forEach(so => {
                        inner += `<td><input type="radio" name="${prefix}_g_${si}" data-symptom="${esc(sym)}" value="${esc(so.key)}"></td>`;
                    });
                    inner += `</tr>`;
                });
                inner += `</tbody></table></div>`;
            }
            break;
        }

        case 'client_coach_code_question':
            inner += `
                <p class="qp-hint qp-review-note">In the app, client and Counselor codes are loaded automatically — Counselor review them here, not type them in.</p>
                <div class="qp-form-stack qp-review-codes">
                    <label>Client code
                        <input type="text" class="qp-readonly-field" data-field="client"
                            value="${esc(PREVIEW_CLIENT_CODE)}" readonly tabindex="-1" aria-readonly="true">
                    </label>
                    <label>Counselor code
                        <input type="text" class="qp-readonly-field" data-field="coach"
                            value="${esc(PREVIEW_COACH_CODE)}" readonly tabindex="-1" aria-readonly="true">
                    </label>
                </div>`;
            break;

        case 'last_page':
            inner += `<p class="qp-hint">${esc(config.textKey || 'End of questionnaire.')}</p>`;
            break;

        default:
            inner += `<p class="qp-hint">Preview not available for layout <code>${esc(layout)}</code>.</p>`;
    }

    if (config.noteAfter) inner += `<p class="qp-note qp-note-after">${esc(config.noteAfter)}</p>`;
    return inner;
}

function layoutLabel(type) {
    const labels = {
        radio_question: 'Radio',
        multi_check_box_question: 'Checkbox',
        glass_scale_question: 'Glass scale',
        value_spinner: 'Value spinner',
        slider_question: 'Slider',
        date_spinner: 'Date',
        string_spinner: 'String spinner',
        free_text: 'Free text',
        client_coach_code_question: 'Client / Counselor code',
        last_page: 'Last page',
    };
    return labels[type] || type || 'Unknown';
}

/**
 * @param {HTMLElement} container
 * @param {{ questionnaireName?: string, questions: object[] }} opts
 */
export function mountQuestionnairePreview(container, opts) {
    const questions = opts.questions || [];

    function render() {
        if (!questions.length) {
            container.innerHTML = `
                <div class="qp-empty">
                    <p>No questions to preview. Add questions on the Questions tab first.</p>
                </div>`;
            return;
        }

        const sections = questions.map((q, idx) => {
            const lid = localId(q);
            const num = idx + 1;
            return `
                <section class="qp-section" data-idx="${idx}" data-qid="${esc(lid)}" hidden>
                    <header class="qp-section-header">
                        <span class="qp-section-num">${num}</span>
                        <div class="qp-section-meta">
                            <code class="qp-id">${esc(lid)}</code>
                            <span class="qp-type">${esc(layoutLabel(q.type))}</span>
                            ${q.isRequired ? '<span class="badge badge-active">Required</span>' : ''}
                        </div>
                    </header>
                    <div class="qp-question-body">
                        ${renderQuestionBody(q, idx)}
                    </div>
                </section>`;
        }).join('');

        container.innerHTML = `
            <div class="qp-shell">
                <div class="qp-banner">Preview mode — all questions on one page. Answers are not saved.</div>
                <div class="qp-toolbar">
                    <span class="qp-visible-count" id="qpVisibleCount"></span>
                    <button type="button" class="btn btn-sm" data-action="restart">Reset all</button>
                </div>
                <div class="qp-scroll card">
                    <form class="qp-form" id="qpForm" novalidate>
                        ${sections}
                    </form>
                </div>
            </div>`;

        wireEvents();
        updateVisibility();
    }

    function sectionElements() {
        return [...container.querySelectorAll('.qp-section')];
    }

    function sectionByIdxMap() {
        const map = new Map();
        for (const el of sectionElements()) {
            map.set(parseInt(el.dataset.idx, 10), el);
        }
        return map;
    }

    function updateVisibility() {
        const map = sectionByIdxMap();
        const visible = visibleQuestionIndices(questions, map);
        let shown = 0;
        for (const el of sectionElements()) {
            const idx = parseInt(el.dataset.idx, 10);
            const isVisible = visible.has(idx);
            el.hidden = !isVisible;
            el.classList.toggle('qp-section-hidden', !isVisible);
            if (isVisible) shown++;
        }
        const counter = container.querySelector('#qpVisibleCount');
        if (counter) {
            counter.textContent = `Showing ${shown} of ${questions.length} questions`;
        }
    }

    function wireEvents() {
        const form = container.querySelector('#qpForm');
        if (!form) return;

        form.addEventListener('input', () => updateVisibility());
        form.addEventListener('change', () => updateVisibility());

        form.querySelectorAll('input[type=range]').forEach(slider => {
            const output = slider.closest('.qp-slider')?.querySelector('.qp-slider-value');
            if (!output) return;
            slider.addEventListener('input', () => {
                output.textContent = slider.value;
            });
        });

        container.querySelector('[data-action=restart]')?.addEventListener('click', () => {
            form.reset();
            form.querySelectorAll('input[type=radio], input[type=checkbox]').forEach(el => {
                el.checked = false;
            });
            form.querySelectorAll('.qp-slider-value').forEach((output, i) => {
                const slider = form.querySelectorAll('input[type=range]')[i];
                if (slider) output.textContent = slider.value;
            });
            updateVisibility();
        });
    }

    render();
    return { restart: () => container.querySelector('[data-action=restart]')?.click() };
}
