/* ==========================================================================
   Fill — respondent-facing form renderer
   Three display modes, chosen per-form by the creator:
     - "one_at_a_time" : Typeform-style, one question fills the screen, progress bar
     - "single_page"   : Google Forms-style, everything on one scrollable page
     - "stack"         : Fill's own signature style — related questions grouped
                         into small flip-through cards (2-3 questions per card),
                         a distinct interaction model from either competitor
   Branching logic (skip/jump based on an answer) works identically across
   all three modes, computed by the same shared function.
   ========================================================================== */
(function (global) {
  function computeNextQuestionId(currentId, answers, questions) {
    const idx = questions.findIndex(q => q.id === currentId);
    const current = questions[idx];
    if (current && current.branch && current.branch.rules) {
      const val = answers[current.id];
      for (const rule of current.branch.rules) {
        if (matchesRule(val, rule)) return rule.goTo;
      }
    }
    return idx >= 0 && idx < questions.length - 1 ? questions[idx + 1].id : null;
  }

  function matchesRule(value, rule) {
    if (rule.equals !== undefined) {
      if (Array.isArray(value)) return value.includes(rule.equals);
      return value === rule.equals;
    }
    return false;
  }

  function isAnswered(question, value) {
    if (!question.required) return true;
    if (value === undefined || value === null) return false;
    if (Array.isArray(value)) return value.length > 0;
    return String(value).trim().length > 0;
  }

  function fieldInputHTML(q, currentValue) {
    const val = currentValue !== undefined ? currentValue : '';
    switch (q.type) {
      case 'long_text':
        return `<textarea class="fill-input" data-qid="${q.id}" rows="4">${escapeHtml(val)}</textarea>`;
      case 'email':
        return `<input class="fill-input" type="email" data-qid="${q.id}" value="${escapeHtml(val)}">`;
      case 'number':
        return `<input class="fill-input" type="number" data-qid="${q.id}" value="${escapeHtml(val)}">`;
      case 'multiple_choice':
        return (q.options || []).map((opt) => `
          <label class="fill-choice">
            <input type="radio" name="q-${q.id}" value="${escapeHtml(opt)}" data-qid="${q.id}" ${val === opt ? 'checked' : ''}>
            <span>${escapeHtml(opt)}</span>
          </label>`).join('');
      case 'checkboxes': {
        const arr = Array.isArray(val) ? val : [];
        return (q.options || []).map((opt) => `
          <label class="fill-choice">
            <input type="checkbox" name="q-${q.id}" value="${escapeHtml(opt)}" data-qid="${q.id}" ${arr.includes(opt) ? 'checked' : ''}>
            <span>${escapeHtml(opt)}</span>
          </label>`).join('');
      }
      case 'dropdown':
        return `<select class="fill-input" data-qid="${q.id}"><option value="">Choose…</option>${(q.options||[]).map(opt => `<option value="${escapeHtml(opt)}" ${val===opt?'selected':''}>${escapeHtml(opt)}</option>`).join('')}</select>`;
      case 'rating': {
        const max = q.max || 5;
        let out = '<div class="fill-rating" data-qid="' + q.id + '">';
        for (let i = 1; i <= max; i++) {
          out += `<span class="fill-star ${Number(val) >= i ? 'active' : ''}" data-rating-value="${i}">★</span>`;
        }
        return out + '</div>';
      }
      case 'short_text':
      default:
        return `<input class="fill-input" type="text" data-qid="${q.id}" value="${escapeHtml(val)}">`;
    }
  }

  function escapeHtml(s) {
    return String(s == null ? '' : s).replace(/[&<>"]/g, c => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' }[c]));
  }

  function readFieldValue(container, q) {
    if (q.type === 'checkboxes') {
      return Array.from(container.querySelectorAll(`input[data-qid="${q.id}"]:checked`)).map(el => el.value);
    }
    if (q.type === 'multiple_choice') {
      const checked = container.querySelector(`input[data-qid="${q.id}"]:checked`);
      return checked ? checked.value : '';
    }
    if (q.type === 'rating') {
      const wrap = container.querySelector(`.fill-rating[data-qid="${q.id}"]`);
      return wrap ? Number(wrap.dataset.value || 0) : 0;
    }
    const el = container.querySelector(`[data-qid="${q.id}"]`);
    return el ? el.value : '';
  }

  function wireRatingClicks(container, onChange) {
    container.querySelectorAll('.fill-rating').forEach(wrap => {
      wrap.querySelectorAll('.fill-star').forEach(star => {
        star.addEventListener('click', () => {
          const v = Number(star.dataset.ratingValue);
          wrap.dataset.value = v;
          wrap.querySelectorAll('.fill-star').forEach(s => {
            s.classList.toggle('active', Number(s.dataset.ratingValue) <= v);
          });
          if (onChange) onChange();
        });
      });
    });
  }

  function renderOneAtATime(form, container, opts) {
    const answers = {};
    let currentId = form.questions[0] ? form.questions[0].id : null;
    let history = [];

    function paint() {
      if (currentId === null) { paintComplete(); return; }
      const q = form.questions.find(x => x.id === currentId);
      const answeredCount = history.length;
      const pct = Math.round((answeredCount / form.questions.length) * 100);
      container.innerHTML = `
        <div class="fill-oaat">
          <div class="fill-progress"><div class="fill-progress-bar" style="width:${pct}%"></div></div>
          <div class="fill-oaat-card">
            <div class="fill-qlabel">${escapeHtml(q.label)}${q.required ? ' <span class="fill-required">*</span>' : ''}</div>
            <div class="fill-qinput">${fieldInputHTML(q, answers[q.id])}</div>
            <div class="fill-error" id="fillErr" style="display:none;">This question needs an answer.</div>
            <div class="fill-oaat-actions">
              ${history.length > 0 ? '<button type="button" class="fill-btn-secondary" id="fillBack">Back</button>' : ''}
              <button type="button" class="fill-btn-primary" id="fillNext">${isLastQuestion(q) ? 'Submit' : 'OK'}</button>
            </div>
          </div>
        </div>`;
      wireRatingClicks(container);
      const nextBtn = container.querySelector('#fillNext');
      nextBtn.addEventListener('click', () => {
        const val = readFieldValue(container, q);
        if (!isAnswered(q, val)) {
          container.querySelector('#fillErr').style.display = 'block';
          return;
        }
        answers[q.id] = val;
        history.push(q.id);
        currentId = computeNextQuestionId(q.id, answers, form.questions);
        paint();
      });
      const backBtn = container.querySelector('#fillBack');
      if (backBtn) backBtn.addEventListener('click', () => { currentId = history.pop(); paint(); });
    }
    function isLastQuestion(q) { return computeNextQuestionId(q.id, answers, form.questions) === null; }
    function paintComplete() {
      container.innerHTML = `<div class="fill-complete">${form.thankYouMessage ? escapeHtml(form.thankYouMessage) : 'Thanks — your response has been recorded.'}</div>`;
      if (opts.onSubmit) opts.onSubmit(answers);
    }
    paint();
  }

  function renderSinglePage(form, container, opts) {
    container.innerHTML = `
      <div class="fill-singlepage">
        <h2 class="fill-form-title">${escapeHtml(form.title || '')}</h2>
        ${form.questions.map(q => `
          <div class="fill-sp-block" data-block-for="${q.id}">
            <div class="fill-qlabel">${escapeHtml(q.label)}${q.required ? ' <span class="fill-required">*</span>' : ''}</div>
            <div class="fill-qinput">${fieldInputHTML(q, undefined)}</div>
            <div class="fill-error" style="display:none;">This question needs an answer.</div>
          </div>
        `).join('')}
        <button type="button" class="fill-btn-primary" id="fillSubmitAll">Submit</button>
      </div>`;
    wireRatingClicks(container);
    updateVisibility();
    container.addEventListener('change', updateVisibility);
    container.addEventListener('click', (e) => { if (e.target.closest('.fill-choice, .fill-star')) updateVisibility(); });

    function currentAnswers() {
      const answers = {};
      form.questions.forEach(q => { answers[q.id] = readFieldValue(container, q); });
      return answers;
    }
    function updateVisibility() {
      const answers = currentAnswers();
      const visible = new Set();
      let cursor = form.questions[0] ? form.questions[0].id : null;
      const seen = new Set();
      while (cursor && !seen.has(cursor)) {
        seen.add(cursor);
        visible.add(cursor);
        cursor = computeNextQuestionId(cursor, answers, form.questions);
      }
      form.questions.forEach(q => {
        const block = container.querySelector(`[data-block-for="${q.id}"]`);
        if (block) block.style.display = visible.has(q.id) ? '' : 'none';
      });
    }

    container.querySelector('#fillSubmitAll').addEventListener('click', () => {
      const answers = currentAnswers();
      let firstInvalid = null;
      form.questions.forEach(q => {
        const block = container.querySelector(`[data-block-for="${q.id}"]`);
        const visible = block && block.style.display !== 'none';
        const err = block ? block.querySelector('.fill-error') : null;
        if (visible && !isAnswered(q, answers[q.id])) {
          if (err) err.style.display = 'block';
          if (!firstInvalid) firstInvalid = block;
        } else if (err) err.style.display = 'none';
      });
      if (firstInvalid) { firstInvalid.scrollIntoView({ behavior: 'smooth', block: 'center' }); return; }
      container.innerHTML = `<div class="fill-complete">${form.thankYouMessage ? escapeHtml(form.thankYouMessage) : 'Thanks — your response has been recorded.'}</div>`;
      if (opts.onSubmit) opts.onSubmit(answers);
    });
  }

  function groupIntoCards(questions, groupSize) {
    const cards = [];
    let i = 0;
    while (i < questions.length) { cards.push(questions.slice(i, i + (groupSize || 2))); i += (groupSize || 2); }
    return cards;
  }
  function renderStack(form, container, opts) {
    const answers = {};
    const cards = groupIntoCards(form.questions, form.stackGroupSize || 2);
    let cardIdx = 0;

    function paint() {
      if (cardIdx >= cards.length) { paintComplete(); return; }
      const card = cards[cardIdx];
      container.innerHTML = `
        <div class="fill-stack">
          <div class="fill-stack-counter">Card ${cardIdx + 1} of ${cards.length}</div>
          <div class="fill-stack-card">
            ${card.map(q => `
              <div class="fill-stack-q" data-block-for="${q.id}">
                <div class="fill-qlabel">${escapeHtml(q.label)}${q.required ? ' <span class="fill-required">*</span>' : ''}</div>
                <div class="fill-qinput">${fieldInputHTML(q, answers[q.id])}</div>
                <div class="fill-error" style="display:none;">This question needs an answer.</div>
              </div>
            `).join('')}
          </div>
          <div class="fill-oaat-actions">
            ${cardIdx > 0 ? '<button type="button" class="fill-btn-secondary" id="fillStackBack">Back</button>' : ''}
            <button type="button" class="fill-btn-primary" id="fillStackNext">${cardIdx === cards.length - 1 ? 'Submit' : 'Next card'}</button>
          </div>
        </div>`;
      wireRatingClicks(container);
      container.querySelector('#fillStackNext').addEventListener('click', () => {
        let ok = true;
        card.forEach(q => {
          const val = readFieldValue(container, q);
          answers[q.id] = val;
          const block = container.querySelector(`[data-block-for="${q.id}"]`);
          const err = block.querySelector('.fill-error');
          if (!isAnswered(q, val)) { err.style.display = 'block'; ok = false; }
          else err.style.display = 'none';
        });
        if (!ok) return;
        cardIdx++;
        paint();
      });
      const backBtn = container.querySelector('#fillStackBack');
      if (backBtn) backBtn.addEventListener('click', () => { cardIdx--; paint(); });
    }
    function paintComplete() {
      container.innerHTML = `<div class="fill-complete">${form.thankYouMessage ? escapeHtml(form.thankYouMessage) : 'Thanks — your response has been recorded.'}</div>`;
      if (opts.onSubmit) opts.onSubmit(answers);
    }
    paint();
  }

  const FormRenderer = {
    mount(container, form, opts) {
      opts = opts || {};
      const mode = form.displayMode || 'one_at_a_time';
      if (mode === 'single_page') renderSinglePage(form, container, opts);
      else if (mode === 'stack') renderStack(form, container, opts);
      else renderOneAtATime(form, container, opts);
    },
    computeNextQuestionId,
    isAnswered,
  };

  if (typeof module !== 'undefined' && module.exports) module.exports = FormRenderer;
  global.FillFormRenderer = FormRenderer;
})(typeof window !== 'undefined' ? window : globalThis);
