(function () {
  function closest(el, selector) {
    while (el && el.nodeType === 1) {
      if (el.matches(selector)) return el;
      el = el.parentElement;
    }
    return null;
  }

  async function postAjax(action, data) {
    const form = new FormData();
    form.append('action', action);
    Object.keys(data || {}).forEach((k) => {
      form.append(k, data[k]);
    });

    const res = await fetch((window.WPSN_GSC && window.WPSN_GSC.ajaxUrl) || '', {
      method: 'POST',
      credentials: 'same-origin',
      body: form,
    });

    const json = await res.json().catch(() => null);
    if (!json) {
      throw new Error('Bad JSON response');
    }
    return json;
  }

  document.addEventListener('click', async function (e) {
    const btn = closest(e.target, '.wpsn-gsc-inspect');
    if (!btn) return;

    const liveInspect = !!(window.WPSN_GSC && window.WPSN_GSC.inspectLive);
    if (!liveInspect) {
      return;
    }

    e.preventDefault();

    const url = btn.getAttribute('data-url') || '';
    const property = btn.getAttribute('data-property') || '';
    const nonce = (window.WPSN_GSC && window.WPSN_GSC.nonce) || '';

    if (!url || !property || !nonce) {
      return;
    }

    const originalText = btn.textContent;
    btn.textContent = (window.WPSN_GSC && window.WPSN_GSC.strings && window.WPSN_GSC.strings.inspectLoading) || 'Loading…';
    btn.classList.add('disabled');
    btn.setAttribute('aria-disabled', 'true');

    try {
      const resp = await postAjax('wpsn_gsc_inspect_link', {
        nonce,
        url,
        property,
      });

      if (resp && resp.success && resp.data && resp.data.link) {
        window.open(resp.data.link, '_blank', 'noopener');
      } else {
        const msg = (resp && resp.data && (resp.data.message || resp.data)) || 'Failed';
        // eslint-disable-next-line no-alert
        alert(msg);
      }
    } catch (err) {
      // eslint-disable-next-line no-alert
      alert((window.WPSN_GSC && window.WPSN_GSC.strings && window.WPSN_GSC.strings.inspectFailed) || 'Failed');
    } finally {
      btn.textContent = originalText;
      btn.classList.remove('disabled');
      btn.removeAttribute('aria-disabled');
    }
  });

  // Opportunities: Bulk meta drafts (Generate / Apply) for selected opportunity pages
  document.addEventListener('DOMContentLoaded', function () {
    var selectAll = document.getElementById('wpsn-opp-pages-select-all');
    if (selectAll) {
      selectAll.addEventListener('change', function () {
        var cbs = document.querySelectorAll('.wpsn-opp-page-cb');
        cbs.forEach(function (cb) {
          if (cb && !cb.disabled) {
            cb.checked = !!selectAll.checked;
          }
        });
      });

      document.addEventListener('change', function (e) {
        var t = e.target;
        if (!t || !t.classList || !t.classList.contains('wpsn-opp-page-cb')) return;

        var all = document.querySelectorAll('.wpsn-opp-page-cb');
        var checked = document.querySelectorAll('.wpsn-opp-page-cb:checked');
        if (!all || !all.length) return;
        selectAll.indeterminate = checked.length > 0 && checked.length < all.length;
        if (checked.length === all.length) {
          selectAll.checked = true;
          selectAll.indeterminate = false;
        }
        if (checked.length === 0) {
          selectAll.checked = false;
          selectAll.indeterminate = false;
        }
      });
    }

    var btnLocal = document.getElementById('wpsn-opp-meta-generate-local');
    var btnOpenAi = document.getElementById('wpsn-opp-meta-generate-openai');
    var btnApply = document.getElementById('wpsn-opp-meta-apply');
    if (!btnLocal && !btnApply) return;
    if (!window.WPSN_GSC) return;

    var cancelBtn = document.getElementById('wpsn-opp-meta-cancel');
    var spinner = document.getElementById('wpsn-opp-meta-spinner');
    var wrap = document.getElementById('wpsn-opp-meta-progress');
    var bar = document.getElementById('wpsn-opp-meta-bar');
    var statusEl = document.getElementById('wpsn-opp-meta-status');

    var overwriteEl = document.getElementById('wpsn-opp-meta-overwrite');
    var applyOnlyChangedEl = document.getElementById('wpsn-opp-meta-apply-only-changed');
    var backoffEl = document.getElementById('wpsn-opp-meta-backoff');

    var openAiInitialDisabled = btnOpenAi ? !!btnOpenAi.disabled : false;

    var running = false;
    var cancelled = false;
    var session = '';
    var stage = '';
    var source = 'local';

    var total = 0;
    var processed = 0;
    var generated = 0;
    var skipped = 0;
    var failed = 0;
    var applied = 0;

    function setSpinner(active) {
      if (!spinner) return;
      if (active) spinner.classList.add('is-active');
      else spinner.classList.remove('is-active');
    }

    function showProgress(show) {
      if (!wrap) return;
      wrap.style.display = show ? 'block' : 'none';
      if (cancelBtn) cancelBtn.style.display = show ? 'inline-block' : 'none';
    }

    function setProgress(pct) {
      if (!bar) return;
      var v = Math.max(0, Math.min(100, pct));
      bar.style.width = String(v) + '%';
    }

    function setStatus(msg) {
      if (!statusEl) return;
      statusEl.textContent = msg || '';
    }

    function bulkNonce() {
      return (window.WPSN_GSC && window.WPSN_GSC.bulkNonce) ? String(window.WPSN_GSC.bulkNonce) : '';
    }

    function selectedIds() {
      var cbs = document.querySelectorAll('.wpsn-opp-page-cb:checked');
      var ids = [];
      cbs.forEach(function (cb) {
        var v = cb ? parseInt(cb.value, 10) : 0;
        if (v && v > 0) ids.push(v);
      });
      // Dedupe
      return Array.from(new Set(ids));
    }

    // Queue-first form: populate selected IDs into a hidden input (no AJAX).
    (function bindOppMetaQueueForm() {
      var form = document.getElementById('wpsn-opp-meta-queue-form');
      var input = document.getElementById('wpsn-opp-meta-post-ids');
      if (!form || !input) return;

      form.addEventListener('submit', function (e) {
        var ids = selectedIds();
        if (!ids || !ids.length) {
          e.preventDefault();
          alert('Select at least one page first.');
          return;
        }
        input.value = ids.join(',');
      });
    })();

    function disableButtons(disabled) {
      if (btnLocal) btnLocal.disabled = !!disabled;
      if (btnOpenAi) btnOpenAi.disabled = disabled ? true : openAiInitialDisabled;
      if (btnApply) btnApply.disabled = !!disabled;
    }

    function resetCounters() {
      total = 0;
      processed = 0;
      generated = 0;
      skipped = 0;
      failed = 0;
      applied = 0;
    }

    function finish(ok, message) {
      running = false;
      cancelled = false;
      session = '';
      stage = '';
      setSpinner(false);
      disableButtons(false);
      if (cancelBtn) cancelBtn.style.display = 'none';
      if (message) setStatus(message);
      // Keep progress visible so the user can see the result.
    }

    function tickRateLimit(waitSec, msg, nextFn) {
      var remaining = waitSec;
      (function tick() {
        if (cancelled || !session) {
          finish(false, window.WPSN_GSC.strings.cancelled || 'Cancelled.');
          return;
        }
        var tmsg = 'Rate limited. Waiting ' + remaining + 's…';
        if (msg) tmsg += ' ' + msg;
        setStatus(tmsg);
        if (remaining <= 0) {
          setTimeout(nextFn, 60);
          return;
        }
        remaining -= 1;
        setTimeout(tick, 1000);
      })();
    }

    function stepGenerate() {
      if (cancelled || !session) {
        finish(false, window.WPSN_GSC.strings.cancelled || 'Cancelled.');
        return;
      }

      var nonce = bulkNonce();
      var chunk = (source === 'openai') ? 1 : 10;

      postAjax('wpsn_bulk_generate_step', { nonce: nonce, session: session, chunk: chunk })
        .then(function (res) {
          if (!res || !res.success) {
            throw new Error((res && res.data && (res.data.message || res.data)) || (window.WPSN_GSC.strings.error || 'Error'));
          }

          processed = res.data.processed || 0;
          generated = res.data.generated || 0;
          skipped = res.data.skipped || 0;
          failed = res.data.failed || 0;
          total = res.data.total || total;

          var pct = total > 0 ? Math.round((processed / total) * 100) : 0;
          setProgress(pct);

          var msg = 'Processed ' + processed + ' / ' + total +
            ' • Generated ' + generated +
            ' • Skipped ' + skipped +
            ' • Failed ' + failed;
          setStatus(msg);

          if (res.data.rate_limited) {
            var waitSec = parseInt(res.data.retry_after, 10);
            if (!waitSec || waitSec < 1) waitSec = 10;
            if (waitSec > 120) waitSec = 120;
            tickRateLimit(waitSec, res.data.rate_limit_message ? String(res.data.rate_limit_message) : '', stepGenerate);
            return;
          }

          if (res.data.done) {
            finish(true, (window.WPSN_GSC.strings.done || 'Done.') + ' ' + msg);
            return;
          }

          setTimeout(stepGenerate, (source === 'openai') ? 350 : 150);
        })
        .catch(function (err) {
          finish(false, (window.WPSN_GSC.strings.error || 'Error') + ' ' + (err && err.message ? err.message : ''));
        });
    }

    function startGenerate(src) {
      if (running) return;

      var ids = selectedIds();
      if (!ids.length) {
        // eslint-disable-next-line no-alert
        alert((window.WPSN_GSC.strings && window.WPSN_GSC.strings.selectPages) || 'Select at least one page.');
        return;
      }

      var nonce = bulkNonce();
      if (!nonce) {
        // eslint-disable-next-line no-alert
        alert('Bulk nonce missing. Please reload the page.');
        return;
      }

      source = src || 'local';
      stage = 'generate';
      running = true;
      cancelled = false;
      session = '';
      resetCounters();

      disableButtons(true);
      setSpinner(true);
      showProgress(true);
      setProgress(0);
      setStatus(window.WPSN_GSC.strings.starting || 'Starting…');

      var overwrite = overwriteEl && overwriteEl.checked ? 1 : 0;
      var backoff = backoffEl ? parseInt(backoffEl.value, 10) : 10;
      if (!backoff || backoff < 1) backoff = 10;
      if (backoff > 120) backoff = 120;

      postAjax('wpsn_bulk_generate_start', {
        nonce: nonce,
        source: source,
        fields: 'title,description',
        ids: ids.join(','),
        limit: ids.length,
        only_if_empty: 0,
        overwrite_suggestions: overwrite,
        backoff_seconds: backoff,
      })
        .then(function (res) {
          if (!res || !res.success) {
            throw new Error((res && res.data && (res.data.message || res.data)) || (window.WPSN_GSC.strings.error || 'Error'));
          }

          session = res.data.session || '';
          total = res.data.total || 0;

          if (!session) {
            throw new Error('Missing session id');
          }

          if (total <= 0) {
            finish(true, 'No published posts matched the selection.');
            return;
          }

          setStatus((window.WPSN_GSC.strings.processing || 'Processing…') + ' 0 / ' + total);
          stepGenerate();
        })
        .catch(function (err) {
          finish(false, (window.WPSN_GSC.strings.error || 'Error') + ' ' + (err && err.message ? err.message : ''));
        });
    }

    function stepApply() {
      if (cancelled || !session) {
        finish(false, window.WPSN_GSC.strings.cancelled || 'Cancelled.');
        return;
      }

      var nonce = bulkNonce();
      postAjax('wpsn_bulk_apply_all_step', { nonce: nonce, session: session, chunk: 20 })
        .then(function (res) {
          if (!res || !res.success) {
            throw new Error((res && res.data && (res.data.message || res.data)) || (window.WPSN_GSC.strings.error || 'Error'));
          }

          processed = res.data.processed || 0;
          applied = res.data.applied || 0;
          total = res.data.total || total;

          var pct = total > 0 ? Math.round((processed / total) * 100) : 0;
          setProgress(pct);

          var msg = 'Processed ' + processed + ' / ' + total + ' • Applied ' + applied;
          setStatus(msg);

          if (res.data.done) {
            finish(true, (window.WPSN_GSC.strings.done || 'Done.') + ' ' + msg);
            return;
          }

          setTimeout(stepApply, 200);
        })
        .catch(function (err) {
          finish(false, (window.WPSN_GSC.strings.error || 'Error') + ' ' + (err && err.message ? err.message : ''));
        });
    }

    function startApply() {
      if (running) return;

      var ids = selectedIds();
      if (!ids.length) {
        // eslint-disable-next-line no-alert
        alert((window.WPSN_GSC.strings && window.WPSN_GSC.strings.selectPages) || 'Select at least one page.');
        return;
      }

      var nonce = bulkNonce();
      if (!nonce) {
        // eslint-disable-next-line no-alert
        alert('Bulk nonce missing. Please reload the page.');
        return;
      }

      stage = 'apply';
      running = true;
      cancelled = false;
      session = '';
      resetCounters();

      disableButtons(true);
      setSpinner(true);
      showProgress(true);
      setProgress(0);
      setStatus(window.WPSN_GSC.strings.starting || 'Starting…');

      var applyOnlyChanged = applyOnlyChangedEl && applyOnlyChangedEl.checked ? 1 : 0;

      postAjax('wpsn_bulk_apply_all_start', {
        nonce: nonce,
        ids: ids.join(','),
        limit: ids.length,
        apply_only_changed: applyOnlyChanged,
      })
        .then(function (res) {
          if (!res || !res.success) {
            throw new Error((res && res.data && (res.data.message || res.data)) || (window.WPSN_GSC.strings.error || 'Error'));
          }

          session = res.data.session || '';
          total = res.data.total || 0;
          if (!session) {
            throw new Error('Missing session id');
          }

          if (total <= 0) {
            finish(true, 'No draft suggestions found for the selected pages.');
            return;
          }

          setStatus((window.WPSN_GSC.strings.processing || 'Processing…') + ' 0 / ' + total);
          stepApply();
        })
        .catch(function (err) {
          finish(false, (window.WPSN_GSC.strings.error || 'Error') + ' ' + (err && err.message ? err.message : ''));
        });
    }

    function cancel() {
      if (!running || !session) {
        finish(false, window.WPSN_GSC.strings.cancelled || 'Cancelled.');
        showProgress(false);
        return;
      }

      cancelled = true;
      setStatus(window.WPSN_GSC.strings.cancelled || 'Cancelled.');

      var nonce = bulkNonce();
      var action = (stage === 'apply') ? 'wpsn_bulk_apply_all_cancel' : 'wpsn_bulk_generate_cancel';
      postAjax(action, { nonce: nonce, session: session })
        .then(function () {
          finish(false, window.WPSN_GSC.strings.cancelled || 'Cancelled.');
          showProgress(false);
        })
        .catch(function () {
          finish(false, window.WPSN_GSC.strings.cancelled || 'Cancelled.');
          showProgress(false);
        });
    }

    if (btnLocal) {
      btnLocal.addEventListener('click', function (e) {
        e.preventDefault();
        startGenerate('local');
      });
    }
    if (btnOpenAi) {
      btnOpenAi.addEventListener('click', function (e) {
        e.preventDefault();
        if (btnOpenAi.disabled) return;
        startGenerate('openai');
      });
    }
    if (btnApply) {
      btnApply.addEventListener('click', function (e) {
        e.preventDefault();
        startApply();
      });
    }
    if (cancelBtn) {
      cancelBtn.addEventListener('click', function (e) {
        e.preventDefault();
        cancel();
      });
    }
  });

  // ===== Clusters: bulk generate internal link drafts across all clusters =====

  document.addEventListener('DOMContentLoaded', function () {
    var running = false;
    var current = null;

    function getWrap(el) {
      if (!el) return null;
      var card = el.closest('.wpsn-card');
      if (!card) return null;
      return card;
    }

    function setProgress(card, pct) {
      if (!card) return;
      var bar = card.querySelector('.wpsn-cluster-bulk-progress .wpsn-progress-bar');
      if (bar) bar.style.width = String(pct || 0) + '%';
    }

    function show(card, on) {
      if (!card) return;
      var p = card.querySelector('.wpsn-cluster-bulk-progress');
      if (p) p.style.display = on ? '' : 'none';
    }

    function setStatus(card, msg) {
      if (!card) return;
      var el = card.querySelector('.wpsn-cluster-bulk-status');
      if (el) el.textContent = String(msg || '');
    }

    function setSummary(card, msg) {
      if (!card) return;
      var el = card.querySelector('.wpsn-cluster-bulk-summary');
      if (el) el.textContent = String(msg || '');
    }

    function setCancel(card, on) {
      if (!card) return;
      var b = card.querySelector('.wpsn-cluster-bulk-cancel');
      if (b) b.style.display = on ? '' : 'none';
    }

    function setOpenDrafts(card, url) {
      if (!card) return;
      var a = card.querySelector('.wpsn-cluster-bulk-open-drafts');
      if (!a) return;
      if (url) {
        a.href = String(url);
        a.style.display = '';
      } else {
        a.style.display = 'none';
      }
    }

    function disableAll(on) {
      var btn = document.querySelector('.wpsn-cluster-bulk-start');
      if (btn) btn.disabled = !!on;
      // Also prevent running cluster generators in parallel.
      var btns = document.querySelectorAll('.wpsn-cluster-generate');
      btns.forEach(function (b) {
        b.disabled = !!on;
      });
    }

    function getInt(selector, fallback, min, max) {
      var el = document.querySelector(selector);
      var v = el ? parseInt(el.value || String(fallback || 0), 10) : parseInt(String(fallback || 0), 10);
      if (isNaN(v)) v = fallback || 0;
      if (typeof min === 'number' && v < min) v = min;
      if (typeof max === 'number' && v > max) v = max;
      return v;
    }

    function getFloat(selector, fallback, min, max) {
      var el = document.querySelector(selector);
      var v = el ? parseFloat(el.value || String(fallback || 0)) : parseFloat(String(fallback || 0));
      if (isNaN(v)) v = fallback || 0;
      if (typeof min === 'number' && v < min) v = min;
      if (typeof max === 'number' && v > max) v = max;
      return v;
    }

    function getChecked(selector) {
      var el = document.querySelector(selector);
      return el && el.checked ? 1 : 0;
    }

    function step() {
      if (!current || !current.session) return;
      var nonce = window.WPSN_GSC && window.WPSN_GSC.nonce;
      postAjax('wpsn_il_cluster_bulk_step', { nonce: nonce, session: current.session, chunk: current.chunk })
        .then(function (res) {
          if (!res || !res.success) {
            var m = (res && res.data && (res.data.message || res.data)) || (window.WPSN_GSC.strings.error || 'Error');
            throw new Error(m);
          }
          var d = res.data || {};
          var processed = parseInt(d.processed || 0, 10);
          var total = parseInt(d.total || 0, 10);
          var gen = parseInt(d.generated || 0, 10);
          var skip = parseInt(d.skipped || 0, 10);
          var fail = parseInt(d.failed || 0, 10);
          var pct = total > 0 ? Math.floor((processed / total) * 100) : 0;
          setProgress(current.card, pct);

          var msg = d.message ? String(d.message) : '';
          if (!msg) {
            msg = (window.WPSN_GSC.strings.processing || 'Processing') + ': ' + processed + '/' + total + ' · drafts ' + gen + ' · skipped ' + skip + (fail ? (' · failed ' + fail) : '');
          }
          setStatus(current.card, msg);

          // Skip breakdown.
          var parts = [];
          var n0 = parseInt(d.skipped_not_zero_impr || 0, 10);
          var nf = parseInt(d.skipped_forbidden || 0, 10);
          var nm = parseInt(d.skipped_no_post || 0, 10);
          var ng = parseInt(d.skipped_no_gsc || 0, 10);
          var na = parseInt(d.skipped_already_linked || 0, 10);
          var no = parseInt(d.skipped_no_overlap || 0, 10);
          var nx = parseInt(d.skipped_no_anchor || 0, 10);
          if (n0) parts.push('not 0-impr: ' + n0);
          if (nf) parts.push('no permission: ' + nf);
          if (nm) parts.push('missing post: ' + nm);
          if (ng) parts.push('no GSC match: ' + ng);
          if (na) parts.push('already linked: ' + na);
          if (no) parts.push('no shared queries: ' + no);
          if (nx) parts.push('no anchor in content: ' + nx);
          setSummary(current.card, parts.length ? ('Skip breakdown: ' + parts.join(' · ')) : '');

          if (d.drafts_url) {
            current.draftsUrl = String(d.drafts_url);
          }

          if (d.done) {
            setProgress(current.card, 100);
            setStatus(current.card, (window.WPSN_GSC.strings.done || 'Done') + ': drafts ' + gen + ' · skipped ' + skip + (fail ? (' · failed ' + fail) : ''));
            setCancel(current.card, false);
            setOpenDrafts(current.card, gen > 0 ? current.draftsUrl : null);
            running = false;
            disableAll(false);
            current = null;
            return;
          }

          setTimeout(step, 200);
        })
        .catch(function (err) {
          setStatus(current.card, (window.WPSN_GSC.strings.error || 'Error') + ': ' + (err && err.message ? err.message : ''));
          setCancel(current.card, false);
          running = false;
          disableAll(false);
          current = null;
        });
    }

    document.addEventListener('click', function (e) {
      var btn = e.target && e.target.closest ? e.target.closest('.wpsn-cluster-bulk-start') : null;
      if (!btn) return;
      e.preventDefault();

      if (running) return;

      var card = getWrap(btn);
      if (!card) return;

      // Read Parameters form inputs (works even if user changes them without reloading).
      var maxPages = getInt('input[name="max_pages"]', 200, 20, 5000);
      var topQ = getInt('input[name="top_q"]', 20, 3, 200);
      var minImpr = getInt('input[name="min_impr"]', 10, 0, 1000000);
      var maxPq = getInt('input[name="max_pq"]', 25, 2, 200);
      var minShared = getInt('input[name="min_shared"]', 3, 1, 50);
      var sim = getFloat('input[name="sim"]', 0.4, 0.05, 1.0);
      var minCluster = getInt('input[name="min_cluster"]', 3, 2, 100);
      var wpOnly = getChecked('input[name="wp_only"]');
      var preferWp = getChecked('input[name="prefer_wp"]');

      var limitClustersEl = card.querySelector('.wpsn-cluster-bulk-limit');
      var limitClusters = limitClustersEl ? parseInt(limitClustersEl.value || '10', 10) : 10;
      if (!limitClusters || limitClusters < 1) limitClusters = 10;
      limitClusters = Math.max(1, Math.min(500, limitClusters));

      var maxQEl = card.querySelector('.wpsn-cluster-bulk-max-q');
      var maxQ = maxQEl ? parseInt(maxQEl.value || '5', 10) : 5;
      if (!maxQ || maxQ < 1) maxQ = 5;
      maxQ = Math.max(1, Math.min(20, maxQ));

      var onlyZeroEl = card.querySelector('.wpsn-cluster-bulk-only-zero');
      var onlyZero = onlyZeroEl && onlyZeroEl.checked ? 1 : 0;

      running = true;
      disableAll(true);
      show(card, true);
      setCancel(card, true);
      setOpenDrafts(card, null);
      setProgress(card, 0);
      setStatus(card, window.WPSN_GSC.strings.starting || 'Starting…');
      setSummary(card, '');

      var nonce = window.WPSN_GSC && window.WPSN_GSC.nonce;
      postAjax('wpsn_il_cluster_bulk_start', {
        nonce: nonce,
        max_pages: maxPages,
        top_q: topQ,
        min_impr: minImpr,
        max_pq: maxPq,
        min_shared: minShared,
        sim: sim,
        min_cluster: minCluster,
        wp_only: wpOnly,
        prefer_wp: preferWp,
        limit_clusters: limitClusters,
        max_q: maxQ,
        only_zero_impr: onlyZero,
        chunk: 3
      })
        .then(function (res) {
          if (!res || !res.success) {
            var m = (res && res.data && (res.data.message || res.data)) || (window.WPSN_GSC.strings.error || 'Error');
            throw new Error(m);
          }
          var d = res.data || {};
          current = {
            session: d.session,
            card: card,
            draftsUrl: d.drafts_url || '',
            chunk: parseInt(d.chunk || 3, 10) || 3
          };
          var total = parseInt(d.total || 0, 10);
          var clusters = parseInt(d.clusters || 0, 10);
          setStatus(card, (window.WPSN_GSC.strings.processing || 'Processing') + ': 0/' + total + (clusters ? (' · clusters ' + clusters) : ''));
          if (d.skipped_not_zero_impr) {
            setSummary(card, 'Filtered (not 0-impr): ' + String(d.skipped_not_zero_impr));
          }
          step();
        })
        .catch(function (err) {
          setStatus(card, (window.WPSN_GSC.strings.error || 'Error') + ': ' + (err && err.message ? err.message : ''));
          setCancel(card, false);
          running = false;
          disableAll(false);
          current = null;
        });
    });

    document.addEventListener('click', function (e) {
      var btn = e.target && e.target.closest ? e.target.closest('.wpsn-cluster-bulk-cancel') : null;
      if (!btn) return;
      e.preventDefault();
      if (!current || !current.session || !running) return;
      var card = current.card;
      setStatus(card, window.WPSN_GSC.strings.canceling || 'Canceling…');
      var nonce = window.WPSN_GSC && window.WPSN_GSC.nonce;
      postAjax('wpsn_il_cluster_bulk_cancel', { nonce: nonce, session: current.session })
        .then(function () {
          setStatus(card, window.WPSN_GSC.strings.canceled || 'Canceled');
        })
        .finally(function () {
          setCancel(card, false);
          running = false;
          disableAll(false);
          current = null;
        });
    });
  });

  // Cannibalization: Bulk workout (preview/apply) with progress
  document.addEventListener('DOMContentLoaded', function () {
    var btnPreview = document.getElementById('wpsn-cannibal-bulk-preview');
    var btnApply = document.getElementById('wpsn-cannibal-bulk-apply');
    var btnDry = document.getElementById('wpsn-cannibal-bulk-dry');
    if (!btnPreview && !btnApply && !btnDry) return;
    if (!window.WPSN_GSC) return;

    var selectAll = document.getElementById('wpsn-cannibal-select-all');
    if (selectAll) {
      selectAll.addEventListener('change', function () {
        var cbs = document.querySelectorAll('.wpsn-cannibal-cb');
        cbs.forEach(function (cb) {
          if (cb && !cb.disabled) cb.checked = !!selectAll.checked;
        });
      });

      document.addEventListener('change', function (e) {
        var t = e.target;
        if (!t || !t.classList || !t.classList.contains('wpsn-cannibal-cb')) return;
        var all = document.querySelectorAll('.wpsn-cannibal-cb');
        var checked = document.querySelectorAll('.wpsn-cannibal-cb:checked');
        if (!all || !all.length) return;
        selectAll.indeterminate = checked.length > 0 && checked.length < all.length;
        if (checked.length === all.length) {
          selectAll.checked = true;
          selectAll.indeterminate = false;
        }
        if (checked.length === 0) {
          selectAll.checked = false;
          selectAll.indeterminate = false;
        }
      });
    }

    var cancelBtn = document.getElementById('wpsn-cannibal-bulk-cancel');
    var spinner = document.getElementById('wpsn-cannibal-bulk-spinner');
    var wrap = document.getElementById('wpsn-cannibal-bulk-progress');
    var bar = document.getElementById('wpsn-cannibal-bulk-bar');
    var statusEl = document.getElementById('wpsn-cannibal-bulk-status');
    var resultsWrap = document.getElementById('wpsn-cannibal-bulk-results');
    var resultsBody = document.getElementById('wpsn-cannibal-bulk-results-tbody');
    var exportBtn = document.getElementById('wpsn-cannibal-bulk-export');

    var strategyEl = document.getElementById('wpsn-cannibal-primary-strategy');
    var maxPagesEl = document.getElementById('wpsn-cannibal-max-pages');
    var limitQueriesEl = document.getElementById('wpsn-cannibal-limit-queries');

    var doCanonEl = document.getElementById('wpsn-cannibal-do-canonical');
    var doFocusEl = document.getElementById('wpsn-cannibal-do-focus');
    var owCanonEl = document.getElementById('wpsn-cannibal-ow-canonical');
    var owFocusEl = document.getElementById('wpsn-cannibal-ow-focus');
    var strictWpEl = document.getElementById('wpsn-cannibal-strict-wp');

    var running = false;
    var cancelled = false;
    var session = '';
    var mode = 'preview';
    var total = 0;
    var processed = 0;
    var canonicalApplied = 0;
    var focusApplied = 0;
    var errors = 0;

    function setSpinner(active) {
      if (!spinner) return;
      if (active) spinner.classList.add('is-active');
      else spinner.classList.remove('is-active');
    }

    function showProgress(show) {
      if (!wrap) return;
      wrap.style.display = show ? 'block' : 'none';
      if (cancelBtn) cancelBtn.style.display = show ? 'inline-block' : 'none';
    }

    function setProgress(pct) {
      if (!bar) return;
      var v = Math.max(0, Math.min(100, pct));
      bar.style.width = String(v) + '%';
    }

    function setStatus(msg) {
      if (!statusEl) return;
      statusEl.textContent = msg || '';
    }

    function disableButtons(disabled) {
      if (btnPreview) btnPreview.disabled = !!disabled;
      if (btnApply) btnApply.disabled = !!disabled;
      if (btnDry) btnDry.disabled = !!disabled;
    }

    function selectedTokens() {
      var cbs = document.querySelectorAll('.wpsn-cannibal-cb:checked');
      var list = [];
      cbs.forEach(function (cb) {
        var v = cb ? String(cb.value || '') : '';
        if (v) list.push(v);
      });
      return Array.from(new Set(list));
    }

    function clearResults() {
      if (resultsBody) resultsBody.innerHTML = '';
      if (resultsWrap) resultsWrap.style.display = 'none';
    }

    function ensureResultsVisible() {
      if (resultsWrap) resultsWrap.style.display = 'block';
    }

    function appendItem(item) {
      if (!resultsBody || !item) return;
      ensureResultsVisible();

      var itemMode = String(item.mode || mode || 'preview');

      var tr = document.createElement('tr');

      var tdQ = document.createElement('td');
      var qCode = document.createElement('code');
      qCode.textContent = item.query || '';
      tdQ.appendChild(qCode);
      tr.appendChild(tdQ);

      var tdP = document.createElement('td');
      if (item.primary_url) {
        var pCode = document.createElement('code');
        pCode.textContent = item.primary_url;
        tdP.appendChild(pCode);
      } else {
        tdP.textContent = '—';
      }
      tr.appendChild(tdP);

      var tdC = document.createElement('td');
      if (item.error) {
        tdC.textContent = 'ERROR';
      } else if (item.skipped) {
        tdC.textContent = 'SKIP';
      } else if (itemMode === 'apply') {
        tdC.textContent = 'Applied: ' + String(item.canonical_applied || 0);
      } else if (itemMode === 'dry_run') {
        tdC.textContent = 'Would: ' + String(item.canonical_would_apply || 0);
      } else {
        tdC.textContent = 'Change: ' + String(item.canonical_change || 0) + ' / Noop: ' + String(item.canonical_noop || 0) + ' / Skip: ' + String(item.canonical_skip || 0);
      }
      tr.appendChild(tdC);

      var tdF = document.createElement('td');
      if (item.error) {
        tdF.textContent = String(item.error);
      } else if (item.skipped) {
        tdF.textContent = 'SKIP' + (item.skip_reason ? ' — ' + String(item.skip_reason) : '');
      } else if (itemMode === 'apply') {
        tdF.textContent = (item.focus_applied && item.focus_applied > 0) ? 'Applied' : '—';
      } else if (itemMode === 'dry_run') {
        tdF.textContent = (item.focus_would_apply && item.focus_would_apply > 0) ? 'WOULD SET' : '—';
      } else {
        var st = String(item.focus_status || 'skip').toUpperCase();
        tdF.textContent = st;
      }
      tr.appendChild(tdF);

      var tdA = document.createElement('td');
      if (item.workout_url) {
        var a1 = document.createElement('a');
        a1.href = item.workout_url;
        a1.className = 'button button-small';
        a1.textContent = 'Workout';
        tdA.appendChild(a1);
        tdA.appendChild(document.createTextNode(' '));
      }
      if (item.gsc_url) {
        var a2 = document.createElement('a');
        a2.href = item.gsc_url;
        a2.className = 'button button-small';
        a2.target = '_blank';
        a2.rel = 'noopener';
        a2.textContent = 'GSC';
        tdA.appendChild(a2);
      }
      tr.appendChild(tdA);

      resultsBody.appendChild(tr);
    }

    function start(runMode) {
      if (running) return;

      var tokens = selectedTokens();
      if (!tokens.length) {
        // eslint-disable-next-line no-alert
        alert((window.WPSN_GSC.strings && window.WPSN_GSC.strings.selectQueries) || 'Select at least one query first.');
        return;
      }

      mode = runMode === 'apply' ? 'apply' : (runMode === 'dry' ? 'dry_run' : 'preview');
      var serverMode = mode === 'apply' ? 'apply' : 'preview';
      var dryRun = mode === 'dry_run';
      cancelled = false;
      session = '';
      total = 0;
      processed = 0;
      canonicalApplied = 0;
      focusApplied = 0;
      errors = 0;

      clearResults();
      setProgress(0);

      if (exportBtn) {
        exportBtn.style.display = 'none';
        exportBtn.href = '#';
      }

      var nonce = window.WPSN_GSC && window.WPSN_GSC.nonce;
      var primaryStrategy = strategyEl ? String(strategyEl.value || 'clicks') : 'clicks';
      var maxPages = maxPagesEl ? parseInt(maxPagesEl.value, 10) : 10;
      var limitQueries = limitQueriesEl ? parseInt(limitQueriesEl.value, 10) : 20;
      var doCanonical = doCanonEl ? !!doCanonEl.checked : true;
      var doFocus = doFocusEl ? !!doFocusEl.checked : true;
      var overwriteCanonical = owCanonEl ? !!owCanonEl.checked : false;
      var overwriteFocus = owFocusEl ? !!owFocusEl.checked : false;
      var strictWp = strictWpEl ? !!strictWpEl.checked : false;

      running = true;
      disableButtons(true);
      setSpinner(true);
      showProgress(true);
      setStatus((window.WPSN_GSC.strings && window.WPSN_GSC.strings.starting) || 'Starting…');

      postAjax('wpsn_gsc_cannibal_bulk_start', {
        nonce: nonce,
        mode: serverMode,
        queries: JSON.stringify(tokens),
        primary_strategy: primaryStrategy,
        max_pages: String(isNaN(maxPages) ? 10 : maxPages),
        limit_queries: String(isNaN(limitQueries) ? 20 : limitQueries),
        do_canonical: doCanonical ? '1' : '',
        do_focus: doFocus ? '1' : '',
        overwrite_canonical: overwriteCanonical ? '1' : '',
        overwrite_focus: overwriteFocus ? '1' : '',
        dry_run: dryRun ? '1' : '',
        strict_wp_primary: strictWp ? '1' : '',
      })
        .then(function (res) {
          if (!res || !res.success) {
            throw new Error((res && res.data && (res.data.message || res.data)) || 'Error');
          }
          session = res.data.session;
          total = res.data.total || tokens.length;
          processed = 0;
          setStatus((window.WPSN_GSC.strings && window.WPSN_GSC.strings.processing) || 'Processing…');
          step();
        })
        .catch(function (err) {
          running = false;
          disableButtons(false);
          setSpinner(false);
          showProgress(false);
          setStatus((window.WPSN_GSC.strings && window.WPSN_GSC.strings.error) || 'Error');
          // eslint-disable-next-line no-alert
          alert(err && err.message ? err.message : 'Error');
        });
    }

    function step() {
      if (cancelled || !session) {
        running = false;
        disableButtons(false);
        setSpinner(false);
        showProgress(false);
        setStatus((window.WPSN_GSC.strings && window.WPSN_GSC.strings.cancelled) || 'Cancelled.');
        return;
      }

      var nonce = window.WPSN_GSC && window.WPSN_GSC.nonce;
      postAjax('wpsn_gsc_cannibal_bulk_step', { nonce: nonce, session: session, chunk: 1 })
        .then(function (res) {
          if (!res || !res.success) {
            throw new Error((res && res.data && (res.data.message || res.data)) || 'Error');
          }

          processed = res.data.processed || processed;
          total = res.data.total || total;
          canonicalApplied = res.data.canonical_applied || canonicalApplied;
          focusApplied = res.data.focus_applied || focusApplied;
          errors = res.data.errors || errors;

          if (res.data.item) {
            appendItem(res.data.item);
          }

          var pct = total > 0 ? Math.round((processed / total) * 100) : 0;
          setProgress(pct);

          var msg = 'Processed ' + String(processed) + '/' + String(total);
          if (mode === 'apply') {
            msg += ' — Canonicals: ' + String(canonicalApplied) + ', Focus: ' + String(focusApplied);
          }
          if (errors > 0) {
            msg += ' — Errors: ' + String(errors);
          }
          setStatus(msg);

          if (res.data.done) {
            running = false;
            disableButtons(false);
            setSpinner(false);
            if (cancelBtn) cancelBtn.style.display = 'none';
            if (exportBtn && window.WPSN_GSC && window.WPSN_GSC.adminPostUrl && session) {
              exportBtn.href = String(window.WPSN_GSC.adminPostUrl) + '?action=wpsn_gsc_cannibal_bulk_export&session=' + encodeURIComponent(session) + '&_wpnonce=' + encodeURIComponent(nonce);
              exportBtn.style.display = 'inline-block';
            }
            setStatus(((window.WPSN_GSC.strings && window.WPSN_GSC.strings.done) || 'Done.') + ' ' + msg);
            return;
          }

          setTimeout(step, 80);
        })
        .catch(function (err) {
          running = false;
          disableButtons(false);
          setSpinner(false);
          showProgress(false);
          setStatus((window.WPSN_GSC.strings && window.WPSN_GSC.strings.error) || 'Error');
          // eslint-disable-next-line no-alert
          alert(err && err.message ? err.message : 'Error');
        });
    }

    function cancel() {
      if (!running || !session) {
        running = false;
        disableButtons(false);
        setSpinner(false);
        showProgress(false);
        return;
      }

      cancelled = true;
      setStatus((window.WPSN_GSC.strings && window.WPSN_GSC.strings.cancelled) || 'Cancelled.');

      var nonce = window.WPSN_GSC && window.WPSN_GSC.nonce;
      postAjax('wpsn_gsc_cannibal_bulk_cancel', { nonce: nonce, session: session })
        .then(function () {
          running = false;
          disableButtons(false);
          setSpinner(false);
          showProgress(false);
        })
        .catch(function () {
          running = false;
          disableButtons(false);
          setSpinner(false);
          showProgress(false);
        });
    }

    if (btnPreview) {
      btnPreview.addEventListener('click', function (e) {
        e.preventDefault();
        start('preview');
      });
    }

    if (btnDry) {
      btnDry.addEventListener('click', function (e) {
        e.preventDefault();
        start('dry');
      });
    }

    if (btnApply) {
      btnApply.addEventListener('click', function (e) {
        e.preventDefault();
        // eslint-disable-next-line no-alert
        if (!confirm('Apply canonicals/focus keyword for the selected queries now?')) return;
        start('apply');
      });
    }

    if (cancelBtn) {
      cancelBtn.addEventListener('click', function (e) {
        e.preventDefault();
        cancel();
      });
    }
  });

  // Internal links: Apply all drafts now (AJAX) with progress
  document.addEventListener('DOMContentLoaded', function () {
    var btn = document.getElementById('wpsn-il-ajax-apply-all');
    if (!btn || !window.WPSN_GSC) return;

    var cancelBtn = document.getElementById('wpsn-il-ajax-cancel');
    var spinner = document.getElementById('wpsn-il-ajax-spinner');
    var wrap = document.getElementById('wpsn-il-ajax-progress');
    var bar = document.getElementById('wpsn-il-ajax-bar');
    var statusEl = document.getElementById('wpsn-il-ajax-status');

    var running = false;
    var cancelled = false;
    var session = '';
    var total = 0;
    var processed = 0;
    var applied = 0;
    var failed = 0;

    function setSpinner(active) {
      if (!spinner) return;
      if (active) spinner.classList.add('is-active');
      else spinner.classList.remove('is-active');
    }

    function setProgress(pct) {
      if (!bar) return;
      var v = Math.max(0, Math.min(100, pct));
      bar.style.width = String(v) + '%';
    }

    function setStatus(msg) {
      if (!statusEl) return;
      statusEl.textContent = msg || '';
    }

    function showProgress(show) {
      if (!wrap) return;
      wrap.style.display = show ? 'block' : 'none';
      if (cancelBtn) {
        cancelBtn.style.display = show ? 'inline-block' : 'none';
      }
    }

    function reset() {
      running = false;
      cancelled = false;
      session = '';
      total = 0;
      processed = 0;
      applied = 0;
      failed = 0;
      btn.disabled = false;
      setSpinner(false);
    }

    function getFiltersFromUrl() {
      var p = new URLSearchParams(window.location.search || '');
      return {
        target_id: p.get('target_id') || '',
        source_id: p.get('source_id') || '',
        min_impr: p.get('min_impr') || '',
        max_pos: p.get('max_pos') || '',
        min_conf: p.get('min_conf') || '',
      };
    }

    function finish(ok, message) {
      running = false;
      setSpinner(false);
      if (message) setStatus(message);

      if (ok) {
        setTimeout(function () {
          window.location.reload();
        }, 800);
      }
    }

    function step() {
      if (cancelled || !session) {
        reset();
        showProgress(false);
        return;
      }

      var nonce = window.WPSN_GSC.nonce || '';
      postAjax('wpsn_il_apply_all_step', { nonce: nonce, session: session, chunk: 5 })
        .then(function (res) {
          if (!res || !res.success) {
            throw new Error((res && res.data && (res.data.message || res.data)) || (window.WPSN_GSC.strings.error || 'Error'));
          }

          processed = res.data.processed || 0;
          applied = res.data.applied || 0;
          failed = res.data.failed || 0;
          total = res.data.total || total;

          var pct = total > 0 ? Math.round((processed / total) * 100) : 0;
          setProgress(pct);

          var msg = 'Processed ' + processed + ' / ' + total + ' • Applied ' + applied;
          if (failed) msg += ' • Failed ' + failed;
          if (res.data.remaining !== undefined) msg += ' • Remaining ' + res.data.remaining;
          setStatus(msg);

          if (res.data.done) {
            finish(true, (window.WPSN_GSC.strings.done || 'Done.') + ' ' + msg);
            return;
          }

          setTimeout(step, 200);
        })
        .catch(function (err) {
          finish(false, (window.WPSN_GSC.strings.error || 'Error') + ' ' + (err && err.message ? err.message : ''));
        });
    }

    function start() {
      if (running) return;
      running = true;
      cancelled = false;

      var nonce = window.WPSN_GSC.nonce || '';
      var limitInput = document.getElementById('wpsn-il-apply-limit');
      var limit = limitInput ? parseInt(limitInput.value, 10) : 500;
      if (!limit || limit < 1) limit = 500;

      btn.disabled = true;
      setSpinner(true);
      showProgress(true);
      setProgress(0);
      setStatus((window.WPSN_GSC.strings.starting || 'Starting…'));

      var filters = getFiltersFromUrl();
      postAjax('wpsn_il_apply_all_start', Object.assign({ nonce: nonce, limit: limit }, filters))
        .then(function (res) {
          if (!res || !res.success) {
            throw new Error((res && res.data && (res.data.message || res.data)) || (window.WPSN_GSC.strings.error || 'Error'));
          }

          session = res.data.session || '';
          total = res.data.total || 0;
          if (!session) throw new Error('Missing session id');

          if (total <= 0) {
            finish(true, 'No draft link suggestions found for the current filters.');
            return;
          }

          setStatus((window.WPSN_GSC.strings.processing || 'Processing…') + ' 0 / ' + total);
          step();
        })
        .catch(function (err) {
          finish(false, (window.WPSN_GSC.strings.error || 'Error') + ' ' + (err && err.message ? err.message : ''));
        });
    }

    function cancel() {
      if (!running || !session) {
        reset();
        showProgress(false);
        return;
      }

      cancelled = true;
      setStatus(window.WPSN_GSC.strings.cancelled || 'Cancelled.');

      var nonce = window.WPSN_GSC.nonce || '';
      postAjax('wpsn_il_apply_all_cancel', { nonce: nonce, session: session })
        .then(function () {
          reset();
          showProgress(false);
        })
        .catch(function () {
          reset();
          showProgress(false);
        });
    }

    btn.addEventListener('click', function (e) {
      e.preventDefault();
      start();
    });

    if (cancelBtn) {
      cancelBtn.addEventListener('click', function (e) {
        e.preventDefault();
        cancel();
      });
    }
  });

  // Orphan pages: Compute inbound link counts (AJAX) with progress
  document.addEventListener('DOMContentLoaded', function () {
    var btn = document.getElementById('wpsn-il-inlinks-ajax-start');
    if (!btn || !window.WPSN_GSC) return;

    var cancelBtn = document.getElementById('wpsn-il-inlinks-ajax-cancel');
    var spinner = document.getElementById('wpsn-il-inlinks-ajax-spinner');
    var wrap = document.getElementById('wpsn-il-inlinks-ajax-progress');
    var bar = document.getElementById('wpsn-il-inlinks-ajax-bar');
    var statusEl = document.getElementById('wpsn-il-inlinks-ajax-status');

    var running = false;
    var cancelled = false;
    var session = '';
    var total = 0;
    var processed = 0;
    var updated = 0;

    function setSpinner(active) {
      if (!spinner) return;
      if (active) spinner.classList.add('is-active');
      else spinner.classList.remove('is-active');
    }

    function setProgress(pct) {
      if (!bar) return;
      var v = Math.max(0, Math.min(100, pct));
      bar.style.width = String(v) + '%';
    }

    function setStatus(msg) {
      if (!statusEl) return;
      statusEl.textContent = msg || '';
    }

    function showProgress(show) {
      if (!wrap) return;
      wrap.style.display = show ? 'block' : 'none';
      if (cancelBtn) {
        cancelBtn.style.display = show ? 'inline-block' : 'none';
      }
    }

    function reset() {
      running = false;
      cancelled = false;
      session = '';
      total = 0;
      processed = 0;
      updated = 0;
      btn.disabled = false;
      setSpinner(false);
    }

    function collectPostTypes() {
      var els = document.querySelectorAll('.wpsn-il-inlinks-pt');
      var out = [];
      els.forEach(function (el) {
        if (el && el.checked) {
          out.push(el.value);
        }
      });
      return out;
    }

    function finish(ok, message) {
      running = false;
      setSpinner(false);
      if (message) setStatus(message);

      if (ok) {
        setTimeout(function () {
          window.location.reload();
        }, 800);
      }
    }

    function step() {
      if (cancelled || !session) {
        reset();
        showProgress(false);
        return;
      }

      var nonce = window.WPSN_GSC.nonce || '';
      postAjax('wpsn_il_inlinks_step', { nonce: nonce, session: session, chunk: 5 })
        .then(function (res) {
          if (!res || !res.success) {
            throw new Error((res && res.data && (res.data.message || res.data)) || (window.WPSN_GSC.strings.error || 'Error'));
          }

          processed = res.data.processed || 0;
          updated = res.data.updated || 0;
          total = res.data.total || total;

          var pct = total > 0 ? Math.round((processed / total) * 100) : 0;
          setProgress(pct);

          var msg = 'Processed ' + processed + ' / ' + total + ' • Updated ' + updated;
          if (res.data.remaining !== undefined) msg += ' • Remaining ' + res.data.remaining;
          setStatus(msg);

          if (res.data.done) {
            finish(true, (window.WPSN_GSC.strings.done || 'Done.') + ' ' + msg);
            return;
          }

          setTimeout(step, 200);
        })
        .catch(function (err) {
          finish(false, (window.WPSN_GSC.strings.error || 'Error') + ' ' + (err && err.message ? err.message : ''));
        });
    }

    function start() {
      if (running) return;
      running = true;
      cancelled = false;

      var nonce = window.WPSN_GSC.nonce || '';
      var limitInput = document.getElementById('wpsn-il-inlinks-limit');
      var limit = limitInput ? parseInt(limitInput.value, 10) : 5000;
      if (!limit || limit < 1) limit = 5000;

      var onlyMissingEl = document.getElementById('wpsn-il-inlinks-only-missing');
      var onlyMissing = onlyMissingEl && onlyMissingEl.checked ? 1 : 0;
      var pts = collectPostTypes();

      btn.disabled = true;
      setSpinner(true);
      showProgress(true);
      setProgress(0);
      setStatus((window.WPSN_GSC.strings.starting || 'Starting…'));

      postAjax('wpsn_il_inlinks_start', {
        nonce: nonce,
        limit: limit,
        only_missing: onlyMissing,
        post_types: pts.join(','),
      })
        .then(function (res) {
          if (!res || !res.success) {
            throw new Error((res && res.data && (res.data.message || res.data)) || (window.WPSN_GSC.strings.error || 'Error'));
          }

          session = res.data.session || '';
          total = res.data.total || 0;
          if (!session) throw new Error('Missing session id');

          if (total <= 0) {
            finish(true, 'No posts matched the compute criteria.');
            return;
          }

          setStatus((window.WPSN_GSC.strings.processing || 'Processing…') + ' 0 / ' + total);
          step();
        })
        .catch(function (err) {
          finish(false, (window.WPSN_GSC.strings.error || 'Error') + ' ' + (err && err.message ? err.message : ''));
        });
    }

    function cancel() {
      if (!running || !session) {
        reset();
        showProgress(false);
        return;
      }

      cancelled = true;
      setStatus(window.WPSN_GSC.strings.cancelled || 'Cancelled.');

      var nonce = window.WPSN_GSC.nonce || '';
      postAjax('wpsn_il_inlinks_cancel', { nonce: nonce, session: session })
        .then(function () {
          reset();
          showProgress(false);
        })
        .catch(function () {
          reset();
          showProgress(false);
        });
    }

    btn.addEventListener('click', function (e) {
      e.preventDefault();
      start();
    });

    if (cancelBtn) {
      cancelBtn.addEventListener('click', function (e) {
        e.preventDefault();
        cancel();
      });
    }
  });

  // Orphan workout: Generate internal link drafts (and optionally apply) for the current orphan list (AJAX)
  document.addEventListener('DOMContentLoaded', function () {
    var btnGen = document.getElementById('wpsn-il-orphan-gen-ajax-start');
    var btnGenApply = document.getElementById('wpsn-il-orphan-genapply-ajax-start');
    if ((!btnGen && !btnGenApply) || !window.WPSN_GSC) return;

    var cancelBtn = document.getElementById('wpsn-il-orphan-gen-ajax-cancel');
    var spinner = document.getElementById('wpsn-il-orphan-gen-ajax-spinner');
    var wrap = document.getElementById('wpsn-il-orphan-gen-ajax-progress');
    var bar = document.getElementById('wpsn-il-orphan-gen-ajax-bar');
    var statusEl = document.getElementById('wpsn-il-orphan-gen-ajax-status');

    var running = false;
    var cancelled = false;
    var session = '';
    var stage = 'generate';

    // Generate counters
    var total = 0;
    var processed = 0;
    var targetsWithSuggestions = 0;
    var suggestions = 0;
    var skippedNoGsc = 0;
    var skippedNoQueries = 0;
    var skippedByInbound = 0;

    // Apply counters
    var applyTotal = 0;
    var applyProcessed = 0;
    var applyApplied = 0;
    var applyFailed = 0;
    var applySkipped = 0;

    function setSpinner(active) {
      if (!spinner) return;
      if (active) spinner.classList.add('is-active');
      else spinner.classList.remove('is-active');
    }

    function setProgress(pct) {
      if (!bar) return;
      var v = Math.max(0, Math.min(100, pct));
      bar.style.width = String(v) + '%';
    }

    function setStatus(msg) {
      if (!statusEl) return;
      statusEl.textContent = msg || '';
    }

    function showProgress(show) {
      if (!wrap) return;
      wrap.style.display = show ? 'block' : 'none';
      if (cancelBtn) {
        cancelBtn.style.display = show ? 'inline-block' : 'none';
      }
    }

    function reset() {
      running = false;
      cancelled = false;
      session = '';
      stage = 'generate';
      total = 0;
      processed = 0;
      targetsWithSuggestions = 0;
      suggestions = 0;
      skippedNoGsc = 0;
      skippedNoQueries = 0;
      skippedByInbound = 0;

      applyTotal = 0;
      applyProcessed = 0;
      applyApplied = 0;
      applyFailed = 0;
      applySkipped = 0;

      if (btnGen) btnGen.disabled = false;
      if (btnGenApply) btnGenApply.disabled = false;
      setSpinner(false);
    }

    function getOrphanFiltersFromUrl() {
      var p = new URLSearchParams(window.location.search || '');
      return {
        s: p.get('s') || '',
        max_in: p.get('max_in') || '',
        include_unknown: p.get('include_unknown') || '',
        post_type: p.get('post_type') || '',
      };
    }

    function finish(ok, message) {
      running = false;
      btn.disabled = false;
      setSpinner(false);
      if (message) setStatus(message);

      if (ok) {
        setTimeout(function () {
          window.location.reload();
        }, 800);
      }
    }

    function step() {
      if (cancelled || !session) {
        reset();
        showProgress(false);
        return;
      }

      var nonce = window.WPSN_GSC.nonce || '';
      var chunk = stage === 'apply' ? 5 : 3;
      postAjax('wpsn_il_orphan_generate_step', { nonce: nonce, session: session, chunk: chunk })
        .then(function (res) {
          if (!res || !res.success) {
            throw new Error((res && res.data && (res.data.message || res.data)) || (window.WPSN_GSC.strings.error || 'Error'));
          }

          var nextStage = (res.data && res.data.stage) || stage;
          if (nextStage !== stage) {
            // Stage switch: reset bar.
            stage = nextStage;
            setProgress(0);
          } else {
            stage = nextStage;
          }

          // Generation counters are always returned (for summary).
          processed = res.data.processed || 0;
          total = res.data.total || total;
          targetsWithSuggestions = res.data.targets_with_suggestions || 0;
          suggestions = res.data.suggestions || 0;
          skippedNoGsc = res.data.skipped_no_gsc || 0;
          skippedNoQueries = res.data.skipped_no_queries || 0;
          skippedByInbound = res.data.skipped_by_inbound || 0;

          // Apply counters (may be 0 during generate stage).
          applyProcessed = res.data.apply_processed || 0;
          applyTotal = res.data.apply_total || applyTotal;
          applyApplied = res.data.apply_applied || 0;
          applyFailed = res.data.apply_failed || 0;
          applySkipped = res.data.apply_skipped || 0;

          var pct = 0;
          var msg = '';

          if (stage === 'apply') {
            pct = applyTotal > 0 ? Math.round((applyProcessed / applyTotal) * 100) : 0;
            msg = 'Applying ' + applyProcessed + ' / ' + applyTotal + ' • Applied ' + applyApplied;
            if (applySkipped) msg += ' • Skipped ' + applySkipped;
            if (applyFailed) msg += ' • Failed ' + applyFailed;
            if (res.data.remaining !== undefined) msg += ' • Remaining ' + res.data.remaining;
          } else {
            pct = total > 0 ? Math.round((processed / total) * 100) : 0;
            msg = 'Generating ' + processed + ' / ' + total;
            msg += ' • Targets ' + targetsWithSuggestions;
            msg += ' • Suggestions ' + suggestions;
            if (skippedNoGsc) msg += ' • No GSC ' + skippedNoGsc;
            if (skippedNoQueries) msg += ' • No queries ' + skippedNoQueries;
            if (skippedByInbound) msg += ' • Inbound>max ' + skippedByInbound;
            if (res.data.remaining !== undefined) msg += ' • Remaining ' + res.data.remaining;
          }

          setProgress(pct);
          setStatus(msg);

          if (res.data.done) {
            finish(true, (window.WPSN_GSC.strings.done || 'Done.') + ' ' + msg);
            return;
          }

          setTimeout(step, 200);
        })
        .catch(function (err) {
          finish(false, (window.WPSN_GSC.strings.error || 'Error') + ' ' + (err && err.message ? err.message : ''));
        });
    }

    function start(mode) {
      if (running) return;
      running = true;
      cancelled = false;
      stage = 'generate';

      var nonce = window.WPSN_GSC.nonce || '';
      var limitInput = document.getElementById('wpsn-il-orphan-gen-limit');
      var limit = limitInput ? parseInt(limitInput.value, 10) : 100;
      if (!limit || limit < 1) limit = 100;

      var skipEl = document.getElementById('wpsn-il-orphan-gen-skip-drafts');
      var skipDrafts = skipEl && skipEl.checked ? 1 : 0;

      var applyLimitInput = document.getElementById('wpsn-il-orphan-apply-limit');
      var applyLimit = applyLimitInput ? parseInt(applyLimitInput.value, 10) : 500;
      if (!applyLimit || applyLimit < 1) applyLimit = 500;

      var minConfInput = document.getElementById('wpsn-il-orphan-apply-min-conf');
      var minConf = minConfInput ? parseFloat(minConfInput.value) : 0.85;
      if (isNaN(minConf) || minConf < 0) minConf = 0;
      if (minConf > 1) minConf = 1;

      var propEl = btnGenApply && mode === 'generate_apply' ? btnGenApply : btnGen;
      if (!propEl) propEl = btnGen || btnGenApply;
      var property = (propEl && propEl.getAttribute('data-property')) || '';
      var startDate = (propEl && propEl.getAttribute('data-start-date')) || '';
      var endDate = (propEl && propEl.getAttribute('data-end-date')) || '';

      if (btnGen) btnGen.disabled = true;
      if (btnGenApply) btnGenApply.disabled = true;
      setSpinner(true);
      showProgress(true);
      setProgress(0);
      setStatus((window.WPSN_GSC.strings.starting || 'Starting…'));

      var filters = getOrphanFiltersFromUrl();
      postAjax(
        'wpsn_il_orphan_generate_start',
        Object.assign(
          {
            nonce: nonce,
            property: property,
            start_date: startDate,
            end_date: endDate,
            limit: limit,
            skip_drafts: skipDrafts,
            mode: mode || 'generate',
            apply_limit: applyLimit,
            min_conf: minConf,
          },
          filters
        )
      )
        .then(function (res) {
          if (!res || !res.success) {
            throw new Error((res && res.data && (res.data.message || res.data)) || (window.WPSN_GSC.strings.error || 'Error'));
          }

          session = res.data.session || '';
          total = res.data.total || 0;
          if (!session) throw new Error('Missing session id');

          if (total <= 0) {
            finish(true, 'No orphan targets matched the current filters.');
            return;
          }

          setStatus((window.WPSN_GSC.strings.processing || 'Processing…') + ' 0 / ' + total);
          step();
        })
        .catch(function (err) {
          finish(false, (window.WPSN_GSC.strings.error || 'Error') + ' ' + (err && err.message ? err.message : ''));
        });
    }

    function cancel() {
      if (!running || !session) {
        reset();
        showProgress(false);
        return;
      }

      cancelled = true;
      setStatus(window.WPSN_GSC.strings.cancelled || 'Cancelled.');

      var nonce = window.WPSN_GSC.nonce || '';
      postAjax('wpsn_il_orphan_generate_cancel', { nonce: nonce, session: session })
        .then(function () {
          reset();
          showProgress(false);
        })
        .catch(function () {
          reset();
          showProgress(false);
        });
    }

    if (btnGen) {
      btnGen.addEventListener('click', function (e) {
        e.preventDefault();
        start('generate');
      });
    }

    if (btnGenApply) {
      btnGenApply.addEventListener('click', function (e) {
        e.preventDefault();
        start('generate_apply');
      });
    }

    if (cancelBtn) {
      cancelBtn.addEventListener('click', function (e) {
        e.preventDefault();
        cancel();
      });
    }
  });

  // Content gap: create WP drafts from selected queries with progress (AJAX)
  document.addEventListener('DOMContentLoaded', function () {
    var selectAll = document.getElementById('wpsn-gap-select-all');
    if (selectAll) {
      selectAll.addEventListener('change', function () {
        var cbs = document.querySelectorAll('.wpsn-gap-cb');
        cbs.forEach(function (cb) {
          if (cb && !cb.disabled) {
            cb.checked = !!selectAll.checked;
          }
        });
      });
    }

    var btn = document.getElementById('wpsn-gap-create-btn');
    if (!btn) return;
  var directBtn = document.getElementById('wpsn-gap-create-direct-btn');
  var directForm = document.getElementById('wpsn-gap-create-direct-form');
  var directPostType = document.getElementById('wpsn-gap-direct-post-type');
  var directSetFocus = document.getElementById('wpsn-gap-direct-set-focus');
  var directIncludeBrief = document.getElementById('wpsn-gap-direct-include-brief');
  var directItems = document.getElementById('wpsn-gap-direct-items');

    var cancelBtn = document.getElementById('wpsn-gap-create-cancel');
    var spinner = document.getElementById('wpsn-gap-create-spinner');
    var progressWrap = document.getElementById('wpsn-gap-create-progress');
    var bar = document.getElementById('wpsn-gap-create-bar');
    var statusEl = document.getElementById('wpsn-gap-create-status');

    var postTypeEl = document.getElementById('wpsn-gap-post-type');
    var setFocusEl = document.getElementById('wpsn-gap-set-focus');
    var includeBriefEl = document.getElementById('wpsn-gap-include-brief');

    var running = false;
    var cancelled = false;
    var session = '';
    var total = 0;

    function esc(s) {
      return (s || '')
        .toString()
        .replace(/&/g, '&amp;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;')
        .replace(/"/g, '&quot;')
        .replace(/'/g, '&#039;');
    }

    function setStatus(msg) {
      if (statusEl) statusEl.innerHTML = msg;
    }

    function setBar(pct) {
      if (!bar) return;
      var p = Math.max(0, Math.min(100, pct || 0));
      bar.style.width = p + '%';
    }

    function showProgress(show) {
      if (progressWrap) progressWrap.style.display = show ? '' : 'none';
    }

    function setRunning(isRunning) {
      running = !!isRunning;
      btn.disabled = running;
      if (cancelBtn) cancelBtn.style.display = running ? '' : 'none';
      if (spinner) spinner.classList[running ? 'add' : 'remove']('is-active');
    }

    function getSelectedItems() {
      var items = [];
      var cbs = document.querySelectorAll('.wpsn-gap-cb:checked');
      cbs.forEach(function (cb) {
        if (!cb || cb.disabled) return;
        var qid = cb.value || '';
        var q = cb.getAttribute('data-q') || '';
        var url = cb.getAttribute('data-url') || '';
        var impr = cb.getAttribute('data-impr') || '';
        var clicks = cb.getAttribute('data-clicks') || '';
        var ctr = cb.getAttribute('data-ctr') || '';
        var pos = cb.getAttribute('data-pos') || '';
        if (!qid || !q) return;
        items.push({
          qid: qid,
          query: q,
          url: url,
          impressions: impr,
          clicks: clicks,
          ctr: ctr,
          position: pos,
        });
      });
      return items;
    }

    function updateRow(payload) {
      if (!payload || !payload.qid) return;
      var cell = document.getElementById('wpsn-gap-actions-' + payload.qid);
      if (!cell) return;

      var title = payload.title || ('#' + payload.post_id);
      var html = '';
      if (payload.edit_link) {
        html += '<a href="' + esc(payload.edit_link) + '">' + esc(title) + '</a>';
        if (payload.status) {
          html += ' <span class="wpsn-note">(' + esc(payload.status) + ')</span>';
        }
        if (payload.bulk_link) {
          html += '<div><a href="' + esc(payload.bulk_link) + '">Open in Bulk</a></div>';
        }
      } else {
        html = '<span class="wpsn-note">Created ' + esc(title) + '</span>';
      }

      cell.innerHTML = html;

      var cb = document.querySelector('.wpsn-gap-cb[value="' + payload.qid + '"]');
      if (cb) {
        cb.checked = false;
        cb.disabled = true;
      }
    }

    function updateRows(list) {
      (list || []).forEach(updateRow);
    }

    function step() {
      if (!running || cancelled) return;
      var nonce = window.WPSN_GSC && window.WPSN_GSC.nonce;

      postAjax('wpsn_gap_create_step', { nonce: nonce, session: session, chunk: 3 })
        .then(function (res) {
          if (!res || !res.success) {
            throw new Error((res && res.data && (res.data.message || res.data)) || (window.WPSN_GSC.strings.error || 'Error'));
          }

          var d = res.data || {};
          var processed = d.processed || 0;
          var created = d.created || 0;
          var skipped = d.skipped || 0;
          var failed = d.failed || 0;
          var pct = total > 0 ? Math.round((processed / total) * 100) : 0;

          setBar(pct);

          var msg =
            esc((window.WPSN_GSC.strings.processing || 'Processing…')) +
            ' ' +
            esc(processed) +
            ' / ' +
            esc(total) +
            ' · Created: ' +
            esc(created) +
            ' · Skipped: ' +
            esc(skipped) +
            ' · Failed: ' +
            esc(failed);
          setStatus(msg);

          updateRows(d.created_rows);
          updateRows(d.skipped_rows);

          if (d.done) {
            setRunning(false);
            setBar(100);
            setStatus(esc((window.WPSN_GSC.strings.done || 'Done.')) + ' ' + msg);
            showProgress(false);
            return;
          }

          setTimeout(step, 120);
        })
        .catch(function (err) {
          setRunning(false);
          setStatus(esc((window.WPSN_GSC.strings.error || 'Error')) + ' ' + esc(err && err.message ? err.message : ''));
          showProgress(false);
        });
    }

    function start() {
      if (running) return;

      var items = getSelectedItems();
      if (!items.length) {
        // eslint-disable-next-line no-alert
        alert((window.WPSN_GSC.strings && window.WPSN_GSC.strings.selectQueries) || 'Select at least one query row first.');
        return;
      }

      cancelled = false;
      session = '';
      total = 0;

      var nonce = window.WPSN_GSC && window.WPSN_GSC.nonce;
      var postType = (postTypeEl && postTypeEl.value) || 'page';
      var setFocus = !!(setFocusEl && setFocusEl.checked);
      var includeBrief = !!(includeBriefEl && includeBriefEl.checked);

      setRunning(true);
      showProgress(true);
      setBar(0);
      setStatus(esc(window.WPSN_GSC.strings.starting || 'Starting…'));

      postAjax('wpsn_gap_create_start', {
        nonce: nonce,
        post_type: postType,
        set_focus: setFocus ? 1 : 0,
        include_brief: includeBrief ? 1 : 0,
        items: JSON.stringify(items),
      })
        .then(function (res) {
          if (!res || !res.success) {
            throw new Error((res && res.data && (res.data.message || res.data)) || (window.WPSN_GSC.strings.error || 'Error'));
          }

          session = res.data.session || '';
          total = res.data.total || 0;
          if (!session) throw new Error('Missing session id');

          if (total <= 0) {
            setRunning(false);
            showProgress(false);
            return;
          }

          setStatus(esc(window.WPSN_GSC.strings.processing || 'Processing…') + ' 0 / ' + esc(total));
          step();
        })
        .catch(function (err) {
          setRunning(false);
          setStatus(esc((window.WPSN_GSC.strings.error || 'Error')) + ' ' + esc(err && err.message ? err.message : ''));
          showProgress(false);
        });
    }

    function cancel() {
      if (!running || !session) {
        setRunning(false);
        showProgress(false);
        return;
      }

      cancelled = true;
      setStatus(esc(window.WPSN_GSC.strings.cancelled || 'Cancelled.'));

      var nonce = window.WPSN_GSC && window.WPSN_GSC.nonce;
      postAjax('wpsn_gap_create_cancel', { nonce: nonce, session: session })
        .then(function () {
          setRunning(false);
          showProgress(false);
        })
        .catch(function () {
          setRunning(false);
          showProgress(false);
        });
    }

    btn.addEventListener('click', function (e) {
      e.preventDefault();
      start();
    });

    if (cancelBtn) {
      cancelBtn.addEventListener('click', function (e) {
        e.preventDefault();
        cancel();
      });
    }
  });

  if (directBtn && directForm && directPostType && directSetFocus && directIncludeBrief && directItems) {
    directBtn.addEventListener('click', function (e) {
      e.preventDefault();
      var items = getSelectedItems();
      if (!items.length) {
        alert(window.WPSN_GSC && window.WPSN_GSC.strings && window.WPSN_GSC.strings.selectAtLeastOne || 'Select at least one query row.');
        return;
      }
      directPostType.value = postType.value || 'page';
      directSetFocus.value = setFocus.checked ? '1' : '0';
      directIncludeBrief.value = includeBrief.checked ? '1' : '0';
      directItems.value = JSON.stringify(items);
      directForm.submit();
    });
  }

  // Query optimizer / Content brief.
  onReady(function () {
    var btn = document.getElementById('wpsn-gsc-brief-generate');
    if (!btn) return;

    var spinner = document.getElementById('wpsn-gsc-brief-spinner');
    var out = document.getElementById('wpsn-brief-output');
    var postIdEl = document.getElementById('wpsn-brief-post-id');
    var queryEl = document.getElementById('wpsn-brief-query');
    var modeEl = document.getElementById('wpsn-brief-mode');
    var persistEl = document.getElementById('wpsn-brief-persist');

    function setLoading(loading) {
      if (spinner) {
        if (loading) spinner.classList.add('is-active');
        else spinner.classList.remove('is-active');
      }
      btn.disabled = !!loading;
    }

    function clearOut() {
      if (!out) return;
      while (out.firstChild) out.removeChild(out.firstChild);
    }

    function addEl(tag, text, parent, className) {
      var el = document.createElement(tag);
      if (className) el.className = className;
      if (typeof text === 'string') el.textContent = text;
      (parent || out).appendChild(el);
      return el;
    }

    function renderBrief(brief, meta) {
      if (!out) return;
      clearOut();

      if (meta && (meta.source || meta.generated_at)) {
        var src = meta.source || '-';
        var ts = meta.generated_at || '-';
        addEl('p', (window.WPSN_GSC.strings.source || 'Source') + ': ' + src + ' · ' + (window.WPSN_GSC.strings.generated || 'Generated') + ': ' + ts, out, 'description');
      }

      // Preset / intent.
      if (meta && (meta.preset || meta.intent)) {
        var parts = [];
        if (meta.preset) {
          parts.push('Preset: ' + String(meta.preset));
        }
        if (meta.intent) {
          var intentLabel = String(meta.intent);
          if (meta.intent_local) {
            intentLabel += ' · local';
          }
          parts.push('Intent: ' + intentLabel);
        }
        if (parts.length) {
          addEl('p', parts.join(' · '), out, 'description');
        }
      }

      // Coverage.
      if (meta && meta.coverage && meta.coverage.total) {
        var cov = meta.coverage;
        var total = parseInt(cov.total, 10) || 0;
        var ch = parseInt(cov.covered_heading, 10) || 0;
        var cc = parseInt(cov.covered_content, 10) || 0;
        if (total > 0) {
          addEl('p', 'Coverage (top queries): H2/H3 ' + ch + '/' + total + ' · content ' + cc + '/' + total, out, 'description');

          if (Array.isArray(cov.items)) {
            var missing = cov.items.filter(function (it) { return it && it.query && !it.covered_heading; });
            if (missing.length) {
              var wrap = document.createElement('div');
              wrap.style.margin = '10px 0 16px';
              var strong = document.createElement('strong');
              strong.textContent = 'Missing (not covered by headings):';
              wrap.appendChild(strong);
              var ulm = document.createElement('ul');
              ulm.style.margin = '6px 0 0 18px';
              missing.slice(0, 8).forEach(function (it) {
                var li = document.createElement('li');
                var imp = (it.impressions != null) ? parseInt(it.impressions, 10) : 0;
                var pos = (it.position != null) ? Number(it.position) : 0;
                li.textContent = String(it.query) + ' (' + imp + ' imp, pos ' + (Math.round(pos * 10) / 10) + ')';
                ulm.appendChild(li);
              });
              wrap.appendChild(ulm);
              out.appendChild(wrap);
            }
          }
        }
      }

      if (!brief) {
        addEl('p', window.WPSN_GSC.strings.noData || 'No data.', out, 'description');
        return;
      }

      var outline = (brief.outline && Array.isArray(brief.outline)) ? brief.outline : [];
      var faq = (brief.faq && Array.isArray(brief.faq)) ? brief.faq : [];
      var recs = (brief.recommendations && Array.isArray(brief.recommendations)) ? brief.recommendations : [];
      var terms = (brief.terms && Array.isArray(brief.terms)) ? brief.terms : [];

      if (outline.length) {
        addEl('h4', window.WPSN_GSC.strings.outline || 'Outline (H2/H3)');
        var ol = document.createElement('ol');
        ol.style.marginLeft = '20px';
        out.appendChild(ol);
        outline.forEach(function (sec) {
          if (!sec || !sec.title) return;
          var li = document.createElement('li');
          var strong = document.createElement('strong');
          strong.textContent = String(sec.title);
          li.appendChild(strong);
          var h3 = (sec.h3 && Array.isArray(sec.h3)) ? sec.h3 : [];
          if (h3.length) {
            var ul = document.createElement('ul');
            ul.style.margin = '6px 0 0 18px';
            h3.forEach(function (h) {
              if (!h) return;
              var hli = document.createElement('li');
              hli.textContent = String(h);
              ul.appendChild(hli);
            });
            li.appendChild(ul);
          }
          ol.appendChild(li);
        });
      }

      if (faq.length) {
        addEl('h4', window.WPSN_GSC.strings.faq || 'FAQ');
        var ol2 = document.createElement('ol');
        ol2.style.marginLeft = '20px';
        out.appendChild(ol2);
        faq.forEach(function (item) {
          if (!item || !item.q) return;
          var li2 = document.createElement('li');
          var strong2 = document.createElement('strong');
          strong2.textContent = String(item.q);
          li2.appendChild(strong2);
          if (item.a) {
            var div = document.createElement('div');
            div.style.marginTop = '4px';
            div.textContent = String(item.a);
            li2.appendChild(div);
          }
          ol2.appendChild(li2);
        });
      }

      if (recs.length) {
        addEl('h4', window.WPSN_GSC.strings.recommendations || 'Recommendations');
        var ul2 = document.createElement('ul');
        ul2.style.marginLeft = '20px';
        out.appendChild(ul2);
        recs.forEach(function (r) {
          if (!r) return;
          var li3 = document.createElement('li');
          li3.textContent = String(r);
          ul2.appendChild(li3);
        });
      }

      if (terms.length) {
        addEl('h4', window.WPSN_GSC.strings.terms || 'Terms to include');
        addEl('p', terms.map(function (t) { return String(t); }).join(', '));
      }
    }

    function showError(msg) {
      if (!out) return;
      clearOut();
      addEl('p', String(msg || (window.WPSN_GSC.strings.error || 'Error')), out, 'description');
    }

    btn.addEventListener('click', function (e) {
      e.preventDefault();
      var postId = postIdEl ? parseInt(postIdEl.value || '0', 10) : 0;
      var query = queryEl ? String(queryEl.value || '').trim() : '';
      var presetEl = document.getElementById('wpsn-brief-preset');
      var preset = presetEl ? String(presetEl.value || 'auto') : 'auto';
      var mode = modeEl ? String(modeEl.value || 'local') : 'local';
      var persist = persistEl ? !!persistEl.checked : true;

      if (!postId || postId <= 0) {
        showError('Invalid post_id');
        return;
      }
      if (!query) {
        showError('Query is required');
        return;
      }

      setLoading(true);
      var nonce = window.WPSN_GSC && window.WPSN_GSC.nonce;
      postAjax('wpsn_gsc_brief_generate', { nonce: nonce, post_id: postId, query: query, preset: preset, mode: mode, persist: persist ? 1 : 0 })
        .then(function (res) {
          if (!res || !res.success) {
            var m = (res && res.data && (res.data.message || res.data)) || (window.WPSN_GSC.strings.error || 'Error');
            if (res && res.data && res.data.retry_after) {
              m = m + ' (retry after ' + res.data.retry_after + 's)';
            }
            throw new Error(m);
          }
          renderBrief(res.data.brief, res.data.meta);
        })
        .catch(function (err) {
          showError(err && err.message ? err.message : (window.WPSN_GSC.strings.error || 'Error'));
        })
        .finally(function () {
          setLoading(false);
        });
    });
  });

  // ===== Clusters: generate internal link drafts inside a cluster (spokes → hub) =====

  document.addEventListener('DOMContentLoaded', function () {
    var running = false;
    var current = null;

    function getWrap(el) {
      if (!el) return null;
      // The progress block sits within the same mini card.
      var card = el.closest('.wpsn-card');
      if (!card) return null;
      return card;
    }

    function setProgress(card, pct) {
      if (!card) return;
      var bar = card.querySelector('.wpsn-cluster-progress .wpsn-progress-bar');
      if (bar) bar.style.width = String(pct || 0) + '%';
    }

    function show(card, on) {
      if (!card) return;
      var p = card.querySelector('.wpsn-cluster-progress');
      if (p) p.style.display = on ? '' : 'none';
    }

    function setStatus(card, msg) {
      if (!card) return;
      var el = card.querySelector('.wpsn-cluster-status');
      if (el) el.textContent = String(msg || '');
    }

    function setSummary(card, msg) {
      if (!card) return;
      var el = card.querySelector('.wpsn-cluster-summary');
      if (el) el.textContent = String(msg || '');
    }

    function setCancel(card, on) {
      if (!card) return;
      var b = card.querySelector('.wpsn-cluster-cancel');
      if (b) b.style.display = on ? '' : 'none';
    }

    function setOpenDrafts(card, url) {
      if (!card) return;
      var a = card.querySelector('.wpsn-cluster-open-drafts');
      if (!a) return;
      if (url) {
        a.href = String(url);
        a.style.display = '';
      } else {
        a.style.display = 'none';
      }
    }

    function disableAll(on) {
      var btns = document.querySelectorAll('.wpsn-cluster-generate');
      btns.forEach(function (b) {
        b.disabled = !!on;
      });
    }

    function step() {
      if (!current || !current.session) return;
      var nonce = window.WPSN_GSC && window.WPSN_GSC.nonce;
      postAjax('wpsn_il_cluster_generate_step', { nonce: nonce, session: current.session, chunk: current.chunk })
        .then(function (res) {
          if (!res || !res.success) {
            var m = (res && res.data && (res.data.message || res.data)) || (window.WPSN_GSC.strings.error || 'Error');
            throw new Error(m);
          }
          var d = res.data || {};
          var processed = parseInt(d.processed || 0, 10);
          var total = parseInt(d.total || 0, 10);
          var gen = parseInt(d.generated || 0, 10);
          var skip = parseInt(d.skipped || 0, 10);
          var fail = parseInt(d.failed || 0, 10);
          var pct = total > 0 ? Math.floor((processed / total) * 100) : 0;
          setProgress(current.card, pct);

          var msg = d.message ? String(d.message) : '';
          if (!msg) {
            msg = (window.WPSN_GSC.strings.processing || 'Processing') + ': ' + processed + '/' + total + ' · drafts ' + gen + ' · skipped ' + skip + (fail ? (' · failed ' + fail) : '');
          }
          setStatus(current.card, msg);

          // Skip breakdown (helps when result is "empty").
          var parts = [];
          var n0 = parseInt(d.skipped_not_zero_impr || 0, 10);
          var nf = parseInt(d.skipped_forbidden || 0, 10);
          var nm = parseInt(d.skipped_no_post || 0, 10);
          var ng = parseInt(d.skipped_no_gsc || 0, 10);
          var na = parseInt(d.skipped_already_linked || 0, 10);
          var no = parseInt(d.skipped_no_overlap || 0, 10);
          var nx = parseInt(d.skipped_no_anchor || 0, 10);
          if (n0) parts.push('not 0-impr: ' + n0);
          if (nf) parts.push('no permission: ' + nf);
          if (nm) parts.push('missing post: ' + nm);
          if (ng) parts.push('no GSC match: ' + ng);
          if (na) parts.push('already linked: ' + na);
          if (no) parts.push('no shared queries: ' + no);
          if (nx) parts.push('no anchor in content: ' + nx);
          setSummary(current.card, parts.length ? ('Skip breakdown: ' + parts.join(' · ')) : '');

          if (d.drafts_url) {
            current.draftsUrl = String(d.drafts_url);
          }

          if (d.done) {
            setProgress(current.card, 100);
            setStatus(current.card, (window.WPSN_GSC.strings.done || 'Done') + ': drafts ' + gen + ' · skipped ' + skip + (fail ? (' · failed ' + fail) : ''));
            setCancel(current.card, false);
            setOpenDrafts(current.card, gen > 0 ? current.draftsUrl : null);
            running = false;
            disableAll(false);
            current = null;
            return;
          }

          setTimeout(step, 200);
        })
        .catch(function (err) {
          setStatus(current.card, (window.WPSN_GSC.strings.error || 'Error') + ': ' + (err && err.message ? err.message : ''));
          setCancel(current.card, false);
          running = false;
          disableAll(false);
          current = null;
        });
    }

    document.addEventListener('click', function (e) {
      var btn = e.target && e.target.closest ? e.target.closest('.wpsn-cluster-generate') : null;
      if (!btn) return;
      e.preventDefault();

      if (running) return;

      var card = getWrap(btn);
      if (!card) return;

      var hub = parseInt(btn.getAttribute('data-hub') || '0', 10);
      var spokes = String(btn.getAttribute('data-spokes') || '').trim();
      if (!hub || hub <= 0 || !spokes) return;

      var maxQEl = card.querySelector('.wpsn-cluster-max-q');
      var maxQ = maxQEl ? parseInt(maxQEl.value || '5', 10) : 5;
      if (!maxQ || maxQ < 1) maxQ = 5;

      running = true;
      disableAll(true);
      show(card, true);
      setCancel(card, true);
      setOpenDrafts(card, null);
      setProgress(card, 0);
      setStatus(card, window.WPSN_GSC.strings.starting || 'Starting…');
      setSummary(card, '');

      var onlyZeroEl = card.querySelector('.wpsn-cluster-only-zero');
      var onlyZero = onlyZeroEl && onlyZeroEl.checked ? 1 : 0;

      var nonce = window.WPSN_GSC && window.WPSN_GSC.nonce;
      postAjax('wpsn_il_cluster_generate_start', { nonce: nonce, hub_post_id: hub, spokes: spokes, max_q: maxQ, chunk: 2, only_zero_impr: onlyZero })
        .then(function (res) {
          if (!res || !res.success) {
            var m = (res && res.data && (res.data.message || res.data)) || (window.WPSN_GSC.strings.error || 'Error');
            throw new Error(m);
          }
          var d = res.data || {};
          current = {
            session: d.session,
            card: card,
            draftsUrl: d.drafts_url || '',
            chunk: 2
          };
          setStatus(card, (window.WPSN_GSC.strings.processing || 'Processing') + ': 0/' + (d.total || 0));
          if (d.skipped_not_zero_impr) {
            setSummary(card, 'Filtered (not 0-impr): ' + String(d.skipped_not_zero_impr));
          }
          step();
        })
        .catch(function (err) {
          setStatus(card, (window.WPSN_GSC.strings.error || 'Error') + ': ' + (err && err.message ? err.message : ''));
          setCancel(card, false);
          running = false;
          disableAll(false);
          current = null;
        });
    });

    document.addEventListener('click', function (e) {
      var btn = e.target && e.target.closest ? e.target.closest('.wpsn-cluster-cancel') : null;
      if (!btn) return;
      e.preventDefault();
      if (!current || !current.session || !running) return;
      var card = current.card;
      setStatus(card, window.WPSN_GSC.strings.canceling || 'Canceling…');
      var nonce = window.WPSN_GSC && window.WPSN_GSC.nonce;
      postAjax('wpsn_il_cluster_generate_cancel', { nonce: nonce, session: current.session })
        .then(function () {
          setStatus(card, window.WPSN_GSC.strings.canceled || 'Canceled');
        })
        .finally(function () {
          setCancel(card, false);
          running = false;
          disableAll(false);
          current = null;
        });
    });
  });

  // Snippet drift monitor (SERP): run SERP checks for selected WP pages with progress
  document.addEventListener('DOMContentLoaded', function () {
    var startBtn = document.getElementById('wpsn-drift-start');
    if (!startBtn) return;

    var cancelBtn = document.getElementById('wpsn-drift-cancel');
    var progressWrap = document.getElementById('wpsn-drift-progress');
    var progressBar = document.getElementById('wpsn-drift-progress-bar');
    var statusEl = document.getElementById('wpsn-drift-status');
    var selectAll = document.getElementById('wpsn-drift-select-all');

    var qppEl = document.getElementById('wpsn-drift-qpp');
    var numEl = document.getElementById('wpsn-drift-num');
    var glEl = document.getElementById('wpsn-drift-gl');
    var hlEl = document.getElementById('wpsn-drift-hl');
    var providerEl = document.getElementById('wpsn-drift-provider');

    var running = false;
    var cancelled = false;
    var session = '';
    var batchId = '';
    var processed = 0;
    var total = 0;

    function setStatus(msg) {
      if (statusEl) statusEl.textContent = msg || '';
    }

    function showProgress(on) {
      if (progressWrap) progressWrap.style.display = on ? 'block' : 'none';
      if (!on && progressBar) progressBar.style.width = '0%';
    }

    function setProgress(pct) {
      if (!progressBar) return;
      var p = pct;
      if (p < 0) p = 0;
      if (p > 100) p = 100;
      progressBar.style.width = p + '%';
    }

    function selectedPostIds() {
      var ids = [];
      var cbs = document.querySelectorAll('.wpsn-drift-page');
      cbs.forEach(function (cb) {
        if (cb && cb.checked && !cb.disabled) {
          var v = parseInt(cb.value, 10);
          if (v && v > 0) ids.push(v);
        }
      });
      return ids;
    }

    function disableUi(disabled) {
      var cbs = document.querySelectorAll('.wpsn-drift-page');
      cbs.forEach(function (cb) {
        if (cb) cb.disabled = !!disabled;
      });
      if (selectAll) selectAll.disabled = !!disabled;
      if (qppEl) qppEl.disabled = !!disabled;
      if (numEl) numEl.disabled = !!disabled;
      if (glEl) glEl.disabled = !!disabled;
      if (hlEl) hlEl.disabled = !!disabled;
      if (providerEl) providerEl.disabled = !!disabled;
      if (startBtn) startBtn.disabled = !!disabled;
      if (cancelBtn) cancelBtn.style.display = disabled ? 'inline-block' : 'none';
    }

    function tickWait(waitSec, msg, nextFn) {
      var remaining = waitSec;
      (function tick() {
        if (cancelled || !session) {
          setStatus((window.WPSN_GSC.strings && window.WPSN_GSC.strings.cancelled) || 'Cancelled.');
          running = false;
          disableUi(false);
          showProgress(false);
          return;
        }
        var tmsg = 'Rate limited. Waiting ' + remaining + 's…';
        if (msg) tmsg += ' ' + msg;
        setStatus(tmsg);
        if (remaining <= 0) {
          setTimeout(nextFn, 80);
          return;
        }
        remaining -= 1;
        setTimeout(tick, 1000);
      })();
    }

    function step() {
      if (cancelled || !session) {
        setStatus((window.WPSN_GSC.strings && window.WPSN_GSC.strings.cancelled) || 'Cancelled.');
        running = false;
        disableUi(false);
        showProgress(false);
        return;
      }

      var nonce = window.WPSN_GSC && window.WPSN_GSC.nonce;
      postAjax('wpsn_gsc_drift_step', { nonce: nonce, session: session })
        .then(function (res) {
          if (!res || !res.success) {
            throw new Error((res && res.data && (res.data.message || res.data)) || (window.WPSN_GSC.strings.error || 'Error'));
          }

          var d = res.data || {};
          processed = d.processed || processed;
          total = d.total || total;
          batchId = d.batch_id || batchId;

          var pct = total > 0 ? Math.round((processed / total) * 100) : 0;
          setProgress(pct);

          var msg = 'Processed ' + processed + ' / ' + total;
          if (d.found !== undefined) msg += ' • Found ' + d.found;
          if (d.drift !== undefined) msg += ' • Drift ' + d.drift;
          if (d.rewrite !== undefined) msg += ' • Rewrite ' + d.rewrite;
          if (d.errors !== undefined) msg += ' • Errors ' + d.errors;
          setStatus(msg);

          if (d.rate_limited) {
            var waitSec = parseInt(d.retry_after, 10);
            if (!waitSec || waitSec < 1) waitSec = 10;
            if (waitSec > 180) waitSec = 180;
            tickWait(waitSec, d.rate_limit_message ? String(d.rate_limit_message) : '', step);
            return;
          }

          if (d.done) {
            running = false;
            disableUi(false);
            showProgress(false);
            setStatus((window.WPSN_GSC.strings.done || 'Done.') + ' ' + msg);
            if (d.redirect) {
              window.location.href = String(d.redirect);
            } else {
              window.location.reload();
            }
            return;
          }

          setTimeout(step, 150);
        })
        .catch(function (err) {
          running = false;
          disableUi(false);
          showProgress(false);
          setStatus((window.WPSN_GSC.strings.error || 'Error') + ': ' + (err && err.message ? err.message : ''));
        });
    }

    if (selectAll) {
      selectAll.addEventListener('change', function () {
        var cbs = document.querySelectorAll('.wpsn-drift-page');
        cbs.forEach(function (cb) {
          if (cb && !cb.disabled) cb.checked = !!selectAll.checked;
        });
      });
    }

    startBtn.addEventListener('click', function () {
      if (running) return;
      var ids = selectedPostIds();
      if (!ids.length) {
        // eslint-disable-next-line no-alert
        alert((window.WPSN_GSC.strings && window.WPSN_GSC.strings.selectPages) || 'Select at least one page.');
        return;
      }

      var qpp = qppEl ? parseInt(qppEl.value, 10) : 3;
      if (!qpp || qpp < 1) qpp = 3;
      if (qpp > 20) qpp = 20;

      var num = numEl ? parseInt(numEl.value, 10) : 10;
      if (!num || num < 1) num = 10;
      if (num > 20) num = 20;

      var gl = glEl ? String(glEl.value || '') : '';
      var hl = hlEl ? String(hlEl.value || '') : '';
      var provider = providerEl ? String(providerEl.value || 'serper') : 'serper';

      var nonce = window.WPSN_GSC && window.WPSN_GSC.nonce;
      running = true;
      cancelled = false;
      session = '';
      batchId = '';
      processed = 0;
      total = 0;

      disableUi(true);
      showProgress(true);
      setProgress(0);
      setStatus((window.WPSN_GSC.strings.starting || 'Starting…'));

      postAjax('wpsn_gsc_drift_start', {
        nonce: nonce,
        post_ids: ids.join(','),
        queries_per_page: qpp,
        num: num,
        gl: gl,
        hl: hl,
        provider_id: provider
      })
        .then(function (res) {
          if (!res || !res.success) {
            throw new Error((res && res.data && (res.data.message || res.data)) || (window.WPSN_GSC.strings.error || 'Error'));
          }

          session = res.data.session || '';
          batchId = res.data.batch_id || '';
          total = res.data.total || 0;
          processed = 0;
          setStatus('Starting… 0 / ' + total);
          step();
        })
        .catch(function (err) {
          running = false;
          disableUi(false);
          showProgress(false);
          setStatus((window.WPSN_GSC.strings.error || 'Error') + ': ' + (err && err.message ? err.message : ''));
        });
    });

    if (cancelBtn) {
      cancelBtn.addEventListener('click', function () {
        if (!running || !session) return;
        cancelled = true;
        setStatus((window.WPSN_GSC.strings.canceling || 'Canceling…'));
        var nonce = window.WPSN_GSC && window.WPSN_GSC.nonce;
        postAjax('wpsn_gsc_drift_cancel', { nonce: nonce, session: session })
          .finally(function () {
            running = false;
            disableUi(false);
            showProgress(false);
            session = '';
          });
      });
    }
  });
})();
