/**
 * ===========================================================
 * WordAI Chat Widget — R8.7 (Final)
 * -----------------------------------------------------------
 * - Bubble is ALWAYS visible; acts as toggle on all devices
 * - Window opens above bubble with gap (no overlap)
 * - Mobile uses 90vw x 60vh (floating), not fullscreen
 * - Footer fully visible + input focused after open & reply
 * - Typing dots, footer fade, avatars, markdown, smooth scroll
 * - Send disabled while in-flight; arrow before label
 * ===========================================================
 */
(function ($) {
  "use strict";
  const cfg = window.scwordai_frontchat || {};
  const conversationKey = getConversationKey();
  const leadCfg = cfg.lead_capture || { mode: "off", fields: {} };
  let leadProfile = getStoredLeadProfile();
  let $bubble, $window, $messages, $input, $send, $close, inFlight = false, firstReplySeen = false;

  function initWordAIChatWidget() {
    if ($("#wordai-chat-bubble").data("wordai-chat-ready")) return;
    ensureDOM();
    cacheRefs();
    applyColors();
    applyChatFontSize();
    applyPosition();
    applyTheme();
    ensureAvatarWraps();
    renderAvatars();
    ensureSendIcon();
    addFooterShadow();
    wireUX();
    $("#wordai-chat-bubble").data("wordai-chat-ready", true);
    maybeShowBubble();
  }

  if (document.readyState === "loading") {
    $(initWordAIChatWidget);
  } else {
    initWordAIChatWidget();
  }


  /* ---------------- DOM & INIT ---------------- */
  function ensureDOM() {
    if (!$("#wordai-chat-bubble").length) {
      $("body").append(`
        <div id="wordai-chat-bubble" class="wordai-chat-bubble" role="button" tabindex="0" aria-expanded="false">
          <span class="wordai-bubble-label">${escapeHtml(cfg.bubble_label || "Chat")}</span>
        </div>`);
    } else if (!$("#wordai-chat-bubble .wordai-bubble-label").length) {
      $("#wordai-chat-bubble").append(`<span class="wordai-bubble-label">${escapeHtml(cfg.bubble_label || "Chat")}</span>`);
    }

    if (!$("#wordai-chat-window").length) {
      $("body").append(`
        <div id="wordai-chat-window" class="wordai-chat-window" style="display:none;opacity:0;transform:translateY(20px);">
          <div class="wordai-chat-header">
            <div class="assistant-name">
              <span class="title-text">${escapeHtml(cfg.assistant_name || "Assistant")}</span>
            </div>
            <button type="button" class="chat-close" aria-label="Close">×</button>
          </div>
          <div class="chat-messages"></div>
          <div class="wordai-chat-footer">
            <div class="footer-fade"></div>
            <textarea class="chat-input" placeholder="Type your question..."></textarea>
            <button class="chat-send"><span class="arrow">➤</span> Send</button>
            ${String(cfg.free_branding) === "1" ? `<div class="wordai-chat-branding">Powered by <a href="${escapeAttr(cfg.softcoy_url || "https://softcoy.com")}" target="_blank" rel="noopener noreferrer">SOFTCOY</a></div>` : ``}
          </div>
        </div>`);
    }
  }

  function cacheRefs() {
    $bubble = $("#wordai-chat-bubble");
    $window = $("#wordai-chat-window");
    $messages = $window.find(".chat-messages");
    $input = $window.find(".chat-input");
    $send = $window.find(".chat-send");
    $close = $window.find(".chat-close");
  }

  function ensureSendIcon() {
    // Self-heal: guarantee arrow span exists
    if ($send.length && !$send.find(".arrow").length) {
      $send.prepend('<span class="arrow">➤</span> ');
    }
  }

  function addFooterShadow() {
    if (!$window.find(".footer-fade").length) {
      $window.find(".wordai-chat-footer").prepend('<div class="footer-fade"></div>');
    }
    if (String(cfg.free_branding) === "1" && !$window.find(".wordai-chat-branding").length) {
      $window.find(".wordai-chat-footer").append(`<div class="wordai-chat-branding">Powered by <a href="${escapeAttr(cfg.softcoy_url || "https://softcoy.com")}" target="_blank" rel="noopener noreferrer">SOFTCOY</a></div>`);
    }
  }

  /* ---------------- Style & Theme ---------------- */
  function applyColors() {
    const r = document.documentElement;
    if (cfg.bubble_color) r.style.setProperty("--wordai-bubble-bg", cfg.bubble_color);
    if (cfg.avatar_color) r.style.setProperty("--wordai-avatar-fg", cfg.avatar_color);
  }
  function applyChatFontSize() {
    const r = document.documentElement;
    const parsedSize = parseInt(cfg.chat_font_size || "16", 10);
    const size = Math.max(13, Math.min(18, Number.isFinite(parsedSize) ? parsedSize : 16));
    r.style.setProperty("--wordai-chat-conversation-font-size", size + "px");
    r.style.setProperty("--wordai-chat-input-font-size", Math.max(13, size - 1) + "px");
  }
  function applyPosition() {
    const left = cfg.position === "bottom-left";
    $("#wordai-chat-bubble,#wordai-chat-window").toggleClass("wordai-pos-left", left);
  }
  function applyTheme() {
    const dark = cfg.theme === "auto"
      ? window.matchMedia("(prefers-color-scheme: dark)").matches
      : cfg.theme === "dark";
    $("#wordai-chat-window").attr("data-theme", dark ? "dark" : "light");
  }

  function isMobile() {
    return window.matchMedia ? window.matchMedia('(max-width: 640px)').matches : window.innerWidth <= 640;
  }

  /* ---------------- Avatar logic ---------------- */
  function ensureAvatarWraps() {
    const style = (cfg.avatar_style || "none").toLowerCase();
    const $b = $("#wordai-chat-bubble");
    const $w = $("#wordai-chat-window .assistant-name");
    if (style === "none") { $b.find(".wordai-avatar-wrap").remove(); $w.find(".wordai-avatar").remove(); return; }
    if (!$b.find(".wordai-avatar-wrap").length) $b.prepend('<span class="wordai-avatar-wrap"></span>');
    if (!$w.find(".wordai-avatar").length) $w.prepend('<span class="wordai-avatar"></span>');
  }

  function renderAvatars() {
    const style = (cfg.avatar_style || "none").toLowerCase();
    if (style === "none") return;
    const bubbleColor = cfg.bubble_color || "#0073aa";
    let avatarColor = cfg.avatar_color || "#fff";
    if (isColorTooSimilar(bubbleColor, avatarColor))
      avatarColor = adjustColorContrast(bubbleColor);
    const svg = buildAvatarSVG(style, avatarColor);
    $("#wordai-chat-bubble .wordai-avatar-wrap").html(`<span class="wordai-avatar">${svg}</span>`);
    $("#wordai-chat-window .assistant-name .wordai-avatar").html(svg);
  }

  const hexToRgb = h => { const i = parseInt(h.replace("#", ""), 16); return { r: (i >> 16) & 255, g: (i >> 8) & 255, b: i & 255 }; };
  const colorDistance = (a, b) => { const A = hexToRgb(a), B = hexToRgb(b); return Math.sqrt((A.r - B.r) ** 2 + (A.g - B.g) ** 2 + (A.b - B.b) ** 2); };
  const isColorTooSimilar = (a, b) => colorDistance(a, b) < 40;
  const adjustColorContrast = h => { const { r, g, b } = hexToRgb(h); const bright = (r * 299 + g * 587 + b * 114) / 1000; const d = bright > 150 ? -80 : 80; const clamp = v => Math.min(255, Math.max(0, v)); return "#" + ((1 << 24) + (clamp(r + d) << 16) + (clamp(g + d) << 8) + clamp(b + d)).toString(16).slice(1); };
  const buildAvatarSVG = (t, c) => {
    if (t === "male") return `<svg viewBox="0 0 64 64"><circle cx="32" cy="32" r="32" fill="${c}"/><circle cx="32" cy="24" r="10" fill="#0e7490"/><path d="M16 50c4-8 12-12 16-12s12 4 16 12" fill="#155e75"/></svg>`;
    if (t === "female") return `<svg viewBox="0 0 64 64"><circle cx="32" cy="32" r="32" fill="${c}"/><circle cx="32" cy="24" r="9" fill="#a21caf"/><path d="M14 50c5-9 12-12 18-12s13 3 18 12" fill="#86198f"/></svg>`;
    if (t === "bot") return `<svg viewBox="0 0 64 64"><rect x="8" y="14" width="48" height="36" rx="10" fill="${c}"/><circle cx="24" cy="32" r="6" fill="#0f172a"/><circle cx="40" cy="32" r="6" fill="#0f172a"/></svg>`;
    return "";
  };

  /* ---------------- Chat behavior ---------------- */
  function appendMsg(role, text) {
    const html = role === "ai" ? renderMarkdown(text) : escapeHtml(text);
    const el = $(`<div class="${role}-msg fade-in"><p>${html}</p></div>`);
    $messages.append(el);
    smoothToBottom();
  }

  function appendTyping() {
    const dots = `<span class="typing-dots"><span></span><span></span><span></span></span>`;
    $messages.append(`<div class="ai-msg typing">${dots}</div>`);
    $(".wordai-avatar").addClass("pulsing");
    smoothToBottom();
  }

  function streamLastTyping(text) {
    const p = $messages.find(".ai-msg.typing:last");
    if (p.length) {
      p.html(`<p>${renderMarkdown(text)}</p>`);
    } else {
      appendMsg("ai", text);
    }
    smoothToBottom();
  }

  function replaceLastTypingWith(txt, meta = {}) {
    const p = $messages.find(".ai-msg.typing:last");
    const metaHtml = buildAssistantMeta(meta);
    if (p.length) p.html(`<p>${renderMarkdown(txt)}</p>${metaHtml}`).removeClass("typing").attr("data-message-id", meta?.message_id || ""); else appendMsg("ai", txt);
    $(".wordai-avatar").removeClass("pulsing");
    smoothToBottom();
    focusFooter();
  }

  const escapeHtml = t => $("<div>").text(t).html();
  const escapeAttr = t => escapeHtml(t).replace(/"/g, "&quot;");
  const renderMarkdown = md => escapeHtml(md)
    .replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>")
    .replace(/\*(.*?)\*/g, "<em>$1</em>")
    .replace(/`([^`]+)`/g, "<code>$1</code>")
    .replace(/\[([^\]]+)\]\((https?:\/\/[^\)]+)\)/g, '<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>')
    .replace(/\n/g, "<br>");

  function buildAssistantMeta(meta = {}) {
    const parts = [];
    if (String(cfg.show_sources) === "1" && Array.isArray(meta.sources) && meta.sources.length) {
      const items = meta.sources.slice(0, 3).map(src => {
        const title = escapeHtml(src.title || "Source");
        const url = escapeAttr(src.url || "#");
        return `<a href="${url}" target="_blank" rel="noopener noreferrer">${title}</a>`;
      }).join("");
      parts.push(`<div class="wordai-chat-sources"><span>Sources used</span>${items}</div>`);
    }
    if (String(cfg.handoff_enabled) === "1" && String(meta.handoff_available) === "1") {
      const email = String(meta.handoff_email || cfg.handoff_email || "").trim();
      if (email) {
        const label = escapeHtml(meta.handoff_label || cfg.handoff_label || "Contact support");
        const subject = encodeURIComponent("AI chat support request");
        const body = encodeURIComponent(`Page: ${window.location.href}\n\nQuestion:\n`);
        parts.push(`<div class="wordai-chat-handoff"><a class="wordai-chat-handoff-btn" href="mailto:${escapeAttr(email)}?subject=${subject}&body=${body}">${label}</a></div>`);
      }
    }
    if (String(cfg.feedback_enabled) === "1" && meta.message_id) {
      parts.push(`
        <div class="wordai-chat-feedback" data-message-id="${escapeAttr(String(meta.message_id))}">
          <span>Was this helpful?</span>
          <button type="button" class="wordai-feedback-btn" data-rating="1" aria-label="Helpful">👍</button>
          <button type="button" class="wordai-feedback-btn" data-rating="-1" aria-label="Not helpful">👎</button>
        </div>
      `);
    }
    return parts.join("");
  }

  function isLeadCaptureActive() {
    return leadCfg && leadCfg.mode && leadCfg.mode !== "off" && leadCfg.fields;
  }

  function hasLeadProfileValue(key) {
    return String(leadProfile?.[key] || "").trim() !== "";
  }

  function hasRequiredLeadDetails() {
    if (!isLeadCaptureActive()) return true;
    const fields = leadCfg.fields || {};
    for (const key in fields) {
      if (fields[key]?.enabled && fields[key]?.required && !hasLeadProfileValue(key)) return false;
    }
    if (String(leadCfg.consent_required) === "1" && !leadProfile?.consent) return false;
    return true;
  }

  function shouldShowLeadPrompt(reason) {
    if (!isLeadCaptureActive()) return false;
    if ($messages.find(".wordai-chat-lead-form").length) return false;
    const mode = leadCfg.mode;
    if (mode === "required_before_chat") {
      return (reason === "open" || reason === "required") && !hasRequiredLeadDetails();
    }
    if (leadProfile?.submitted) return false;
    return (reason === "open" && (mode === "optional_before_chat" || mode === "required_before_chat"))
      || (reason === "after_reply" && mode === "after_first_reply")
      || (reason === "fallback" && mode === "fallback_handoff");
  }

  function renderLeadPrompt(reason = "manual") {
    if (!shouldShowLeadPrompt(reason)) return false;
    const fields = leadCfg.fields || {};
    const fieldHtml = Object.keys(fields).map(key => {
      const field = fields[key] || {};
      if (String(field.enabled) !== "1") return "";
      const required = String(field.required) === "1" ? "required" : "";
      const label = escapeHtml(field.label || key);
      const value = escapeAttr(leadProfile?.[key] || "");
      if (field.type === "textarea") {
        return `<label><span>${label}${required ? " *" : ""}</span><textarea data-lead-field="${escapeAttr(key)}" ${required}>${value}</textarea></label>`;
      }
      return `<label><span>${label}${required ? " *" : ""}</span><input type="${escapeAttr(field.type || "text")}" data-lead-field="${escapeAttr(key)}" value="${value}" ${required}></label>`;
    }).join("");
    const consentHtml = String(leadCfg.consent_required) === "1"
      ? `<label class="wordai-chat-lead-consent"><input type="checkbox" data-lead-consent ${leadProfile?.consent ? "checked" : ""}> <span>${escapeHtml(leadCfg.consent_text || "I agree to be contacted about my request.")} ${leadCfg.privacy_url ? `<a href="${escapeAttr(leadCfg.privacy_url)}" target="_blank" rel="noopener noreferrer">Privacy Policy</a>` : ""}</span></label>`
      : "";
    const skipHtml = leadCfg.mode === "required_before_chat" ? "" : `<button type="button" class="wordai-chat-lead-skip">${escapeHtml(leadCfg.labels?.skip || "Skip")}</button>`;
    const form = $(`
      <div class="ai-msg wordai-chat-lead-card">
        <form class="wordai-chat-lead-form" novalidate>
          <p>${escapeHtml(leadCfg.intro_text || "Leave your details and we can follow up with a better answer.")}</p>
          <div class="wordai-chat-lead-fields">${fieldHtml}</div>
          ${consentHtml}
          <div class="wordai-chat-lead-actions">
            <button type="submit" class="wordai-chat-lead-submit">${escapeHtml(leadCfg.labels?.submit || "Save details")}</button>
            ${skipHtml}
          </div>
        </form>
      </div>
    `);
    $messages.append(form);
    setChatInputBlocked(leadCfg.mode === "required_before_chat" && !hasRequiredLeadDetails());
    smoothToBottom();
    return true;
  }

  function ensureRequiredLeadPrompt() {
    if (leadCfg.mode !== "required_before_chat") return false;
    if (hasRequiredLeadDetails()) {
      setChatInputBlocked(false);
      return false;
    }
    const rendered = renderLeadPrompt("required");
    setChatInputBlocked(true);
    return rendered;
  }

  function collectLeadForm($form) {
    const next = Object.assign({}, leadProfile || {});
    $form.find("[data-lead-field]").each(function(){
      next[$(this).data("lead-field")] = $(this).val().trim();
    });
    next.consent = $form.find("[data-lead-consent]").length ? $form.find("[data-lead-consent]").is(":checked") : !!next.consent;
    return next;
  }

  function isValidLeadEmail(value) {
    const email = String(value || "").trim();
    if (!email) return true;
    return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
  }

  function leadValidationMessage(profile) {
    const fields = leadCfg.fields || {};
    for (const key in fields) {
      if (fields[key]?.enabled && fields[key]?.required && !String(profile?.[key] || "").trim()) {
        return (fields[key]?.label || key) + " is required.";
      }
    }
    if (fields.email?.enabled && !isValidLeadEmail(profile?.email)) {
      return "Please enter a valid email address.";
    }
    if (String(leadCfg.consent_required) === "1" && !profile?.consent) {
      return "Please confirm the consent checkbox before saving your details.";
    }
    return "";
  }

  function validateLeadProfile(profile) {
    return !leadValidationMessage(profile);
  }

  function showLeadFormError($form, message) {
    $form.addClass("has-error");
    let $error = $form.find(".wordai-chat-lead-error");
    if (!$error.length) {
      $error = $('<div class="wordai-chat-lead-error" role="alert"></div>');
      $form.prepend($error);
    }
    $error.text(message || "Please complete the highlighted contact details.");
  }

  function clearLeadFormError($form) {
    $form.removeClass("has-error").find(".wordai-chat-lead-error").remove();
  }

  function saveLeadProfile(profile, submitted = true) {
    leadProfile = Object.assign({}, leadProfile || {}, profile || {}, { submitted, _signature: getLeadCaptureSignature() });
    try { window.localStorage.setItem("wordai_chat_lead_profile", JSON.stringify(leadProfile)); } catch(e) {}
  }

  function getStoredLeadProfile() {
    try {
      const raw = window.localStorage.getItem("wordai_chat_lead_profile");
      const parsed = raw ? JSON.parse(raw) : {};
      return parsed && parsed._signature === getLeadCaptureSignature() ? parsed : {};
    } catch(e) {
      return {};
    }
  }

  function getLeadCaptureSignature() {
    const fields = leadCfg?.fields || {};
    const normalizedFields = {};
    Object.keys(fields).sort().forEach(key => {
      normalizedFields[key] = {
        enabled: String(fields[key]?.enabled) === "1" ? 1 : 0,
        required: String(fields[key]?.required) === "1" ? 1 : 0
      };
    });
    return JSON.stringify({
      mode: leadCfg?.mode || "off",
      consent_required: String(leadCfg?.consent_required) === "1" ? 1 : 0,
      fields: normalizedFields
    });
  }

  function setChatInputBlocked(blocked) {
    $input.prop("disabled", !!blocked);
    $send.prop("disabled", !!blocked || inFlight);
    $window.toggleClass("wordai-lead-required-blocked", !!blocked);
  }

  function leadPayloadFromProfile(profile) {
    return {
      lead_name: profile?.name || "",
      lead_email: profile?.email || "",
      lead_phone: profile?.phone || "",
      lead_company: profile?.company || "",
      lead_message: profile?.message || "",
      lead_consent: profile?.consent ? 1 : 0
    };
  }

  function leadPayload() {
    return leadPayloadFromProfile(leadProfile);
  }

  function focusFooter() {
    setTimeout(() => {
      $input.trigger("focus")[0]?.scrollIntoView({ behavior: "smooth", block: "center" });      
      // $window[0].scrollBy({ top: 24, behavior: "smooth" });
      smoothToBottom();
    }, 250);
  }

  // Keep footer fully visible - scroll to chat  window bottom - prev version
  /*function smoothToBottom() {       
    var chatBody = $('.wordai-chat-body');
    if (chatBody.length > 0) {
      chatBody.animate({
        scrollTop: chatBody.prop("scrollHeight")
      }, 250); // 250 is the animation speed in milliseconds
    }
  }*/
  // ===========================================================
  // 🧭 Smooth Scroll to Bottom (Enhanced R8.7.3)
  // -----------------------------------------------------------
  // - Uses native scroll behavior when supported
  // - Falls back to jQuery animate for legacy browsers
  // - Gives a soft easing feel instead of abrupt jump
  // ===========================================================
  function smoothToBottom() {
    const chatBody = document.querySelector('.wordai-chat-body');
    
    if (!chatBody) return;

    // 🧠 Modern browsers: use native smooth scrolling
    if ('scrollBehavior' in document.documentElement.style) {
      chatBody.scrollTo({
        top: chatBody.scrollHeight,
        behavior: 'smooth'
      });
    } 
    else {
      // 🧩 Fallback: jQuery animate for older browsers
      jQuery(chatBody).stop().animate(
        { scrollTop: chatBody.scrollHeight },
        400, // slower and smoother than 250ms
        'swing'
      );
    }
  }
  

  /* ---------------- Open / Close ---------------- */
  function toggleChat() { $window.is(":visible") ? closeChat() : openChat(); }

  function placeWindowAboveBubble() {
    // Bubble CSS: bottom:22px; height:46px; => top of bubble ≈ 68px from bottom
    // We keep a gap (~12px) above bubble. So window bottom ≈ 80px.
    const GAP = 12;
    const bubbleBottom = 22;
    const bubbleHeight = 46;
    const bottom = bubbleBottom + bubbleHeight + GAP; // 80px default

    const leftSide = $bubble.hasClass("wordai-pos-left");
    const desktopWidth = 360;
    const desktopHeight = Math.min(Math.max(Math.round(window.innerHeight * 0.8), 440), 760);

    // Desktop defaults
    let css = {
      bottom: bottom + "px",
      width: desktopWidth + "px",
      height: desktopHeight + "px",
      right: leftSide ? "auto" : "20px",
      left: leftSide ? "20px" : "auto"
    };

    // Mobile override: 90vw x 60vh, still above bubble
    if (isMobile()) {
      css.width = "90vw";
      css.height = Math.min(Math.round(window.innerHeight * 0.82), window.innerHeight - 96) + "px";
      css.right = leftSide ? "auto" : "20px";
      css.left  = leftSide ? "20px" : "auto";
    }
    $window.css(css);
  }

  function openChat() {
    if ($window.is(":visible")) return;
    placeWindowAboveBubble();
    $window.css({ display: "grid" })
           .animate({ opacity: 1, top: "-=20" }, 250)
           .addClass("is-open");
    $("#wordai-chat-bubble").attr("aria-expanded", "true"); // bubble stays visible
    if (!$messages.children().length) {
      appendMsg("ai", cfg.welcome_message || "Hello 👋 How can I help?");
      renderLeadPrompt("open");
    }
    ensureRequiredLeadPrompt();
    focusFooter();
  }

  function closeChat() {
    $window.animate({ opacity: 0, top: "+=20" }, 200, () => {
      $window.hide().removeClass("is-open");
      $("#wordai-chat-bubble").attr("aria-expanded", "false");
    });
  }

  /* ---------------- Send ---------------- */
  function sendMsg() {
    if (inFlight || $send.prop("disabled")) return;
    if (leadCfg.mode === "required_before_chat" && !hasRequiredLeadDetails()) {
      const rendered = ensureRequiredLeadPrompt();
      if (!rendered && !$messages.find(".wordai-chat-lead-form").length) {
        appendMsg("ai", leadCfg.labels?.required || "Please complete the required contact fields before asking a question.");
        setChatInputBlocked(true);
      }
      return;
    }
    const txt = $input.val().trim(); if (!txt) return;
    inFlight = true;
    $send.prop("disabled", true).addClass("is-sending");
    appendMsg("user", txt); $input.val(""); appendTyping();
    smoothToBottom();

    if (cfg.reply_mode === "stream" && window.fetch && window.TextDecoder && window.ReadableStream) {
      sendMsgStream(txt);
      return;
    }

    $.post(cfg.ajax_url, Object.assign({
      action: "sc_wordai_agent_chat_request",
      security: cfg.nonce,
      prompt: txt,
      conversation_key: conversationKey,
      page_url: window.location.href,
      page_title: document.title || ""
    }, leadPayload()), res => {
      if (res?.status === "success" && res.reply) {
        replaceLastTypingWith(res.reply, res.meta || {});
        maybePromptAfterAssistant(res.meta || {});
      }
      else replaceLastTypingWith("No response received.");
    }, "json").fail(() => replaceLastTypingWith("Request failed."))
      .always(() => { $send.prop("disabled", false).removeClass("is-sending"); inFlight = false; });
  }

  async function sendMsgStream(txt) {
    const body = new URLSearchParams(Object.assign({
      action: "sc_wordai_agent_chat_request",
      security: cfg.nonce,
      prompt: txt,
      conversation_key: conversationKey,
      page_url: window.location.href,
      page_title: document.title || ""
    }, leadPayload()));

    let finalText = "";
    let finalMeta = {};

    try {
      const response = await fetch(cfg.ajax_url, {
        method: "POST",
        headers: {
          "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
          "Accept": "text/event-stream, application/json"
        },
        body: body.toString(),
        credentials: "same-origin"
      });

      if (!response.ok) throw new Error("Request failed.");

      const contentType = (response.headers.get("content-type") || "").toLowerCase();
      if (contentType.includes("application/json")) {
        const json = await response.json();
        if (json?.status === "success" && json.reply) {
          replaceLastTypingWith(json.reply, json.meta || {});
          maybePromptAfterAssistant(json.meta || {});
        }
        else replaceLastTypingWith(json?.reply || "No response received.");
        return;
      }

      if (!response.body) throw new Error("Streaming is not available in this browser.");

      const reader = response.body.getReader();
      const decoder = new TextDecoder();
      let buffer = "";
      let streamFinished = false;

      while (!streamFinished) {
        const { value, done } = await reader.read();
        if (done) break;

        buffer += decoder.decode(value, { stream: true });
        const parsed = consumeStreamBuffer(buffer, finalText);
        buffer = parsed.buffer;
        finalText = parsed.text;

        if (parsed.meta) finalMeta = parsed.meta;
        if (parsed.error) throw new Error(parsed.error);
        if (parsed.updated) streamLastTyping(finalText);
        if (parsed.done) streamFinished = true;
      }

      const tail = consumeStreamBuffer(buffer + decoder.decode(), finalText, true);
      finalText = tail.text;

      if (tail.meta) finalMeta = tail.meta;
      if (tail.error) throw new Error(tail.error);
      if (tail.updated) streamLastTyping(finalText);

      replaceLastTypingWith(finalText || "No response received.", finalMeta);
      maybePromptAfterAssistant(finalMeta || {});
    } catch (err) {
      replaceLastTypingWith(err?.message || "Request failed.");
    } finally {
      $send.prop("disabled", false).removeClass("is-sending");
      inFlight = false;
    }
  }

  function consumeStreamBuffer(buffer, currentText, flushAll = false) {
    let working = buffer.replace(/\r\n/g, "\n");
    let text = currentText;
    let updated = false;
    let done = false;
    let error = "";
    let meta = null;

    while (true) {
      const boundary = working.indexOf("\n\n");
      if (boundary === -1) break;

      const block = working.slice(0, boundary).trim();
      working = working.slice(boundary + 2);
      if (!block) continue;

      const parsed = parseStreamBlock(block);
      if (parsed.error) {
        error = parsed.error;
        break;
      }
      if (parsed.meta) meta = parsed.meta;
      if (parsed.chunk) {
        text += parsed.chunk;
        updated = true;
      }
      if (parsed.done) {
        done = true;
        break;
      }
    }

    if (flushAll && !done && !error && working.trim()) {
      const parsed = parseStreamBlock(working.trim());
      if (parsed.error) {
        error = parsed.error;
      } else if (parsed.chunk) {
        text += parsed.chunk;
        updated = true;
        working = "";
      } else if (parsed.meta) {
        meta = parsed.meta;
        working = "";
      } else if (parsed.done) {
        done = true;
        working = "";
      }
    }

    return { buffer: working, text, updated, done, error, meta };
  }

  function parseStreamBlock(block) {
    const payloadParts = [];

    block.split("\n").forEach(line => {
      const trimmed = line.trim();
      if (!trimmed || trimmed.startsWith("event:")) return;
      if (trimmed.startsWith("data:")) payloadParts.push(trimmed.slice(5).trim());
      else payloadParts.push(trimmed);
    });

    const payload = payloadParts.join("\n").trim();
    if (!payload) return {};
    if (payload === "[DONE]") return { done: true };

    let parsed;
    try {
      parsed = JSON.parse(payload);
    } catch (e) {
      return {};
    }

    if (parsed?.error?.message) return { error: parsed.error.message };
    if (parsed?.wordaiMeta) return { meta: parsed.wordaiMeta };
    if (typeof parsed.replyChunk === "string") return { chunk: parsed.replyChunk };

    const openAiDelta = parsed?.choices?.[0]?.delta?.content;
    if (typeof openAiDelta === "string") return { chunk: openAiDelta };

    if (Array.isArray(openAiDelta)) {
      const chunk = openAiDelta
        .map(item => typeof item?.text === "string" ? item.text : "")
        .join("");
      if (chunk) return { chunk };
    }

    return {};
  }

  /* ---------------- UX ---------------- */
  function wireUX() {
    $(document).off("click.wordaiChatBubble").on("click.wordaiChatBubble", "#wordai-chat-bubble", function(e){ e.preventDefault(); toggleChat(); });
    $close.on("click", closeChat);
    $send.on("click", sendMsg);
    $messages.on("click", ".wordai-feedback-btn", submitFeedback);
    $messages.on("submit", ".wordai-chat-lead-form", submitLeadForm);
    $messages.on("click", ".wordai-chat-lead-skip", function(){
      saveLeadProfile({}, true);
      $(this).closest(".wordai-chat-lead-card").fadeOut(160, function(){ $(this).remove(); });
      setChatInputBlocked(false);
    });

    // Enter to send (no Shift; respects disabled)
    $input.on("keypress", e => {
      if (e.which === 13 && !e.shiftKey) {
        e.preventDefault();
        if (!$send.prop("disabled")) sendMsg();
      }
    });

    // Reposition on resize/orientation changes
    $(window).on("resize orientationchange", () => { if ($window.is(":visible")) placeWindowAboveBubble(); });

    // ESC + outside click (close)
    $(document).on("keydown", e => { if (e.key === "Escape" && $window.is(":visible")) closeChat(); });
    $(document).on("mousedown", e => {
      if (!$window.is(":visible")) return;
      if (!$(e.target).closest("#wordai-chat-window,#wordai-chat-bubble").length) closeChat();
    });
  }

  function maybeShowBubble() {
    if (String(cfg.auto_display) === "1") $("#wordai-chat-bubble").fadeIn(200);
  }

  function submitLeadForm(e) {
    e.preventDefault();
    const $form = $(this);
    const profile = collectLeadForm($form);
    const validationMessage = leadValidationMessage(profile);
    if (validationMessage) {
      showLeadFormError($form, validationMessage);
      return;
    }
    clearLeadFormError($form);
    saveLeadProfile(profile, true);
    const $submit = $form.find(".wordai-chat-lead-submit");
    $submit.prop("disabled", true).addClass("is-saving");

    $.post(cfg.ajax_url, Object.assign({
      action: "sc_wordai_agent_save_lead",
      security: cfg.nonce,
      conversation_key: conversationKey,
      page_url: window.location.href,
      page_title: document.title || ""
    }, leadPayloadFromProfile(profile)), res => {
      if (res?.success || res?.status === "success") {
        const savedText = escapeHtml(res?.data?.message || leadCfg.labels?.saved || "Thanks. Your details were saved for follow-up.");
        $form.closest(".wordai-chat-lead-card").replaceWith('<div class="ai-msg wordai-chat-lead-saved"><p>' + savedText + '</p></div>');
        setChatInputBlocked(false);
        focusFooter();
        return;
      }
      showLeadFormError($form, res?.data?.message || "Unable to save your details. Please try again.");
    }, "json").fail(xhr => {
      const message = xhr?.responseJSON?.data?.message || "Unable to save your details. Please try again.";
      showLeadFormError($form, message);
    }).always(() => {
      $submit.prop("disabled", false).removeClass("is-saving");
    });
  }

  function maybePromptAfterAssistant(meta = {}) {
    firstReplySeen = true;
    if (String(meta.fallback_used) === "1") {
      renderLeadPrompt("fallback");
      return;
    }
    renderLeadPrompt("after_reply");
  }

  function submitFeedback(e) {
    e.preventDefault();
    const $btn = $(this);
    const $wrap = $btn.closest(".wordai-chat-feedback");
    const messageId = $wrap.data("message-id");
    if (!messageId || $wrap.hasClass("is-submitted")) return;
    $wrap.addClass("is-submitted");
    $wrap.find("button").prop("disabled", true);
    if (!$wrap.find("em").length) $wrap.append('<em>Thanks</em>');

    $.post(cfg.ajax_url, {
      action: "sc_wordai_agent_chat_feedback",
      security: cfg.nonce,
      message_id: messageId,
      rating: $btn.data("rating"),
      conversation_key: conversationKey,
      page_url: window.location.href
    }, res => {
      if (!res || res.status !== "success") {
        $wrap.removeClass("is-submitted");
        $wrap.find("button").prop("disabled", false);
        $wrap.find("em").remove();
      }
    }, "json").fail(() => {
      $wrap.removeClass("is-submitted");
      $wrap.find("button").prop("disabled", false);
      $wrap.find("em").remove();
    });
  }

  function getConversationKey() {
    const key = "wordai_chat_conversation_key";
    try {
      let current = window.localStorage.getItem(key);
      if (!current) {
        current = "wa-" + Date.now().toString(36) + "-" + Math.random().toString(36).slice(2, 12);
        window.localStorage.setItem(key, current);
      }
      return current;
    } catch (e) {
      return "wa-" + Date.now().toString(36) + "-" + Math.random().toString(36).slice(2, 12);
    }
  }

})(jQuery);
