/* ==========================================================================
   Niwa Lead Form — Chat Mode Adapter
   ---------------------------------------------------------------------------
   Renders the lead form flow as regular bot bubbles using MessageRenderer.
   Hooks into chat input (by wrapping ChatClient.sendMessage) so the next
   user messages are intercepted as answers instead of being forwarded to
   the AI backend. Releases the hook when the flow completes.

   NAMESPACE: window.Niwa.LeadFormChatMode
   ========================================================================== */

window.Niwa = window.Niwa || {};

window.Niwa.LeadFormChatMode = (function () {
  'use strict';

  var SOURCE_TAG = 'lf';
  var _active = false;
  var _hooked = false;
  var _originalSendMessage = null;
  var _triggerArmed = false;
  var _triggerFired = false;
  var _consumeFirstMessageOnSend = false;

  /* -- Helpers ------------------------------------------------------------ */

  function _engine() { return window.Niwa && window.Niwa.LeadFormEngine; }
  function _renderer() { return window.Niwa && window.Niwa.MessageRenderer; }
  function _morph() { return window.Niwa && window.Niwa.MorphController; }
  function _chat() { return window.Niwa && window.Niwa.ChatClient; }

  function _i18n(key, fallback) {
    var lf = window.NiwaLF;
    if (lf && lf.i18n && typeof lf.i18n[key] === 'string' && lf.i18n[key] !== '') {
      return lf.i18n[key];
    }
    return fallback || '';
  }

  function _clearComposer() {
    var mc = _morph();
    var ta = mc && mc.getTextarea ? mc.getTextarea() : null;
    if (ta) {
      ta.value = '';
      if (mc.syncTextareaLayout) mc.syncTextareaLayout();
      else ta.style.height = 'auto';
    }

    var sendBtn = mc && mc.getSendBtn ? mc.getSendBtn() : null;
    if (sendBtn) {
      var disabledCls = sendBtn.classList.contains('niwa-center__send')
        ? 'niwa-center__send--disabled'
        : 'niwa-morph__send--disabled';
      sendBtn.classList.add(disabledCls);
    }
  }

  function _escapeHtml(str) {
    return String(str == null ? '' : str)
      .replace(/&/g, '&amp;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;')
      .replace(/"/g, '&quot;')
      .replace(/'/g, '&#39;');
  }

  /* -- Question rendering ------------------------------------------------- */

  function _renderQuestion(question) {
    var r = _renderer();
    if (!r || !question) return;

    if (window.Niwa && window.Niwa.Scheduler && typeof window.Niwa.Scheduler.destroy === 'function') {
      window.Niwa.Scheduler.destroy();
    }

    // Compose a "3. Label" prefix — question number only, no "of total"
    // fraction (keeps bot bubbles reading as natural numbered questions).
    var eng = _engine();
    var prog = eng ? eng.getProgress() : { current: 0, total: 0 };
    var step = (prog.current || 0) + 1;
    var label = String(question.label || '');
    var text = (prog.total > 0 ? (step + '. ') : '') + label;

    r.addMessage(text, 'bot', { source: SOURCE_TAG });

    if (question.type === 'appointment') {
      _renderAppointment(question);
      return;
    }

    // Render choice options as suggestion buttons for single-choice.
    // Multi-choice cannot be represented cleanly via suggestions (tap-to-send
    // semantics), so the visitor types comma-separated values or we fall back
    // to card mode. For choice_single, we render suggestions so tapping sends.
    if (question.type === 'choice_single' && Array.isArray(question.options) && question.options.length) {
      var items = question.options.map(function (opt) { return { label: opt.label || opt.value }; });
      var codes = question.options.map(function (opt) { return 'lf_opt:' + String(opt.value); });
      r.renderSuggestions(items, codes, true, { source: SOURCE_TAG });
    } else if (question.type === 'choice_multi' && Array.isArray(question.options) && question.options.length) {
      // Show list of options as a hint; user types comma-separated labels.
      var hint = question.options.map(function (opt) { return '• ' + (opt.label || opt.value); }).join('\n');
      if (hint) r.addMessage(hint, 'bot', { source: SOURCE_TAG });
    }
  }

  function _renderCompletion() {
    var r = _renderer();
    var eng = _engine();
    if (!r || !eng) return;

    var completion = eng.getCompletionConfig();
    var text = completion.text || '';
    var mediaUrl = completion.mediaUrl || '';
    var mediaType = completion.mediaType || '';

    if (!mediaUrl) {
      if (text) r.addMessage(text, 'bot', { source: SOURCE_TAG });
      return;
    }

    // Build a bubble with media + text (mimics leave-stoper pattern).
    var bodyEl = document.querySelector('.niwa-morph__body') || document.querySelector('.niwa-center__body');
    if (!bodyEl) {
      if (text) r.addMessage(text, 'bot', { source: SOURCE_TAG });
      return;
    }

    var msgDiv = document.createElement('div');
    msgDiv.className = 'niwa-message niwa-message--bot';
    msgDiv.setAttribute('data-source', SOURCE_TAG);

    var avatarDiv = document.createElement('div');
    avatarDiv.className = 'niwa-message__avatar';
    var avatarUrl = (window.NiwaAI && window.NiwaAI.avatar_url) ? window.NiwaAI.avatar_url : '';
    if (avatarUrl) {
      var avatarImg = document.createElement('img');
      avatarImg.src = avatarUrl;
      avatarImg.alt = 'Niwa';
      avatarImg.width = 28;
      avatarImg.height = 28;
      avatarDiv.appendChild(avatarImg);
    }
    msgDiv.appendChild(avatarDiv);

    var contentWrapper = document.createElement('div');
    contentWrapper.className = 'niwa-message__content';

    var bubbleDiv = document.createElement('div');
    bubbleDiv.className = 'niwa-message__bubble niwa-lf-completion-bubble';

    if (mediaType === 'video') {
      var video = document.createElement('video');
      video.autoplay = true;
      video.muted = true;
      video.loop = true;
      video.setAttribute('playsinline', '');
      video.className = 'niwa-lf-completion-media';
      var source = document.createElement('source');
      source.src = mediaUrl;
      source.type = 'video/mp4';
      video.appendChild(source);
      bubbleDiv.appendChild(video);
    } else {
      var img = document.createElement('img');
      img.src = mediaUrl;
      img.alt = '';
      img.className = 'niwa-lf-completion-media';
      bubbleDiv.appendChild(img);
    }

    if (text) {
      var p = document.createElement('p');
      p.className = 'niwa-message__text';
      p.textContent = text;
      bubbleDiv.appendChild(p);
    }

    contentWrapper.appendChild(bubbleDiv);
    msgDiv.appendChild(contentWrapper);
    bodyEl.appendChild(msgDiv);

    // Best-effort scroll.
    if (window.Niwa && window.Niwa.ScrollBehavior && window.Niwa.ScrollBehavior.scrollToBottom) {
      window.Niwa.ScrollBehavior.scrollToBottom(bodyEl);
    } else {
      bodyEl.scrollTop = bodyEl.scrollHeight;
    }
  }

  /* -- Hook ChatClient.sendMessage --------------------------------------- */

  function _handleFirstUserMessage(rawText) {
    var text = String(rawText == null ? '' : rawText).trim();
    if (!text) return false;

    var r = _renderer();
    if (!r || typeof r.addMessage !== 'function') return false;

    // This message triggers the form itself. Render and persist it locally,
    // but do not forward it to the LLM as a parallel chat turn.
    _consumeFirstMessageOnSend = false;
    r.addMessage(text, 'user');
    if (typeof r.saveHistory === 'function') r.saveHistory();
    if (typeof r.clearSuggestions === 'function') r.clearSuggestions();
    _clearComposer();
    _startFlow();
    return true;
  }

  function _renderAppointment(question) {
    var scheduler = window.Niwa && window.Niwa.Scheduler;
    var r = _renderer();
    var eng = _engine();
    if (!scheduler || typeof scheduler.activate !== 'function' || !r || !eng) {
      if (r) r.addMessage(_i18n('schedulerUnavailable', 'Online booking is not available right now.'), 'bot', { source: SOURCE_TAG });
      return;
    }

    // No host — the scheduler docks itself into the shell above the composer
    // pill (replacing the input), exactly like the product focus surface.
    scheduler.activate({
      dismissible: false,
      question: question,
      onBooked: function (result) {
        var visible = result && result.service && result.slot
          ? String(result.service.name || '') + ' - ' + String(result.slot.date || '') + ' ' + String(result.slot.time || result.slot.label || '')
          : _i18n('bookingConfirmed', 'Appointment confirmed');
        r.addMessage(visible, 'user');
        r.showTyping();
        eng.submitAnswer(question.id, result.answer).then(function () { r.hideTyping(); }).catch(function () { r.hideTyping(); });
      }
    }).catch(function (error) {
      var cancelled = error && error.message === 'scheduler_cancelled';
      if (!cancelled) {
        r.addMessage(error && error.message ? error.message : _i18n('schedulerUnavailable', 'Online booking is not available right now.'), 'bot', { source: SOURCE_TAG });
      }
    });
  }

  function _installSendHook() {
    if (_hooked) return;
    var chat = _chat();
    if (!chat || typeof chat.sendMessage !== 'function') return;

    _originalSendMessage = chat.sendMessage;
    chat.sendMessage = function (text) {
      if (_consumeFirstMessageOnSend && !_triggerFired && _handleFirstUserMessage(text)) {
        return true;
      }
      if (_active && _engine() && !_engine().isCompleted()) {
        return _handleUserAnswer(text);
      }
      return _originalSendMessage.call(chat, text);
    };
    _hooked = true;
  }

  function _releaseSendHook() {
    if (!_hooked) return;
    var chat = _chat();
    if (chat && _originalSendMessage) {
      chat.sendMessage = _originalSendMessage;
    }
    _originalSendMessage = null;
    _hooked = false;
    _consumeFirstMessageOnSend = false;
  }

  /* -- Handle a captured user message as the current-question answer ----- */

  function _handleUserAnswer(rawText) {
    var text = String(rawText == null ? '' : rawText).trim();
    if (!text) return;

    var r = _renderer();
    var eng = _engine();
    if (!r || !eng) return;

    var question = eng.getCurrentQuestion();
    if (!question) return;

    if (question.type === 'appointment') {
      r.addMessage(_i18n('chooseAppointment', 'Choose an available date and time in the scheduler.'), 'bot', { source: SOURCE_TAG });
      return;
    }

    // Display user's typed message as a regular user bubble so the
    // conversation reads naturally.
    r.addMessage(text, 'user');

    // Clear the textarea like the real send flow would.
    _clearComposer();

    // Parse answer shape based on question type.
    var answer = text;
    if (question.type === 'choice_multi') {
      // Accept comma separated values or labels.
      answer = text.split(',').map(function (s) { return s.trim(); }).filter(function (s) { return s !== ''; });
      // Map labels → values if possible.
      if (Array.isArray(question.options)) {
        answer = answer.map(function (entry) {
          for (var i = 0; i < question.options.length; i++) {
            var opt = question.options[i];
            if (String(opt.label).toLowerCase() === entry.toLowerCase()) return String(opt.value);
          }
          return entry;
        });
      }
    }

    // Client-side pre-validation (UX accelerator).
    var local = eng.validateAnswer(question, answer);
    if (!local.valid) {
      r.addMessage(local.error || _i18n('required', 'This field is required.'), 'bot', { source: SOURCE_TAG });
      return;
    }

    r.showTyping();
    eng.submitAnswer(question.id, answer).then(function () {
      r.hideTyping();
    }).catch(function () {
      r.hideTyping();
    });
  }

  /* -- Engine event wiring ----------------------------------------------- */

  function _onStarted(e) {
    if (!_active) return;
    var detail = (e && e.detail) || {};
    var r = _renderer();
    if (r) r.hideTyping();
    if (detail.completed) {
      if (window.Niwa && window.Niwa.Scheduler) window.Niwa.Scheduler.destroy();
      _renderCompletion();
      _releaseSendHook();
      _active = false;
      return;
    }
    if (detail.question) {
      _renderQuestion(detail.question);
    }
  }

  function _onAdvanced(e) {
    if (!_active) return;
    var detail = (e && e.detail) || {};
    if (detail.completed) {
      if (window.Niwa && window.Niwa.Scheduler) window.Niwa.Scheduler.destroy();
      _renderCompletion();
      _releaseSendHook();
      _active = false;
      return;
    }
    if (detail.question) {
      _renderQuestion(detail.question);
    }
  }

  function _onValidationError(e) {
    if (!_active) return;
    var r = _renderer();
    if (!r) return;
    var detail = (e && e.detail) || {};
    r.addMessage(detail.error || _i18n('required', 'This field is required.'), 'bot', { source: SOURCE_TAG });
  }

  function _onSuggestionClickInterceptor(ev) {
    if (!_active) return;
    var btn = ev.target.closest ? ev.target.closest('button.niwa-action[data-code^="lf_opt:"]') : null;
    if (!btn) return;
    ev.preventDefault();
    ev.stopPropagation();
    var code = btn.getAttribute('data-code') || '';
    var value = code.substring('lf_opt:'.length);
    var label = btn.textContent ? btn.textContent.trim() : value;

    var r = _renderer();
    var eng = _engine();
    if (!r || !eng) return;
    var question = eng.getCurrentQuestion();
    if (!question) return;

    r.clearSuggestions();
    r.addMessage(label, 'user');
    r.showTyping();
    eng.submitAnswer(question.id, value).then(function () {
      r.hideTyping();
    }).catch(function () {
      r.hideTyping();
    });
  }

  /* -- Trigger dispatcher ------------------------------------------------- */

  function _startFlow() {
    if (_triggerFired) return;
    _triggerFired = true;
    _active = true;
    var r = _renderer();
    if (r) r.showTyping();
    _installSendHook();
    _engine().start().then(function () {
      // _onStarted handles the rest via event.
    }).catch(function () {
      if (r) r.hideTyping();
      _active = false;
      _releaseSendHook();
    });
  }

  function _armImmediate() {
    if (_triggerArmed) return;
    _triggerArmed = true;
    var mc = _morph();
    if (mc && mc.isOpen && mc.isOpen()) {
      _startFlow();
      return;
    }
    // Poll for open class — MorphController exposes no event hook.
    var morphEl = document.getElementById('niwaMorph');
    if (!morphEl) {
      _startFlow(); // fallback
      return;
    }
    var observer = new MutationObserver(function () {
      if (morphEl.classList.contains('niwa-morph--open')) {
        observer.disconnect();
        _startFlow();
      }
    });
    observer.observe(morphEl, { attributes: true, attributeFilter: ['class'] });
  }

  function _armAfterDelay(delay) {
    if (_triggerArmed) return;
    _triggerArmed = true;
    setTimeout(function () {
      var mc = _morph();
      if (mc && mc.isOpen && mc.isOpen()) {
        _startFlow();
      } else {
        // Wait for morph open after the delay hit.
        var morphEl = document.getElementById('niwaMorph');
        if (!morphEl) { _startFlow(); return; }
        var observer = new MutationObserver(function () {
          if (morphEl.classList.contains('niwa-morph--open')) {
            observer.disconnect();
            _startFlow();
          }
        });
        observer.observe(morphEl, { attributes: true, attributeFilter: ['class'] });
      }
    }, Math.max(0, parseInt(delay || 0, 10)));
  }

  function _armAfterFirstUserMsg() {
    if (_triggerArmed) return;
    _triggerArmed = true;
    _consumeFirstMessageOnSend = true;
    _installSendHook();
  }

  /* -- init --------------------------------------------------------------- */

  function init() {
    var lf = window.NiwaLF;
    if (!lf || !lf.config || !lf.config.enabled) return;
    if (lf.config.mode !== 'chat') return;
    // Visitor already finished (or partial-submitted) the form on an earlier
    // page load — backend ships `completed:true` via NiwaLF.config. Bail
    // before arming any trigger so the completion screen doesn't pop up on
    // every subsequent page render.
    if (lf.config.completed === true) return;

    // Delegated click interceptor for lf_opt suggestion buttons.
    document.addEventListener('click', _onSuggestionClickInterceptor, true);

    // Engine events.
    document.addEventListener('niwa:lf:started', _onStarted);
    document.addEventListener('niwa:lf:advanced', _onAdvanced);
    document.addEventListener('niwa:lf:validation-error', _onValidationError);

    // Trigger.
    var trigger = lf.config.trigger || 'after_first_user_msg';
    if (trigger === 'ai_only') {
      // No JS auto-arm — wait for frontend agent to call activate().
      return;
    }
    if (trigger === 'immediate') {
      _armImmediate();
    } else if (trigger === 'after_delay') {
      _armAfterDelay(lf.config.trigger_delay_ms);
    } else {
      _armAfterFirstUserMsg();
    }
  }

  /**
   * Externally triggered activation (AI agent tool, programmatic start).
   * Idempotent — a re-entry while a flow is running is a no-op.
   */
  function activate() {
    if (_triggerFired) return;
    var lf = window.NiwaLF;
    if (!lf || !lf.config || !lf.config.enabled) return;
    if (lf.config.mode !== 'chat') return;
    // Defense-in-depth: even though Niwa.LeadForm.activate() already checks
    // engine.isCompleted(), guard here too so a stray direct call (e.g. from
    // a future tool path) cannot revive a finished flow.
    if (lf.config.completed === true) return;

    // Install the ai_only safety listeners if init() bailed early.
    document.removeEventListener('click', _onSuggestionClickInterceptor, true);
    document.addEventListener('click', _onSuggestionClickInterceptor, true);
    document.removeEventListener('niwa:lf:started', _onStarted);
    document.removeEventListener('niwa:lf:advanced', _onAdvanced);
    document.removeEventListener('niwa:lf:validation-error', _onValidationError);
    document.addEventListener('niwa:lf:started', _onStarted);
    document.addEventListener('niwa:lf:advanced', _onAdvanced);
    document.addEventListener('niwa:lf:validation-error', _onValidationError);

    _triggerArmed = true;
    _startFlow();
  }

  /* -- Public API --------------------------------------------------------- */

  return {
    init: init,
    activate: activate
  };
})();

/* -- Auto-init on DOMContentLoaded ---------------------------------------- */
(function () {
  'use strict';

  function boot() {
    if (!window.NiwaLF || !window.NiwaLF.config || !window.NiwaLF.config.enabled) return;
    if (window.NiwaLF.config.mode !== 'chat') return;
    // Dependency order guarantees the engine and ChatClient are ready here.
    // Arm now so the first send cannot race the trigger hook.
    window.Niwa.LeadFormChatMode.init();
  }

  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', boot, { once: true });
  } else {
    boot();
  }
})();
