/* ==========================================================================
   Niwa Lead Form — Engine
   ---------------------------------------------------------------------------
   Shared client engine for the Lead Form Generator feature. Holds the active
   session state, issues AJAX calls to the 3 lead-form endpoints
   (niwa_ai_lf_start | niwa_ai_lf_answer | niwa_ai_lf_status) and fires
   DOM CustomEvents that mode adapters listen for.

   NAMESPACE: window.Niwa.LeadFormEngine
   Events (dispatched on document):
     niwa:lf:started          → { question, progress, completed }
     niwa:lf:advanced         → { question, progress, completed, partial_submitted }
     niwa:lf:validation-error → { error, question }
     niwa:lf:completed        → { }
   ========================================================================== */

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

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

  /* -- State -------------------------------------------------------------- */

  var _state = {
    sessionId: '',
    sessionRowId: 0,
    currentQuestion: null,
    progress: { current: 0, total: 0 },
    nonce: '',
    mode: 'chat',
    completed: false,
    started: false,
    inFlight: false
  };

  var _config = null;
  var _emailPhonePartial = false;

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

  function _lf() {
    return window.NiwaLF || null;
  }

  function _getChatSessionId() {
    if (window.Niwa && window.Niwa.ChatClient && typeof window.Niwa.ChatClient.getSessionId === 'function') {
      try { return String(window.Niwa.ChatClient.getSessionId() || ''); } catch (e) { /* ignore */ }
    }
    if (window.Niwa && window.Niwa.Utils && typeof window.Niwa.Utils.getSession === 'function') {
      try { return String(window.Niwa.Utils.getSession() || ''); } catch (e) { /* ignore */ }
    }
    if (window.NiwaAI && window.NiwaAI.session_id) {
      return String(window.NiwaAI.session_id);
    }
    try {
      var existing = localStorage.getItem('niwa_ai_session_id');
      if (existing) return existing;
      var fresh = 'niwa_' + Date.now().toString(36) + '_' + Math.random().toString(36).slice(2, 10);
      localStorage.setItem('niwa_ai_session_id', fresh);
      return fresh;
    } catch (e) {
      return 'niwa_ephemeral_' + Date.now().toString(36);
    }
  }

  function _dispatch(name, detail) {
    try {
      var ev = new CustomEvent(name, { detail: detail || {} });
      document.dispatchEvent(ev);
    } catch (e) {
      // IE fallback (not expected on modern WP)
      var legacy = document.createEvent('CustomEvent');
      legacy.initCustomEvent(name, false, false, detail || {});
      document.dispatchEvent(legacy);
    }
  }

  function _ajaxUrl() {
    var lf = _lf();
    if (lf && lf.ajaxUrl) return lf.ajaxUrl;
    if (window.NiwaAI && window.NiwaAI.ajax) return window.NiwaAI.ajax;
    return '/wp-admin/admin-ajax.php';
  }

  function _buildBody(action, extra) {
    var params = new URLSearchParams();
    params.set('action', action);
    params.set('nonce', _state.nonce || '');
    params.set('session_id', _state.sessionId);
    if (extra && typeof extra === 'object') {
      for (var k in extra) {
        if (!Object.prototype.hasOwnProperty.call(extra, k)) continue;
        var v = extra[k];
        if (Array.isArray(v)) {
          for (var i = 0; i < v.length; i++) {
            params.append(k + '[]', String(v[i]));
          }
        } else if (v !== undefined && v !== null) {
          params.set(k, String(v));
        }
      }
    }
    return params;
  }

  function _post(action, extra) {
    if (_state.inFlight) {
      // Allow overlap for status checks; queue would complicate things.
    }
    _state.inFlight = true;

    return fetch(_ajaxUrl(), {
      method: 'POST',
      credentials: 'same-origin',
      headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
      body: _buildBody(action, extra).toString()
    })
      .then(function (resp) { return resp.json().catch(function () { return null; }); })
      .then(function (json) {
        _state.inFlight = false;
        return json;
      })
      .catch(function (err) {
        _state.inFlight = false;
        throw err;
      });
  }

  function _applyPayload(payload) {
    if (!payload || typeof payload !== 'object') return;
    if (typeof payload.nonce === 'string' && payload.nonce) {
      _state.nonce = payload.nonce;
    }
    if (Object.prototype.hasOwnProperty.call(payload, 'session_row_id')) {
      _state.sessionRowId = parseInt(payload.session_row_id || 0, 10) || 0;
    }
    if (payload.mode === 'chat' || payload.mode === 'card') {
      _state.mode = payload.mode;
    }
    if (Object.prototype.hasOwnProperty.call(payload, 'question')) {
      _state.currentQuestion = payload.question || null;
    }
    if (payload.progress && typeof payload.progress === 'object') {
      _state.progress = {
        current: parseInt(payload.progress.current || 0, 10) || 0,
        total: parseInt(payload.progress.total || 0, 10) || 0
      };
    }
    if (Object.prototype.hasOwnProperty.call(payload, 'completed')) {
      _state.completed = !!payload.completed;
    }
  }

  /* -- Public: init ------------------------------------------------------- */

  function init(config) {
    _config = config || {};
    _state.sessionId = _getChatSessionId();
    _state.nonce = String(_config.nonce || '');
    var cfg = _config.config || {};
    if (cfg.mode === 'chat' || cfg.mode === 'card') {
      _state.mode = cfg.mode;
    }
    // Hydrate completion state from the localized backend payload. The PHP
    // asset loader resolves the visitor's session cookie against the
    // lead-form session store and ships `completed:true` (and `session_status`)
    // when the form was already finished or partial-submitted on a previous
    // page load. Without this, _state.completed stays false on every fresh
    // page render and Niwa.LeadForm.activate() can't no-op the agent's
    // re-trigger — the user sees the completion screen pop up again.
    if (cfg.completed === true) {
      _state.completed = true;
      _state.started = true;
    }
  }

  /* -- Public: start ------------------------------------------------------ */

  function start(modeOverride) {
    if (!_state.sessionId) {
      _state.sessionId = _getChatSessionId();
    }
    var extra = {};
    if (modeOverride === 'chat' || modeOverride === 'card') {
      extra.mode = modeOverride;
    }

    return _post('niwa_ai_lf_start', extra).then(function (json) {
      if (!json || json.success !== true || !json.data) {
        return { ok: false, error: 'start_failed' };
      }
      _applyPayload(json.data);
      _state.started = true;
      _dispatch('niwa:lf:started', {
        question: _state.currentQuestion,
        progress: _state.progress,
        completed: _state.completed,
        mode: _state.mode
      });
      if (_state.completed) {
        _dispatch('niwa:lf:completed', {});
      }
      return { ok: true, data: json.data };
    });
  }

  /* -- Public: submitAnswer ---------------------------------------------- */

  function submitAnswer(questionId, answer) {
    var extra = { question_id: String(questionId || '') };
    if (Array.isArray(answer)) {
      extra.answer = answer;
    } else {
      extra.answer = String(answer == null ? '' : answer);
    }

    return _post('niwa_ai_lf_answer', extra).then(function (json) {
      if (!json || typeof json !== 'object') {
        return { ok: false, error: 'network' };
      }
      var data = json.data || {};

      // Refresh nonce whenever server sent one.
      if (data && typeof data.nonce === 'string' && data.nonce) {
        _state.nonce = data.nonce;
      }

      // Validation error path: server may send success:false OR success:true w/ error.
      var hasError = (data && typeof data.error === 'string' && data.error !== '')
        || json.success === false;

      if (hasError) {
        var question = data.question || _state.currentQuestion;
        if (question) _state.currentQuestion = question;
        _dispatch('niwa:lf:validation-error', {
          error: (data && data.error) ? String(data.error) : 'validation',
          question: question,
          progress: _state.progress
        });
        return { ok: false, error: data.error || 'validation', question: question };
      }

      _applyPayload(data);
      _dispatch('niwa:lf:advanced', {
        question: _state.currentQuestion,
        progress: _state.progress,
        completed: _state.completed,
        partial_submitted: !!data.partial_submitted
      });
      if (data.partial_submitted) _emailPhonePartial = true;
      if (_state.completed) {
        _dispatch('niwa:lf:completed', {});
      }
      return { ok: true, data: data };
    });
  }

  /* -- Public: resume ----------------------------------------------------- */

  function resume() {
    return _post('niwa_ai_lf_status', {}).then(function (json) {
      if (!json || json.success !== true || !json.data) {
        return { ok: false };
      }
      _applyPayload(json.data);
      _state.started = true;
      _dispatch('niwa:lf:started', {
        question: _state.currentQuestion,
        progress: _state.progress,
        completed: _state.completed,
        mode: _state.mode,
        resumed: true
      });
      return { ok: true, data: json.data };
    });
  }

  /* -- Public: validation helper (client-side UX) ------------------------ */

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

  var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/;
  var PHONE_RE = /^[+]?[\d\s()\-./]{6,}$/;

  function validateAnswer(question, answer) {
    if (!question || typeof question !== 'object') {
      return { valid: true, error: null };
    }
    var isEmpty = (answer === null || answer === undefined)
      || (typeof answer === 'string' && answer.trim() === '')
      || (Array.isArray(answer) && answer.length === 0);

    if (isEmpty) {
      if (question.required) {
        return { valid: false, error: _i18n('required', 'This field is required.') };
      }
      return { valid: true, error: null };
    }

    var type = String(question.type || 'text');
    var validation = question.validation || {};

    if (type === 'email' && typeof answer === 'string' && !EMAIL_RE.test(answer.trim())) {
      return { valid: false, error: _i18n('invalidEmail', 'Please enter a valid email.') };
    }
    if (type === 'phone' && typeof answer === 'string' && !PHONE_RE.test(answer.trim())) {
      return { valid: false, error: _i18n('invalidPhone', 'Please enter a valid phone number.') };
    }
    if (type === 'number' && typeof answer === 'string') {
      var numStr = answer.trim().replace(',', '.');
      if (numStr === '' || isNaN(Number(numStr))) {
        return { valid: false, error: _i18n('invalidNumber', 'Please enter a valid number.') };
      }
    }
    if (type === 'choice_single' || type === 'choice_multi') {
      var options = Array.isArray(question.options) ? question.options : [];
      var values = {};
      for (var i = 0; i < options.length; i++) {
        if (options[i] && options[i].value !== undefined) {
          values[String(options[i].value)] = true;
        }
      }
      var selections = Array.isArray(answer) ? answer : [answer];
      if (type === 'choice_single' && selections.length === 0) {
        return { valid: false, error: _i18n('chooseOption', 'Please choose an option.') };
      }
      for (var j = 0; j < selections.length; j++) {
        if (!values[String(selections[j])]) {
          return { valid: false, error: _i18n('chooseOption', 'Please choose an option.') };
        }
      }
    }

    // Length bounds (text/textarea/email/phone)
    if (typeof answer === 'string' && (type === 'text' || type === 'textarea' || type === 'email' || type === 'phone' || type === 'number')) {
      var len = answer.length;
      if (validation.min !== null && validation.min !== undefined && validation.min !== '' && len < parseInt(validation.min, 10)) {
        return { valid: false, error: _i18n('tooShort', 'Answer is too short.') };
      }
      if (validation.max !== null && validation.max !== undefined && validation.max !== '' && len > parseInt(validation.max, 10)) {
        return { valid: false, error: _i18n('tooLong', 'Answer is too long.') };
      }
    }

    // Regex (only text-like)
    if (validation.regex && typeof answer === 'string' && (type === 'text' || type === 'textarea' || type === 'email' || type === 'phone')) {
      try {
        var re = new RegExp(validation.regex);
        if (!re.test(answer)) {
          return { valid: false, error: _i18n('required', 'This field is required.') };
        }
      } catch (e) { /* ignore invalid regex server-side */ }
    }

    return { valid: true, error: null };
  }

  /* -- Public: accessors -------------------------------------------------- */

  function getSessionId() { return _state.sessionId; }
  function getCurrentQuestion() { return _state.currentQuestion; }
  function getProgress() { return { current: _state.progress.current, total: _state.progress.total }; }
  function isCompleted() { return _state.completed; }
  function isStarted() { return _state.started; }
  function getMode() { return _state.mode; }
  function getConfig() { return _config || {}; }

  function getCompletionConfig() {
    var cfg = (_config && _config.config) || {};
    return {
      text: cfg.completion_text || '',
      mediaUrl: cfg.completion_media_url || '',
      mediaType: cfg.completion_media_type || ''
    };
  }

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

  return {
    init: init,
    start: start,
    submitAnswer: submitAnswer,
    resume: resume,
    validateAnswer: validateAnswer,
    getSessionId: getSessionId,
    getCurrentQuestion: getCurrentQuestion,
    getProgress: getProgress,
    isCompleted: isCompleted,
    isStarted: isStarted,
    getMode: getMode,
    getConfig: getConfig,
    getCompletionConfig: getCompletionConfig
  };
})();

/* ==========================================================================
   Niwa Lead Form — Public activation facade
   ---------------------------------------------------------------------------
   `window.Niwa.LeadForm.activate(opts)` — single entry point the frontend
   agent tool handler calls after the backend returns `lead_form_trigger`.
   Routes to the correct mode adapter (chat or card) based on
   `NiwaLF.config.mode`, and is a no-op if the form is already running,
   completed, or disabled.
   ========================================================================== */
window.Niwa = window.Niwa || {};
window.Niwa.LeadForm = (function () {
  'use strict';

  function _cfg() {
    return (window.NiwaLF && window.NiwaLF.config) ? window.NiwaLF.config : null;
  }

  function activate(opts) {
    var cfg = _cfg();
    if (!cfg || !cfg.enabled) return false;

    var eng = window.Niwa && window.Niwa.LeadFormEngine;
    if (eng && typeof eng.isStarted === 'function' && eng.isStarted()) return false;
    if (eng && typeof eng.isCompleted === 'function' && eng.isCompleted()) return false;

    var mode = cfg.mode === 'card' ? 'card' : 'chat';
    var adapter = mode === 'card'
      ? (window.Niwa && window.Niwa.LeadFormCardMode)
      : (window.Niwa && window.Niwa.LeadFormChatMode);

    if (!adapter || typeof adapter.activate !== 'function') return false;
    try { adapter.activate(opts || {}); return true; }
    catch (e) { return false; }
  }

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

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

  function boot() {
    var cfg = window.NiwaLF;
    if (!cfg || !cfg.config || !cfg.config.enabled) return;
    window.Niwa.LeadFormEngine.init(cfg);
  }

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