var showWordAIGlobalNotice;

/* global jQuery, scwordai_agent */
(function ($) {
  "use strict";

  console.log("WordAI Agent Localized data:", scwordai_agent);

  function getSelectedEmbeddingModel() {
      return $("#wa-embed-model").val() || "";
  }

  function getSelectedPostTypes() {
    return $(".wa-cpt:checked")
      .map(function () { return $(this).val(); })
      .get();
  }

  function resetFailedCache() {
    failedItemsCache = [];
  }

  function updatePostTypeSummary() {
    const $types = $(".wa-cpt");
    const selected = $types
      .filter(":checked")
      .map(function () {
        return $(this).data("label") || $(this).val();
      })
      .get();

    let summary = "No content types selected";

    if (selected.length === $types.length && selected.length > 0) {
      summary = "All content types selected";
    } else if (selected.length === 1) {
      summary = `${selected[0]} selected`;
    } else if (selected.length > 1) {
      summary = `${selected.slice(0, 2).join(", ")}${selected.length > 2 ? ` +${selected.length - 2}` : ""} selected`;
    }

    $(".wordai-agent-post-type-summary-text").text(summary);
  }

  function getSelectedPostTypeMeta() {
    return $(".wa-cpt:checked")
      .map(function () {
        return {
          value: $(this).val(),
          label: $(this).data("label") || $(this).val()
        };
      })
      .get();
  }

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

  function formatDateTime(value) {
    if (!value) return "—";
    return escapeHtml(String(value).replace("T", " "));
  }

  function getVoyageMaskValue($input) {
    return String($input.data("mask") || "");
  }

  function getVoyageRealValue($input) {
    return String($input.data("real") || "");
  }

  function getVoyageKeyForSave() {
    const $input = jQuery("#wa-anthropic-voyage-api-key");
    const typed = String($input.val() || "").trim();
    const real = getVoyageRealValue($input);
    const mask = getVoyageMaskValue($input);

    if (typed === "") {
      return "";
    }

    if (typed === mask) {
      return real;
    }

    return typed;
  }

  jQuery(document).on("change", "#wa-anthropic-voyage-show-key", function () {
    const $input = jQuery("#wa-anthropic-voyage-api-key");
    const real = getVoyageRealValue($input);
    const mask = getVoyageMaskValue($input);

    if (jQuery(this).is(":checked")) {
      $input.val(real);
      return;
    }

    if (real) {
      $input.val(mask);
    } else {
      $input.val("").attr("placeholder", mask);
    }
  });

  jQuery(document).on("click", "#wa-anthropic-voyage-save-btn", function (e) {
    e.preventDefault();

    const $btn = jQuery(this).prop("disabled", true).text("Saving...");
    const $input = jQuery("#wa-anthropic-voyage-api-key");
    const typedRealKey = getVoyageKeyForSave();

    jQuery.post(scwordai_agent.adminajax_url, {
      action: "sc_wordai_agent_save_anthropic_embedding_settings",
      security: scwordai_agent.nonce,
      voyage_api_key: typedRealKey
    })
    .done(function (resp) {
      if (resp && resp.status === "success") {
        const masked = resp.masked_key || "pa-xxxxxxxxxxxxxxxxxxxxxxxx";
        const existingReal = getVoyageRealValue($input);
        const finalReal = typedRealKey || existingReal;

        $input
          .data("real", finalReal)
          .data("mask", masked)
          .attr("data-real", finalReal)
          .attr("data-mask", masked)
          .val(resp.configured ? masked : "")
          .attr("placeholder", resp.configured ? "" : masked);

        jQuery("#wa-anthropic-voyage-show-key").prop("checked", false);
        jQuery("#wa-anthropic-voyage-status")
          .text(resp.configured ? "Voyage API key is configured." : "Voyage API key is not configured yet.")
          .css("color", resp.configured ? "#00a32a" : "#b32d2e");
        showWordAIGlobalNotice("success", resp.feedbackMsg || "Voyage embedding settings saved successfully.");
      } else {
        showWordAIGlobalNotice("error", (resp && resp.feedbackMsg) || "Saving Voyage embedding settings failed.");
      }
    })
    .fail(function () {
      showWordAIGlobalNotice("error", "Saving Voyage embedding settings failed.");
    })
    .always(function () {
      $btn.prop("disabled", false).text("Save");
    });
  });

  $("#wa-embed-model").on("change", resetFailedCache);
  $(".wa-cpt").on("change", function () {
    resetFailedCache();
    updatePostTypeSummary();
    sourceItemsPage = 1;
    loadSourceItems();
  });
  $(".wordai-agent-select-all-post-types").on("click", function (e) {
    e.preventDefault();
    $(".wa-cpt").prop("checked", true).trigger("change");
  });
  $(".wordai-agent-clear-post-types").on("click", function (e) {
    e.preventDefault();
    $(".wa-cpt").prop("checked", false).trigger("change");
  });
  updatePostTypeSummary();
  renderSourceTypeTabs();

  $(document).on("click", ".wordai-agent-source-tab", function (e) {
    e.preventDefault();
    activeSourceType = $(this).data("type") || "";
    sourceItemsPage = 1;
    loadSourceItems();
  });

  $("#wa-source-refresh").on("click", function (e) {
    e.preventDefault();
    loadSourceItems();
  });

  $("#wa-source-prev").on("click", function (e) {
    e.preventDefault();
    if (sourceItemsPage > 1) {
      sourceItemsPage--;
      loadSourceItems();
    }
  });

  $("#wa-source-next").on("click", function (e) {
    e.preventDefault();
    if (sourceItemsPage < sourceItemsTotalPages) {
      sourceItemsPage++;
      loadSourceItems();
    }
  });

  $("#wa-source-status-filter").on("change", function () {
    sourceItemsPage = 1;
    loadSourceItems();
  });

  $("#wa-source-search").on("input", function () {
    clearTimeout(sourceSearchTimer);
    sourceSearchTimer = setTimeout(function () {
      sourceItemsPage = 1;
      loadSourceItems();
    }, 350);
  });

  $(document).on("change", ".wa-source-select", function () {
    const $checkbox = $(this);
    const sourceRowId = Number($checkbox.data("source-row-id") || 0);
    const selected = $checkbox.is(":checked") ? 1 : 0;

    $checkbox.prop("disabled", true);

    $.post(scwordai_agent.adminajax_url, {
      action: "sc_wordai_agent_update_source_selection",
      security: scwordai_agent.nonce,
      source_row_id: sourceRowId,
      selected: selected
    })
      .done((resp) => {
        if (resp && resp.status === "success") {
          updateSourceCount(resp);
          resetFailedCache();
          loadSourceItems();
        } else {
          $checkbox.prop("checked", !selected);
          showWordAIGlobalNotice("warning", (resp && resp.feedbackMsg) || "Could not update content selection.");
          loadSourceItems();
        }
      })
      .fail(() => {
        $checkbox.prop("checked", !selected);
        showWordAIGlobalNotice("error", "Request failed while updating content selection.");
        loadSourceItems();
      });
  });

  loadSourceItems();


  // Cached selectors
  const $estimateBtn = $("#wa-estimate");
  const $startBtn = $("#wa-start-indexing");
  const $cancelBtn = $("#wa-cancel-indexing");
  const $refreshBtn = $("#wa-refresh-status");
  const $estimateBox = $("#wa-estimate-result");
  const $progress = $("#wa-progress");
  const $bar = $(".wa-progress-bar", $progress);
  const $label = $(".wa-progress-label", $progress);
  const $eta = $(".wa-progress-eta");
  // Global message helper
  // const $globalMsg = $("#sc-wordai-global-message");

  var failedItemsCache = []; // 🧩 store last failed list for quick reuse
  var sourceItemsPage = 1;
  var sourceItemsTotalPages = 1;
  var activeSourceType = "";
  var sourceSearchTimer = null;

  var statusMeta = {
    embedded: { label: "Embedded", cls: "is-embedded", icon: "✓" },
    needs_embedding: { label: "Needs embedding", cls: "needs-embedding", icon: "○" },
    needs_reindex: { label: "Re-index required", cls: "needs-reindex", icon: "!" },
    failed: { label: "Failed", cls: "is-failed", icon: "!" }
  };

  function renderSourceTypeTabs() {
    const $tabs = $("#wa-source-tabs");
    if (!$tabs.length) return;

    const types = getSelectedPostTypeMeta();
    if (!types.length) {
      activeSourceType = "";
      $tabs.html('<span class="wordai-agent-source-empty">Select a content type above to review individual items.</span>');
      $("#wa-source-items").html('<tr><td colspan="5">Select at least one content type to load content items.</td></tr>');
      $("#wa-source-page-label").text("Page 1 of 1");
      $("#wa-source-prev, #wa-source-next").prop("disabled", true);
      return;
    }

    if (!activeSourceType || !types.some((type) => type.value === activeSourceType)) {
      activeSourceType = types[0].value;
      sourceItemsPage = 1;
    }

    const html = types.map((type) => {
      const active = type.value === activeSourceType ? " is-active" : "";
      return `<button type="button" class="wordai-agent-source-tab${active}" data-type="${escapeHtml(type.value)}">${escapeHtml(type.label)}</button>`;
    }).join("");

    $tabs.html(html);
  }

  function updateSourceCount(resp) {
    const selected = Number((resp && resp.selected_count) || 0);
    const limit = Number((resp && resp.limit) || $(".wordai-agent-source-selection-card").data("embedding-limit") || 0);
    $("#wa-source-selected-count").text(selected.toLocaleString());
    if (limit > 0 && selected >= limit) {
      $(".wordai-agent-source-selection-card").addClass("is-limit-reached");
    } else {
      $(".wordai-agent-source-selection-card").removeClass("is-limit-reached");
    }
  }

  function renderSourceItems(resp) {
    const $tbody = $("#wa-source-items");
    if (!$tbody.length) return;

    const items = (resp && resp.items) || [];
    sourceItemsTotalPages = Number(resp.total_pages || 1);
    sourceItemsPage = Number(resp.page || sourceItemsPage || 1);
    updateSourceCount(resp);

    if (!items.length) {
      $tbody.html('<tr><td colspan="5">No matching content items found.</td></tr>');
    } else {
      const limitReached = !!(resp && resp.limit_reached);
      const rows = items.map((item) => {
        const meta = statusMeta[item.status] || statusMeta.needs_embedding;
        const checked = item.selected ? " checked" : "";
        const disabled = limitReached && !item.selected ? " disabled" : "";
        const title = escapeHtml(item.title || `Content #${item.source_id}`);
        const type = escapeHtml(item.type_label || item.source_type || "");
        const edit = item.edit_url
          ? `<a href="${escapeHtml(item.edit_url)}" target="_blank" rel="noopener">${title}</a>`
          : title;

        return `
          <tr>
            <td>
              <input type="checkbox" class="wa-source-select" data-source-row-id="${Number(item.source_row_id)}"${checked}${disabled}>
            </td>
            <td>
              <strong>${edit}</strong>
              <span class="wordai-agent-source-type">${type} #${Number(item.source_id)}</span>
            </td>
            <td><span class="wordai-agent-source-status ${meta.cls}"><span>${meta.icon}</span>${meta.label}</span></td>
            <td>${formatDateTime(item.modified)}</td>
            <td>${formatDateTime(item.last_embed)}</td>
          </tr>`;
      }).join("");

      $tbody.html(rows);
    }

    $("#wa-source-page-label").text(`Page ${sourceItemsPage} of ${sourceItemsTotalPages}`);
    $("#wa-source-prev").prop("disabled", sourceItemsPage <= 1);
    $("#wa-source-next").prop("disabled", sourceItemsPage >= sourceItemsTotalPages);
  }

  function loadSourceItems() {
    const $card = $(".wordai-agent-source-selection-card");
    if (!$card.length) return;

    const postTypes = getSelectedPostTypes();
    renderSourceTypeTabs();

    if (!postTypes.length || !activeSourceType) return;

    $("#wa-source-items").html('<tr><td colspan="5">Loading content items...</td></tr>');

    $.post(scwordai_agent.adminajax_url, {
      action: "sc_wordai_agent_get_source_items",
      security: scwordai_agent.nonce,
      post_types: postTypes,
      active_type: activeSourceType,
      page: sourceItemsPage,
      per_page: 20,
      search: $("#wa-source-search").val() || "",
      status_filter: $("#wa-source-status-filter").val() || "all"
    })
      .done((resp) => {
        if (resp && resp.status === "success") {
          renderSourceItems(resp);
        } else {
          $("#wa-source-items").html(`<tr><td colspan="5">${escapeHtml((resp && resp.feedbackMsg) || "Unable to load content items.")}</td></tr>`);
        }
      })
      .fail(() => {
        $("#wa-source-items").html('<tr><td colspan="5">Request failed while loading content items.</td></tr>');
      });
  }

  

  /**
   * ===============================================================
   * Helper: Styled Notice Message
   * ===============================================================
   */
  function showNotice($el, type, msg) {
    $el
      .removeClass("notice-success notice-error notice-warning notice-info")
      .addClass("notice-" + type)
      .html("<p>" + msg + "</p>")
      .fadeIn(150);
  }

  // ===============================================================
  // 🔔 Global Message Helper (shared for all admin tabs)
  // ---------------------------------------------------------------
  // Works with any screen using #sc-wordai-global-message container.
  // Shows WP-style dismissible notice with icon, fade, and auto-hide.
  // Type: 'success' | 'error' | 'info' | 'warning'
  // ===============================================================
  showWordAIGlobalNotice = function (type, msg, persistent = false) {
    const $body = jQuery('body');

    // Ensure container exists or create it dynamically
    let $globalMsg = jQuery('#sc-wordai-global-message');
    if (!$globalMsg.length) {
      $body.prepend('<div id="sc-wordai-global-message" class="notice" style="display:none;margin-top:10px;"></div>');
      $globalMsg = jQuery('#sc-wordai-global-message');
    }

    // Define style maps
    const colors = {
      success: '#00a32a',
      error: '#d63638',
      info: '#2271b1',
      warning: '#dba617',
    };
    const icons = {
      success: 'yes-alt',
      error: 'warning',
      info: 'info',
      warning: 'flag',
    };

    // Build markup
    const color = colors[type] || '#2271b1';
    const icon  = icons[type] || 'info';

    const html = `
      <button type="button" class="notice-dismiss" style="position:absolute;top:6px;right:6px;">
        <span class="screen-reader-text">Dismiss this notice.</span>
      </button>
      <p style="margin:4px 0;font-size:14px;color:#1d2327;">
        <span class="dashicons dashicons-${icon}" style="color:${color};vertical-align:middle;margin-right:6px;"></span>
        ${msg}
      </p>
    `;

    // Apply classes and styles
    $globalMsg
      .removeClass('notice-success notice-error notice-info notice-warning')
      .addClass(`notice notice-${type}`)
      .css({
        borderLeft: `4px solid ${color}`,
        display: 'none',
        background: '#fff',
        position: 'relative',
        boxShadow: '0 1px 3px rgba(0,0,0,0.1)',
        padding: '10px 14px',
        borderRadius: '4px',
      })
      .html(html)
      .fadeIn(250);

    // Scroll smoothly to notice
    jQuery('html, body').animate({ scrollTop: $globalMsg.offset().top - 50 }, 400);

    // Close button handler
    $globalMsg.find('.notice-dismiss').on('click', function () {
      $globalMsg.fadeOut(250);
    });

    // Auto-hide timer (if not persistent)
    if (!persistent) {
      clearTimeout(window._scWordAIHideTimer);
      window._scWordAIHideTimer = setTimeout(() => {
        $globalMsg.fadeOut(400);
      }, 5000);
    }
  };


  /**
   * ===============================================================
   * Helper: Progress Bar + Remaining Time
   * ===============================================================
   */
  // function updateProgressVisual(pct) {
  //   $bar.stop().animate({ width: pct + "%" }, 400);
  //   $label.text(`${pct.toFixed(1)}%`);
  // }

  // function formatTime(seconds) {
  //   const m = Math.floor(seconds / 60);
  //   const s = Math.floor(seconds % 60);
  //   return m > 0 ? `~${m}m ${s}s left` : `~${s}s left`;
  // }

  
  /**
   * ===============================================================
   * Estimate Cost — robust JSON handling + duration & cost formatting
   * ===============================================================
   */
  $estimateBtn.on("click", function (e) {
    e.preventDefault();

    const postTypes = $(".wa-cpt:checked")
      .map(function () {
        return $(this).val();
      })
      .get();

    if (postTypes.length === 0) {
      showWordAIGlobalNotice("error", "Please select at least one post type.");
      return;
    }

    $estimateBtn.prop("disabled", true).text("Estimating...");
    showNotice($estimateBox, "info", "Estimating cost… Please wait.");

      $.post(scwordai_agent.adminajax_url, {
          action: "sc_wordai_agent_estimate_index_cost",
          security: scwordai_agent.nonce,
          post_types: postTypes,
          embedding_model: getSelectedEmbeddingModel(),
          chunk_size: $("#wa-chunk-size").val(),
      })
      .done((resp) => {
        if (resp.status === "success") {
          // ✅ Parse tokens correctly even if string with commas
          const totalTokensNum = Number(
            String(resp.total_tokens || "0").replace(/,/g, "")
          );
          const formattedTokens = totalTokensNum.toLocaleString();

          // ✅ Format duration (min → sec/min)
          let durationMin = parseFloat(resp.duration_est || 0);
          let durationText = "";
          if (durationMin < 1) {
            const sec = Math.max(durationMin * 60, 1).toFixed(0);
            durationText = `${sec} sec`;
          } else {
            durationText = `${durationMin.toFixed(2)} min`;
          }

          // ✅ Format cost safely (handles scientific notation properly)
          let costVal = "$0.000000";
          if (resp.estimated_cost) {
            // Convert string → number correctly (handles 2.1e-5, etc.)
            const raw = String(resp.estimated_cost).replace(/[^0-9eE+.\-]/g, "");
            let costNum = Number(raw);
            if (isNaN(costNum)) costNum = 0;
            costVal = "$" + costNum.toFixed(costNum < 0.01 ? 6 : 4);
          }

          // ✅ Optional debug line
          const debugLine = resp.feedbackMsg
            ? `<br><small style="opacity:0.7;">${resp.feedbackMsg}</small>`
            : "";          

          // ✅ Final display
          showNotice(
            $estimateBox,
            "success",
            `<strong>${formattedTokens}</strong> tokens ≈ <strong>${costVal}</strong><br>
            using <code>${resp.model}</code><br>
            ⏱️ Estimated duration: <strong>${durationText}</strong>`
          );
        } else {
          showNotice($estimateBox, "error", resp.feedbackMsg || "Estimation failed.");
        }
      })
      .fail(() => showNotice($estimateBox, "error", "Request failed."))
      .always(() => $estimateBtn.prop("disabled", false).html("<i class='fa-solid fa-calculator'></i> Estimate Cost"));
  });



  /* ===============================================================
    ✅ Stable Start / Progress / Cancel (Final 2025)
    ---------------------------------------------------------------
    - Starts polling immediately (don’t wait for backend response)
    - Ignores any late responses after Cancel / new Start
    - Recursive setTimeout (no overlapping intervals)
    - Resilient to “instant complete” or “all failed” cases
    - Always shows a meaningful message (no blanks)
    - Uses showWordAIGlobalNoticeFromStatus() for terminal states
    =============================================================== */

  /**
   * Optional convenience wrapper (non-breaking).
   * Keep this near your showWordAIGlobalNotice() helper.
   */
  function showWordAIGlobalNoticeFromStatus(resp) {
    if (!resp || !resp.feedbackMsg) return;
    let type = "info";
    switch ((resp.job_status || "").toLowerCase()) {
      case "completed":
      case "running":
        type = "success"; break;
      case "cancelled":
        type = "warning"; break;
      case "failed":
        type = "error";   break;
      case "idle":
      default:
        type = "info";
    }
    showWordAIGlobalNotice(type, resp.feedbackMsg);
  }

  // ---- Local state (shared) ----
  let pollingActive = false;
  let jobFinished   = false;
  let lastPercent   = 0;
  let startTime     = null;
  let warmupTimer   = null;
  let runToken      = 0;     // ✅ increment per run; used to ignore stale callbacks

  /** Update progress bar + numeric label */
  function updateProgressVisual(pct) {
    pct = Math.max(0, Math.min(100, Number(pct) || 0));
    $bar.stop().animate({ width: pct + "%" }, 300);
    $label.text(`${pct.toFixed(1)}%`);
  }

  /** Convert seconds → “~Xm Ys left” */
  function formatTime(seconds) {
    const m = Math.floor(seconds / 60);
    const s = Math.floor(seconds % 60);
    return m > 0 ? `~${m}m ${s}s left` : `~${s}s left`;
  }

  /** Stop polling and restore UI state */
  function stopPolling(finalMsg, type = "success") {
    pollingActive = false;
    jobFinished   = true;

    // Ensure the bar visually completes on success (not on warning/error)
    if (type === "success") updateProgressVisual(100);

    $eta.hide();
    $label.text(finalMsg || "");

    // Cleanup warmup (if still running)
    if (warmupTimer) { clearInterval(warmupTimer); warmupTimer = null; }

    setTimeout(() => {
      $progress.fadeOut(400);
      $startBtn
        .prop("disabled", false)
        .html('<i class="fa-solid fa-play"></i> Start Indexing');
      $estimateBtn.prop("disabled", false);
      $cancelBtn.hide();
    }, 600);

    showWordAIGlobalNotice(type, finalMsg || "Done.");
  }

  /** Single-shot poller (calls itself via setTimeout) */
  function pollProgressLoop(myRun) {
    if (!pollingActive || jobFinished) return;
    // stale guard
    if (myRun !== runToken) return;

    $.post(scwordai_agent.adminajax_url, {
      action: "sc_wordai_agent_get_progress",
      security: scwordai_agent.nonce,
    })
      .done((resp) => {
        // stale guard
        if (myRun !== runToken) return;

        if (!(resp && resp.status === "success")) {
          // Retry softly if malformed
          return setTimeout(() => pollProgressLoop(myRun), 2500);
        }

        const pct    = parseFloat(resp.percent || 0);
        const status = (resp.job_status || "idle").toLowerCase();

        // Ensure we never pass an empty message to the notice system
        let msg = (resp.feedbackMsg || "").trim();
        if (!msg) {
          if (status === "running" && pct > 0 && pct < 100) {
            msg = `Processing… ${pct.toFixed(1)}%`;
          } else if (status === "running") {
            msg = "Preparing first batch…";
          } else if (status === "completed") {
            msg = "Indexing finished.";
          } else if (status === "cancelled") {
            msg = "Indexing cancelled by user.";
          } else { // idle / fallback
            msg = "No active indexing job.";
          }
          resp.feedbackMsg = msg; // so wrapper sees it too
        }

        // --- Progress Bar Update ---
        if (pct > lastPercent) {
          updateProgressVisual(pct);
          lastPercent = pct;
        }

        // --- ETA / Feedback ---
        if (status === "running" && pct >= 1 && pct < 100) {
          if (!startTime) startTime = Date.now();
          const elapsed   = (Date.now() - startTime) / 1000;
          const remaining = (elapsed * (100 - pct)) / (pct || 1);
          $eta.text(formatTime(remaining)).fadeIn(120);
        } else if (status === "running" && pct < 1) {
          $eta.text("Preparing first batch…").fadeIn(120);
        } else {
          $eta.fadeOut(150);
        }

        // --- Terminal states ---
        if (status === "completed" || status === "cancelled" || status === "idle") {
          if (status === "completed" && pct < 100) updateProgressVisual(100);
          // Route all terminal messages through the wrapper (auto type)
          showWordAIGlobalNoticeFromStatus(resp);
          return stopPolling(msg, status === "cancelled" ? "warning" : "success");
        }

        // Keep polling
        setTimeout(() => pollProgressLoop(myRun), 2500);
      })
      .fail(() => {
        if (myRun !== runToken) return; // stale
        if (pollingActive) setTimeout(() => pollProgressLoop(myRun), 3000);
      });
  }

  /** ▶ Start Indexing */
  $startBtn.on("click", function (e) {
    e.preventDefault();

    // Bump run token and snapshot for this run
    runToken++;
    const myRun = runToken;

    // ✅ Require at least one post type selected
    const postTypes = $(".wa-cpt:checked")
      .map(function () { return $(this).val(); })
      .get();

    if (postTypes.length === 0) {
      showWordAIGlobalNotice("error", "Please select at least one post type before starting indexing.");
      return;
    }


    // Reset state
    pollingActive = true;
    jobFinished   = false;
    lastPercent   = 0;
    startTime     = null;

    $estimateBtn.prop("disabled", true);
    $startBtn
      .prop("disabled", true)
      .html('<span class="sc-wordai-spinner"></span> Indexing…');
    $cancelBtn.show().prop("disabled", false);
    $progress.fadeIn(200);
    updateProgressVisual(0);
    $eta.text("Initializing…").show();

    // Gentle warm-up animation while backend starts
    let warm = 0;
    if (warmupTimer) { clearInterval(warmupTimer); }
    warmupTimer = setInterval(() => {
      if (!pollingActive || myRun !== runToken) return (clearInterval(warmupTimer), warmupTimer = null);
      if (warm < 10 && lastPercent < 1) {
        warm += 1;
        updateProgressVisual(warm);
      } else {
        clearInterval(warmupTimer);
        warmupTimer = null;
      }
    }, 250);

    // ✅ Start polling IMMEDIATELY (don’t wait for backend response)
    setTimeout(() => {
      if (myRun !== runToken) return;   // stale guard
      pollProgressLoop(myRun);
    }, 300);

    // const postTypes = $(".wa-cpt:checked")
    //   .map(function () { return $(this).val(); })
    //   .get();

    // Kick off indexing (launcher returns fast)
      $.post(scwordai_agent.adminajax_url, {
          action: "sc_wordai_agent_start_indexing",
          security: scwordai_agent.nonce,
          post_types: postTypes,
          enable_logging: $("#wa-enable-logging").is(":checked") ? 1 : 0,
          embedding_model: getSelectedEmbeddingModel(),
      })
      .done((resp) => {
        if (myRun !== runToken) return; // stale guard

        const terminal =
          resp && (resp.job_status === "completed" || resp.job_status === "cancelled" || resp.job_status === "idle");

        if (resp && resp.status === "success") {
          // Show immediate feedback (“started for N items” or “nothing to index”)
          if (resp.feedbackMsg) showWordAIGlobalNoticeFromStatus(resp);

          if (terminal) {
            // If backend says we’re already terminal, finalize UI now
            if (resp.job_status === "completed") updateProgressVisual(100);
            return stopPolling(
              resp.feedbackMsg || "Done.",
              resp.job_status === "cancelled" ? "warning" : "success"
            );
          }

          // Polling is already running (we started it above). Nothing else to do.
        } else {
          showWordAIGlobalNotice("error", (resp && resp.feedbackMsg) || "Error starting indexing.");
          pollingActive = false;
          $startBtn.prop("disabled", false).text("Start Indexing");
        }
      })
      .fail(() => {
        if (myRun !== runToken) return; // stale guard
        showWordAIGlobalNotice("error", "Request failed.");
        pollingActive = false;
        $startBtn.prop("disabled", false).text("Start Indexing");
      });
  });

  /** ⛔ Cancel Indexing */
  $cancelBtn.on("click", function (e) {
    e.preventDefault();
    if (!confirm("Are you sure you want to cancel indexing?")) return;

    // Invalidate any in-flight polls immediately
    const myRun = ++runToken;
    pollingActive = false;

    $.post(scwordai_agent.adminajax_url, {
      action: "sc_wordai_agent_cancel_indexing",
      security: scwordai_agent.nonce,
    })
      .done((resp) => {
        // Show only the cancel message; ignore any later “completed”
        const msg = (resp && resp.feedbackMsg) ? resp.feedbackMsg : "Indexing cancelled by user.";
        showWordAIGlobalNoticeFromStatus({ job_status: "cancelled", feedbackMsg: msg });

        // Finalize UI as cancelled (do NOT fill bar)
        jobFinished = true;
        updateProgressVisual(0);
        $eta.hide();
        $label.text("Cancelled");
        if (warmupTimer) { clearInterval(warmupTimer); warmupTimer = null; }

        setTimeout(() => {
          $progress.fadeOut(300);
          $startBtn.prop("disabled", false).html('<i class="fa-solid fa-play"></i> Start Indexing');
          $estimateBtn.prop("disabled", false);
          $cancelBtn.hide();
        }, 400);
      })
      .fail(() => showWordAIGlobalNotice("error", "Request failed."));
  });




      /**
     * ===============================================================
     * Refresh Status Summary (auto-refresh failed items)
     * ===============================================================
     */
    $refreshBtn.on("click", function (e) {
      e.preventDefault();
      showWordAIGlobalNotice("info", "Refreshing status…");

      const postTypes = getSelectedPostTypes();

      $.post(scwordai_agent.adminajax_url, {
        action: "sc_wordai_agent_get_summary",
        security: scwordai_agent.nonce,
        post_types: postTypes,
      })
        .done((resp) => {
          if (resp.status === "success") {
            // ✅ Safe numeric parsing & fallback
            const totalIndexed   = Number(resp.total_indexed || 0).toLocaleString();
            const totalFailed    = Number(resp.total_failed || 0).toLocaleString();
            const totalCancelled = Number(resp.total_cancelled || 0).toLocaleString();
            const lastRun        = resp.last_run || "N/A";

            // ✅ Update summary table
            $("#wa-total-indexed").text(totalIndexed);
            $("#wa-total-failed").text(totalFailed);
            $("#wa-total-cancelled").text(totalCancelled);
            $("#wa-last-run").text(lastRun);

            showWordAIGlobalNotice("success", resp.feedbackMsg || "Summary refreshed successfully.");

            // 🔄 Fetch failed items (for modal)
            $.post(scwordai_agent.adminajax_url, {
              action: "sc_wordai_agent_get_failed_items",
              security: scwordai_agent.nonce,
              post_types: postTypes,
            }).done((failResp) => {
              if (failResp.status === "success") {
                failedItemsCache = failResp.items || [];

                // ✅ Build modal table rows
                if (failedItemsCache.length > 0) {
                  let html = "";
                  failedItemsCache.forEach((r) => {
                    const editLink = r.edit_url
                      ? `<a href="${r.edit_url}" target="_blank">${r.post_title}</a>`
                      : r.post_title;

                    html += `
                      <tr>
                        <td style="text-align:center;">${r.post_id}</td>
                        <td>${r.source_type}</td>
                        <td>${editLink}</td>
                        <td>${r.error_msg}</td>
                      </tr>`;
                  });
                  $("#wa-failed-list").html(html);

                  // ✅ Auto-open modal if failed items exist (optional)
                  // $("#wa-failed-modal").dialog("open");
                } else {
                  $("#wa-failed-list").html(
                    `<tr><td colspan="4" style="text-align:center;">No failed items found.</td></tr>`
                  );
                }
              }
            });
          } else {
            showWordAIGlobalNotice("error", resp.feedbackMsg || "Status refresh failed.");
          }
        })
        .fail(() => showWordAIGlobalNotice("error", "Request failed."));
    });


  /**
   * ===============================================================
   * Auto-refresh summary silently on first page load
   * ---------------------------------------------------------------
   * Runs once when page is first loaded, without showing notice popups.
   * Uses same AJAX logic as manual refresh, but skips UI messages.
   * ===============================================================
   */
    if ($refreshBtn.length && !window._wordaiAutoRefreshDone) {
      window._wordaiAutoRefreshDone = true; // mark to prevent repeats

      const postTypes = getSelectedPostTypes();

      // Run silently: no global notice, no animation
      jQuery.post(scwordai_agent.adminajax_url, {
        action: "sc_wordai_agent_get_summary",
        security: scwordai_agent.nonce,
        post_types: postTypes,
      }).done((resp) => {
        if (resp.status === "success") {
          // Update summary quietly
          const totalIndexed   = Number(resp.total_indexed || 0).toLocaleString();
          const totalFailed    = Number(resp.total_failed || 0).toLocaleString();
          const totalCancelled = Number(resp.total_cancelled || 0).toLocaleString();
          const lastRun        = resp.last_run || "N/A";

          jQuery("#wa-total-indexed").text(totalIndexed);
          jQuery("#wa-total-failed").text(totalFailed);
          jQuery("#wa-total-cancelled").text(totalCancelled);
          jQuery("#wa-last-run").text(lastRun);
        }
      });
    }




    /**
     * ===============================================================
     * View Failed — render 4 columns (ID | Type | Title | Error)
     * ===============================================================
     */
    $("#wa-view-failed").on("click", function (e) {
      e.preventDefault();

      // Simple HTML escaper to avoid broken markup
      const esc = (s) =>
        String(s == null ? "" : s)
          .replace(/&/g, "&amp;")
          .replace(/</g, "&lt;")
          .replace(/>/g, "&gt;")
          .replace(/"/g, "&quot;")
          .replace(/'/g, "&#039;");

          const renderFailed = (items) => {
            if (!items || !items.length) {
              $("#wa-failed-list").html(
                "<tr><td colspan='4' style='text-align:center;'>No failed items found.</td></tr>"
              );
              return;
            }

            // ✅ Helper to decode HTML entities (safe)
            const decodeHtml = (str) => $("<textarea/>").html(str || "").text();

            const rows = items
              .map((r) => {
                const id         = r.post_id ?? 0;
                const type       = r.source_type || "Unknown";
                const titlePlain = decodeHtml(r.post_title || "(Untitled)");
                const titleCell  = r.edit_url
                  ? `<a href="${r.edit_url}" target="_blank" rel="noopener noreferrer">${titlePlain}</a>`
                  : titlePlain;

                // ✅ Decode error messages too (for readable quotes, symbols, etc.)
                const errorText  = decodeHtml(r.error_msg || "-");

                return `
                  <tr>
                    <td style="text-align:center; width:60px;">${id}</td>
                    <td style="width:80px;">${type}</td>
                    <td>${titleCell}</td>
                    <td style="white-space:normal; word-break:break-word;">${errorText}</td>
                  </tr>`;
              })
              .join("");

            $("#wa-failed-list").html(rows);
          };

      // Use cache if available, otherwise fetch
      if (typeof failedItemsCache !== "undefined" && failedItemsCache.length > 0) {
        renderFailed(failedItemsCache);
      } else {
        $("#wa-failed-list").html(
          "<tr><td colspan='4' style='text-align:center;'>Loading…</td></tr>"
        );
        const postTypes = getSelectedPostTypes();
        $.post(scwordai_agent.adminajax_url, {
          action: "sc_wordai_agent_get_failed_items",
          security: scwordai_agent.nonce,
          post_types: postTypes,
        })
          .done((resp) => {
            if (resp.status === "success") {
              window.failedItemsCache = resp.items || [];
              renderFailed(window.failedItemsCache);
            } else {
              $("#wa-failed-list").html(
                "<tr><td colspan='4' style='text-align:center;'>Error loading data.</td></tr>"
              );
            }
          })
          .fail(() => {
            $("#wa-failed-list").html(
              "<tr><td colspan='4' style='text-align:center;'>Request failed.</td></tr>"
            );
          });
      }

      // Open modal (height fixed with vertical scroll)
        // $("#wa-failed-modal").dialog({
        //   modal: true,
        //   width: 760,
        //   height: 520,
        //   resizable: false,
        //   buttons: {
        //     Close: function () {
        //       $(this).dialog("close");
        //     },
        //   },
        //   open: function () {
        //     // Remove focus from any auto-focused link or button
        //     $(this).parent().find("a, button").blur();
        //   },
        // });

      // Open modal (height fixed with vertical scroll)
      $("#wa-failed-modal").dialog({
        modal: true,
        width: 760,
        height: 520,
        resizable: true,
        buttons: {
          Close: function () {
            $(this).dialog("close");
          },
        },
        open: function () {
          // Remove focus from auto-focused link or button
          $(this).parent().find("a, button").blur();

          // 🔧 Adjust table layout to fit modal width dynamically
          $("#wa-failed-list td, #wa-failed-list th").css("font-size", "12px");
          $("#wa-failed-list").closest("table").css({
            "table-layout": "auto",
            "width": "100%",
          });
        },
      });


    });


  
})(jQuery);
