(function () {
  'use strict';

  if (window.__pddReviewHelperInstalled__) {
    return;
  }
  window.__pddReviewHelperInstalled__ = true;

  const PANEL_ID = 'pdd-review-helper-panel';
  const STYLE_ID = 'pdd-review-helper-style';
  const REPLY_TEMPLATES_STORAGE_KEY = 'pdd_review_reply_templates_v1';
  const REPORT_REASON_STORAGE_KEY = 'pdd_review_report_reason_v1';
  const ACTIVE_ACTION_TASK_STORAGE_KEY = 'pdd_review_active_action_task_v1';
  const DEFAULT_KEYWORD = '该用户觉得商品很好，给出了';
  const DEFAULT_REPLIES = [
    '感谢您的信任与支持！祝您生活愉快，每天都有好心情。期待与您的再次相遇！',
    '感谢您的支持与认可！您的满意是我们坚持做好品质的最大动力，期待您的下次光临！',
    '感谢您的认可！很高兴能得到您的喜爱。喜欢的话可以点个关注，店铺上新随时来看看哦！',
    '感谢您的支持，我们一定继续保持好品质，不辜负您的信任！',
    '确认过眼神，是选到了对的鞋！感谢您的满意，后续有任何关于鞋子的保养问题，都可以随时联系我们哦。',
    '感谢仙女的满分支持！希望它能陪你走过每一个闪闪发光的日子！',
    '感谢认可！选对一双好鞋，每天出门心情都会变好~ 咱们店铺会不定期上新，记得多回来看看呀！',
    '您的支持就是我们团队最大的动力。祝您步步生风，万事胜意！'
  ];
  const DEFAULT_REPORT_REASON = '请小二核实，本店铺合法合规经营，我方店铺有完善的售后体系，支持7天无理由退换货，商品若有问题我们全额承担退回运费。但该消费者收到货后，从未联系过客服反馈任何商品问题，也未提供任何图片或视频证明产品存在质量缺陷，直接给予差评。\n我们非常愿意为真实的售后问题负责，但这种拒绝沟通、无凭无据的异常行为，严重不符合正常消费者的购物逻辑。我们合理怀疑此单为异常账号或同行恶意抹黑，意图利用差评影响店铺正常经营和平台优良的竞争环境。恳请平台小二明察，核实该账号的历史评价轨迹，协助屏蔽此不实评价，维护商家的正当权益，感谢！';

  const state = {
    mounted: false,
    expanded: true,
    flashTimer: 0,
    resumeTimer: 0,
    pendingResumeTaskType: '',
    dom: {},
    scan: {
      running: false,
      stopRequested: false,
      detailRows: [],
      uniqueOrders: [],
      keyword: DEFAULT_KEYWORD
    },
    reportScan: {
      running: false,
      stopRequested: false,
      detailRows: [],
      uniqueOrders: []
    },
    reply: {
      running: false,
      stopRequested: false,
      lastReplyIndex: -1,
      templates: DEFAULT_REPLIES.slice(),
      result: null
    },
    reportAction: {
      running: false,
      stopRequested: false,
      reason: DEFAULT_REPORT_REASON,
      result: null
    }
  };

  function isEvaluationPage() {
    return location.hostname === 'mms.pinduoduo.com' && location.pathname.startsWith('/goods/evaluation/index');
  }

  function normalizeText(value) {
    return String(value || '').replace(/\s+/g, ' ').trim();
  }

  function sleep(ms) {
    return new Promise((resolve) => setTimeout(resolve, ms));
  }

  async function waitFor(checker, { timeout = 15000, interval = 250, desc = '目标' } = {}) {
    const start = Date.now();
    while (Date.now() - start < timeout) {
      try {
        const value = checker();
        if (value) return value;
      } catch (_) {}
      await sleep(interval);
    }
    throw new Error(`等待超时：${desc}`);
  }

  async function waitUntil(checker, { timeout = 1800, interval = 150 } = {}) {
    const start = Date.now();
    while (Date.now() - start < timeout) {
      try {
        if (checker()) return true;
      } catch (_) {}
      await sleep(interval);
    }
    return false;
  }

  function isVisible(node) {
    if (!node || !node.isConnected) return false;
    const style = window.getComputedStyle(node);
    return style.display !== 'none' &&
      style.visibility !== 'hidden' &&
      (node.offsetWidth > 0 || node.offsetHeight > 0 || node.getClientRects().length > 0);
  }

  function isAnyTaskRunning() {
    return state.scan.running || state.reportScan.running || state.reply.running || state.reportAction.running;
  }

  function createActionTimeoutError(message) {
    const error = new Error(message);
    error.code = 'ACTION_TIMEOUT';
    return error;
  }

  function createNeedReloadError(message) {
    const error = new Error(message);
    error.code = 'ACTION_NEEDS_RELOAD';
    return error;
  }

  function getRemainingMs(deadline) {
    return Math.max(0, Number(deadline || 0) - Date.now());
  }

  async function waitForUntil(checker, deadline, desc, interval = 250) {
    const timeout = getRemainingMs(deadline);
    if (timeout <= 0) {
      throw createActionTimeoutError(`${desc}等待超时`);
    }

    try {
      return await waitFor(checker, { timeout, interval, desc });
    } catch (_) {
      throw createActionTimeoutError(`${desc}等待超时`);
    }
  }

  async function waitUntilUntil(checker, deadline, interval = 150) {
    const timeout = getRemainingMs(deadline);
    if (timeout <= 0) {
      return false;
    }
    return waitUntil(checker, { timeout, interval });
  }

  async function smartClickUntil(node, successChecker, deadline) {
    if (!node) return false;
    clickNode(node);
    if (!successChecker) return true;
    if (await waitUntilUntil(successChecker, deadline, 150)) {
      return true;
    }

    if (getRemainingMs(deadline) <= 0) {
      return false;
    }

    const nativeResult = await nativeClickElement(node);
    if (!nativeResult.success) {
      return false;
    }
    return waitUntilUntil(successChecker, deadline, 150);
  }

  async function saveActiveActionTask(task) {
    if (!chrome?.storage?.local) return;
    await chrome.storage.local.set({
      [ACTIVE_ACTION_TASK_STORAGE_KEY]: task
    });
  }

  async function loadActiveActionTask() {
    if (!chrome?.storage?.local) return null;
    const data = await chrome.storage.local.get(ACTIVE_ACTION_TASK_STORAGE_KEY);
    return data?.[ACTIVE_ACTION_TASK_STORAGE_KEY] || null;
  }

  async function clearActiveActionTask() {
    if (!chrome?.storage?.local) return;
    await chrome.storage.local.remove(ACTIVE_ACTION_TASK_STORAGE_KEY);
  }

  function ensureStyle() {
    if (document.getElementById(STYLE_ID)) return;

    const style = document.createElement('style');
    style.id = STYLE_ID;
    style.textContent = `
      #${PANEL_ID} {
        position: fixed;
        right: 20px;
        bottom: 20px;
        z-index: 999999;
        width: 390px;
        max-height: calc(100vh - 40px);
        display: flex;
        flex-direction: column;
        border: 1px solid #cfd8ea;
        border-radius: 14px;
        background: #ffffff;
        box-shadow: 0 16px 40px rgba(17, 41, 87, 0.16);
        color: #1f2a44;
        overflow: hidden;
        font: 13px/1.5 -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Microsoft YaHei', sans-serif;
      }
      #${PANEL_ID}[data-collapsed="true"] .pdd-review-body {
        display: none;
      }
      #${PANEL_ID}[data-collapsed="true"] {
        width: 260px;
      }
      #${PANEL_ID}.is-flashing {
        box-shadow: 0 0 0 3px rgba(64, 125, 255, 0.22), 0 18px 46px rgba(17, 41, 87, 0.24);
      }
      #${PANEL_ID} .pdd-review-header {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 10px;
        padding: 10px 12px;
        cursor: move;
        user-select: none;
        color: #16336f;
        background: linear-gradient(135deg, #edf4ff 0%, #ffffff 100%);
        border-bottom: 1px solid #e5ebf7;
      }
      #${PANEL_ID} .pdd-review-title {
        font-size: 14px;
        font-weight: 700;
      }
      #${PANEL_ID} .pdd-review-actions {
        display: flex;
        align-items: center;
        gap: 8px;
      }
      #${PANEL_ID} .pdd-review-mini-btn {
        width: 28px;
        height: 28px;
        padding: 0;
        border: 1px solid #d5def0;
        border-radius: 8px;
        background: #fff;
        color: #36528f;
        font-size: 16px;
        cursor: pointer;
      }
      #${PANEL_ID} .pdd-review-body {
        padding: 12px;
        overflow: auto;
      }
      #${PANEL_ID} .pdd-review-section + .pdd-review-section {
        margin-top: 12px;
      }
      #${PANEL_ID} .pdd-review-section-title {
        margin: 0 0 8px;
        color: #173979;
        font-size: 13px;
        font-weight: 700;
      }
      #${PANEL_ID} .pdd-review-tip {
        color: #6d7890;
        font-size: 12px;
        line-height: 1.7;
      }
      #${PANEL_ID} .pdd-review-row {
        display: flex;
        align-items: center;
        gap: 8px;
        flex-wrap: wrap;
      }
      #${PANEL_ID} .pdd-review-row + .pdd-review-row {
        margin-top: 8px;
      }
      #${PANEL_ID} .pdd-review-input,
      #${PANEL_ID} .pdd-review-textarea {
        width: 100%;
        border: 1px solid #ccd8ef;
        border-radius: 10px;
        padding: 8px 10px;
        box-sizing: border-box;
        font: inherit;
        color: inherit;
        background: #fff;
      }
      #${PANEL_ID} .pdd-review-input:focus,
      #${PANEL_ID} .pdd-review-textarea:focus {
        outline: none;
        border-color: #407dff;
        box-shadow: 0 0 0 3px rgba(64, 125, 255, 0.14);
      }
      #${PANEL_ID} .pdd-review-textarea {
        min-height: 90px;
        resize: vertical;
      }
      #${PANEL_ID} .pdd-review-btn {
        height: 32px;
        padding: 0 12px;
        border: none;
        border-radius: 9px;
        background: #407dff;
        color: #fff;
        font-size: 12px;
        font-weight: 700;
        cursor: pointer;
        box-shadow: 0 8px 18px rgba(64, 125, 255, 0.22);
      }
      #${PANEL_ID} .pdd-review-btn.secondary {
        background: #7b93c9;
        box-shadow: none;
      }
      #${PANEL_ID} .pdd-review-btn.ghost {
        background: #f2f5fb;
        color: #37528f;
        box-shadow: none;
        border: 1px solid #d5def0;
      }
      #${PANEL_ID} .pdd-review-btn:disabled {
        opacity: 0.6;
        cursor: not-allowed;
      }
      #${PANEL_ID} .pdd-review-summary {
        padding: 8px 10px;
        border-radius: 10px;
        background: #f7faff;
        color: #4d5f87;
        font-size: 12px;
        line-height: 1.7;
      }
      #${PANEL_ID} .pdd-review-table-wrap {
        margin-top: 8px;
        max-height: 210px;
        overflow: auto;
        border: 1px solid #e2e8f5;
        border-radius: 10px;
      }
      #${PANEL_ID} .pdd-review-table {
        width: 100%;
        border-collapse: collapse;
        background: #fff;
      }
      #${PANEL_ID} .pdd-review-table th,
      #${PANEL_ID} .pdd-review-table td {
        padding: 8px 10px;
        border-bottom: 1px solid #eef2f9;
        text-align: left;
        vertical-align: top;
        font-size: 12px;
        word-break: break-word;
      }
      #${PANEL_ID} .pdd-review-table th {
        position: sticky;
        top: 0;
        z-index: 1;
        background: #f8fbff;
        color: #355189;
      }
      #${PANEL_ID} .pdd-review-table td.small {
        white-space: nowrap;
      }
      #${PANEL_ID} .pdd-review-log {
        max-height: 160px;
        overflow: auto;
        border: 1px solid #e2e8f5;
        border-radius: 10px;
        background: #fbfcff;
        padding: 8px 10px;
        white-space: pre-wrap;
        color: #465777;
        font-size: 12px;
      }
      #${PANEL_ID} .pdd-review-empty {
        padding: 14px 10px;
        color: #7a869d;
        text-align: center;
        font-size: 12px;
      }
      #${PANEL_ID} details {
        border: 1px solid #e2e8f5;
        border-radius: 10px;
        background: #fbfcff;
        padding: 6px 10px;
      }
      #${PANEL_ID} summary {
        cursor: pointer;
        color: #355189;
        font-weight: 700;
      }
      #${PANEL_ID} .pdd-review-reply-list {
        margin: 8px 0 0;
        padding-left: 18px;
        color: #4b5d80;
      }
      #${PANEL_ID} .pdd-review-reply-list li + li {
        margin-top: 4px;
      }
    `;
    document.head.appendChild(style);
  }

  function getTableRows() {
    return Array.from(document.querySelectorAll('tr[data-testid="beast-core-table-body-tr"]'));
  }

  function getReviewWrapper(row) {
    return row?.querySelector('.logic_reviewWrapper__2SAz9') || null;
  }

  function getReviewText(row) {
    const wrap = getReviewWrapper(row);
    if (!wrap) return '';

    const blocks = Array.from(wrap.children || []).filter((node) => {
      const className = String(node.className || '');
      return !className.includes('logic_imgContainer') && !className.includes('logic_replyTime');
    });
    const text = normalizeText(blocks.map((node) => node.innerText || '').join(' '));
    if (text) return text;
    return normalizeText(wrap.innerText || '');
  }

  function getReplyTimeText(row) {
    return normalizeText(row?.querySelector('.logic_replyTime__32PeR')?.innerText || '');
  }

  function getOrderNoFromRow(row) {
    const match = Array.from(row?.querySelectorAll('.logic_orderInfo__1NNH0') || [])
      .map((node) => normalizeText(node.innerText))
      .find((text) => text.startsWith('订单编号：'));
    return match ? match.replace(/^订单编号：/, '').trim() : '';
  }

  function getResultSignature() {
    return getTableRows()
      .map((row) => `${getOrderNoFromRow(row)}@@${getReviewText(row)}`)
      .join('||');
  }

  function getVisibleButtonsByText(text) {
    return Array.from(document.querySelectorAll('button[data-testid="beast-core-button"], a[data-testid="beast-core-button-link"], [data-testid="beast-core-button-link"]'))
      .filter((node) => isVisible(node) && normalizeText(node.innerText) === text);
  }

  function findQueryButton() {
    return getVisibleButtonsByText('查询')[0] || null;
  }

  function findSearchInput() {
    const queryButton = findQueryButton();
    if (queryButton) {
      let current = queryButton.parentElement;
      while (current && current !== document.body) {
        const scopedInputs = Array.from(current.querySelectorAll('input[data-testid="beast-core-input-htmlInput"]'))
          .filter(isVisible);
        if (scopedInputs.length) {
          return scopedInputs[0];
        }
        current = current.parentElement;
      }
    }

    return Array.from(document.querySelectorAll('input[data-testid="beast-core-input-htmlInput"]'))
      .filter(isVisible)[0] || null;
  }

  function findRowForOrder(orderNo) {
    return getTableRows().find((row) => getOrderNoFromRow(row) === orderNo) || null;
  }

  function findReplyAction(row) {
    return Array.from(row?.querySelectorAll('a[data-testid="beast-core-button-link"], [data-testid="beast-core-button-link"]') || [])
      .find((node) => isVisible(node) && normalizeText(node.innerText) === '回复/互动') || null;
  }

  function findReportAction(row) {
    return Array.from(row?.querySelectorAll('a[data-testid="beast-core-button-link"], [data-testid="beast-core-button-link"]') || [])
      .find((node) => isVisible(node) && normalizeText(node.innerText) === '举报') || null;
  }

  function getRatingScoreFromRow(row) {
    const tbody = row?.closest('tbody');
    const headerRow = tbody?.querySelector('tr[class*="TB_bodyGroupHeader"], tr.TB_bodyGroupHeader_5-178-0') || row?.previousElementSibling;
    if (!headerRow) return 0;

    const starCount = headerRow.querySelectorAll('svg[data-testid="beast-core-icon-star_filled"]').length;
    return Number(starCount || 0);
  }

  function findNextButton() {
    return document.querySelector('li[data-testid="beast-core-pagination-next"]');
  }

  function isPaginationDisabled(node) {
    if (!node) return true;
    const className = String(node.className || '');
    return /disabled/i.test(className) || node.getAttribute('aria-disabled') === 'true';
  }

  function setNativeValue(node, value) {
    if (!node) return;
    const proto = node.tagName === 'TEXTAREA' ? window.HTMLTextAreaElement.prototype : window.HTMLInputElement.prototype;
    const descriptor = Object.getOwnPropertyDescriptor(proto, 'value');
    if (descriptor?.set) {
      descriptor.set.call(node, value);
    } else {
      node.value = value;
    }
    node.dispatchEvent(new Event('input', { bubbles: true }));
    node.dispatchEvent(new Event('change', { bubbles: true }));
  }

  function clickNode(node) {
    if (!node) return false;
    node.scrollIntoView?.({ block: 'center', inline: 'center' });
    node.dispatchEvent(new MouseEvent('mousedown', { bubbles: true, cancelable: true }));
    node.dispatchEvent(new MouseEvent('mouseup', { bubbles: true, cancelable: true }));
    if (typeof node.click === 'function') {
      node.click();
    } else {
      node.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
    }
    return true;
  }

  function requestNativeBrowserClick(x, y) {
    return new Promise((resolve) => {
      if (!chrome?.runtime?.sendMessage) {
        resolve({ success: false, error: 'runtime sendMessage unavailable' });
        return;
      }

      chrome.runtime.sendMessage({ type: 'pdd-native-click', x, y }, (response) => {
        if (chrome.runtime.lastError) {
          resolve({ success: false, error: chrome.runtime.lastError.message || 'native click failed' });
          return;
        }
        resolve({
          success: Boolean(response?.success),
          error: response?.error || ''
        });
      });
    });
  }

  async function nativeClickElement(node) {
    if (!node) return { success: false, error: 'empty node' };

    node.scrollIntoView?.({ block: 'center', inline: 'center' });
    await sleep(60);

    const rect = node.getBoundingClientRect?.();
    if (!rect || rect.width <= 0 || rect.height <= 0) {
      return { success: false, error: 'target rect is empty' };
    }

    const x = Math.round(rect.left + rect.width / 2);
    const y = Math.round(rect.top + rect.height / 2);
    const result = await requestNativeBrowserClick(x, y);
    return { ...result, x, y };
  }

  async function smartClick(node, successChecker) {
    if (!node) return false;

    clickNode(node);
    if (!successChecker) return true;
    if (await waitUntil(successChecker, { timeout: 1400, interval: 150 })) {
      return true;
    }

    const nativeResult = await nativeClickElement(node);
    if (!nativeResult.success) {
      return false;
    }
    return waitUntil(successChecker, { timeout: 1800, interval: 150 });
  }

  function createPanel() {
    ensureStyle();
    removePanel();

    const panel = document.createElement('div');
    panel.id = PANEL_ID;
    panel.dataset.collapsed = 'false';
    panel.innerHTML = `
      <div class="pdd-review-header">
        <div>
          <div class="pdd-review-title">拼多多评价助手</div>
          <div class="pdd-review-tip">支持好评筛选批量回复，以及 1-3 星差评举报</div>
        </div>
        <div class="pdd-review-actions">
          <button class="pdd-review-mini-btn" type="button" data-role="toggle" title="收起/展开">−</button>
        </div>
      </div>
      <div class="pdd-review-body">
        <section class="pdd-review-section">
          <h3 class="pdd-review-section-title">1. 自动筛选与结果表格</h3>
          <div class="pdd-review-row">
            <input class="pdd-review-input" data-role="keyword" type="text" />
          </div>
          <div class="pdd-review-row">
            <button class="pdd-review-btn" type="button" data-role="scanStart">开始筛选</button>
            <button class="pdd-review-btn secondary" type="button" data-role="scanStop" disabled>停止</button>
            <button class="pdd-review-btn ghost" type="button" data-role="copyOrders" disabled>复制订单号</button>
            <button class="pdd-review-btn ghost" type="button" data-role="exportCsv" disabled>导出CSV</button>
          </div>
          <div class="pdd-review-row">
            <button class="pdd-review-btn ghost" type="button" data-role="fillOrders" disabled>填入下方订单框</button>
          </div>
          <div class="pdd-review-summary" data-role="summary">请先回到评价列表的起始页，再点击开始筛选。</div>
          <div class="pdd-review-table-wrap">
            <table class="pdd-review-table">
              <thead>
                <tr>
                  <th style="width:58px;">序号</th>
                  <th style="width:70px;">扫描页</th>
                  <th style="width:145px;">订单号</th>
                  <th>评语</th>
                </tr>
              </thead>
              <tbody data-role="resultBody">
                <tr>
                  <td colspan="4" class="pdd-review-empty">尚未生成结果</td>
                </tr>
              </tbody>
            </table>
          </div>
          <div class="pdd-review-tip" style="margin-top:8px;">筛选会从当前页开始自动翻到最后一页。为了拿全量结果，建议先手动回到第一页。</div>
        </section>

        <section class="pdd-review-section">
          <h3 class="pdd-review-section-title">2. 粘贴订单号并批量回复</h3>
          <div class="pdd-review-row">
            <textarea class="pdd-review-textarea" data-role="orders" placeholder="每行一个订单号"></textarea>
          </div>
          <div class="pdd-review-row">
            <button class="pdd-review-btn" type="button" data-role="replyStart">开始回复</button>
            <button class="pdd-review-btn secondary" type="button" data-role="replyStop" disabled>停止</button>
          </div>
          <div class="pdd-review-tip">执行时会自动搜索订单、点击“回复/互动”、随机选择一条内置回复文案并提交。弹窗打开后若 5 秒内未完成，会自动关闭并跳过；若关不掉，会刷新页面并在 5 秒后继续下一条。</div>
          <details>
            <summary>回复文案（可编辑）</summary>
            <div class="pdd-review-row" style="margin-top:8px;">
              <textarea class="pdd-review-textarea" data-role="replyTemplates" style="min-height:140px;" placeholder="每行一条回复文案"></textarea>
            </div>
            <div class="pdd-review-row">
              <button class="pdd-review-btn ghost" type="button" data-role="saveReplies">保存文案</button>
              <button class="pdd-review-btn ghost" type="button" data-role="resetReplies">恢复默认</button>
            </div>
            <div class="pdd-review-tip" data-role="replyTemplateHint"></div>
          </details>
        </section>

        <section class="pdd-review-section">
          <h3 class="pdd-review-section-title">3. 1-3 星差评举报</h3>
          <div class="pdd-review-row">
            <button class="pdd-review-btn" type="button" data-role="reportScanStart">筛差评订单</button>
            <button class="pdd-review-btn secondary" type="button" data-role="reportScanStop" disabled>停止</button>
            <button class="pdd-review-btn ghost" type="button" data-role="copyReportOrders" disabled>复制订单号</button>
            <button class="pdd-review-btn ghost" type="button" data-role="exportReportCsv" disabled>导出CSV</button>
          </div>
          <div class="pdd-review-row">
            <button class="pdd-review-btn ghost" type="button" data-role="fillReportOrders" disabled>填入下方举报框</button>
          </div>
          <div class="pdd-review-summary" data-role="reportSummary">自动筛选当前页开始的 1-3 星评价，并要求该行存在“举报”按钮。</div>
          <div class="pdd-review-table-wrap">
            <table class="pdd-review-table">
              <thead>
                <tr>
                  <th style="width:58px;">序号</th>
                  <th style="width:58px;">星级</th>
                  <th style="width:70px;">扫描页</th>
                  <th style="width:145px;">订单号</th>
                  <th>评语</th>
                </tr>
              </thead>
              <tbody data-role="reportResultBody">
                <tr>
                  <td colspan="5" class="pdd-review-empty">尚未生成差评举报结果</td>
                </tr>
              </tbody>
            </table>
          </div>
          <div class="pdd-review-row" style="margin-top:8px;">
            <textarea class="pdd-review-textarea" data-role="reportOrders" placeholder="每行一个需要举报的订单号"></textarea>
          </div>
          <div class="pdd-review-row">
            <button class="pdd-review-btn" type="button" data-role="reportStart">开始举报</button>
            <button class="pdd-review-btn secondary" type="button" data-role="reportStop" disabled>停止</button>
          </div>
          <div class="pdd-review-tip">执行时会自动搜索订单、点击“举报”、勾选“同行恶意差评”、填入举报原因并提交。弹窗打开后若 5 秒内未完成，会自动关闭并跳过；若关不掉，会刷新页面并在 5 秒后继续下一条。</div>
          <details>
            <summary>举报原因（可编辑）</summary>
            <div class="pdd-review-row" style="margin-top:8px;">
              <textarea class="pdd-review-textarea" data-role="reportReason" style="min-height:150px;" placeholder="请输入举报原因"></textarea>
            </div>
            <div class="pdd-review-row">
              <button class="pdd-review-btn ghost" type="button" data-role="saveReportReason">保存举报原因</button>
              <button class="pdd-review-btn ghost" type="button" data-role="resetReportReason">恢复默认</button>
            </div>
            <div class="pdd-review-tip" data-role="reportReasonHint"></div>
          </details>
        </section>

        <section class="pdd-review-section">
          <h3 class="pdd-review-section-title">4. 运行日志</h3>
          <div class="pdd-review-log" data-role="log">等待操作…</div>
        </section>
      </div>
    `;
    document.body.appendChild(panel);
    bindPanel(panel);

    state.dom.panel = panel;
    state.dom.toggleBtn = panel.querySelector('[data-role="toggle"]');
    state.dom.keywordInput = panel.querySelector('[data-role="keyword"]');
    state.dom.scanStartBtn = panel.querySelector('[data-role="scanStart"]');
    state.dom.scanStopBtn = panel.querySelector('[data-role="scanStop"]');
    state.dom.copyOrdersBtn = panel.querySelector('[data-role="copyOrders"]');
    state.dom.exportCsvBtn = panel.querySelector('[data-role="exportCsv"]');
    state.dom.fillOrdersBtn = panel.querySelector('[data-role="fillOrders"]');
    state.dom.summary = panel.querySelector('[data-role="summary"]');
    state.dom.resultBody = panel.querySelector('[data-role="resultBody"]');
    state.dom.ordersTextarea = panel.querySelector('[data-role="orders"]');
    state.dom.replyStartBtn = panel.querySelector('[data-role="replyStart"]');
    state.dom.replyStopBtn = panel.querySelector('[data-role="replyStop"]');
    state.dom.replyTemplatesTextarea = panel.querySelector('[data-role="replyTemplates"]');
    state.dom.saveRepliesBtn = panel.querySelector('[data-role="saveReplies"]');
    state.dom.resetRepliesBtn = panel.querySelector('[data-role="resetReplies"]');
    state.dom.replyTemplateHint = panel.querySelector('[data-role="replyTemplateHint"]');
    state.dom.reportScanStartBtn = panel.querySelector('[data-role="reportScanStart"]');
    state.dom.reportScanStopBtn = panel.querySelector('[data-role="reportScanStop"]');
    state.dom.copyReportOrdersBtn = panel.querySelector('[data-role="copyReportOrders"]');
    state.dom.exportReportCsvBtn = panel.querySelector('[data-role="exportReportCsv"]');
    state.dom.fillReportOrdersBtn = panel.querySelector('[data-role="fillReportOrders"]');
    state.dom.reportSummary = panel.querySelector('[data-role="reportSummary"]');
    state.dom.reportResultBody = panel.querySelector('[data-role="reportResultBody"]');
    state.dom.reportOrdersTextarea = panel.querySelector('[data-role="reportOrders"]');
    state.dom.reportStartBtn = panel.querySelector('[data-role="reportStart"]');
    state.dom.reportStopBtn = panel.querySelector('[data-role="reportStop"]');
    state.dom.reportReasonTextarea = panel.querySelector('[data-role="reportReason"]');
    state.dom.saveReportReasonBtn = panel.querySelector('[data-role="saveReportReason"]');
    state.dom.resetReportReasonBtn = panel.querySelector('[data-role="resetReportReason"]');
    state.dom.reportReasonHint = panel.querySelector('[data-role="reportReasonHint"]');
    state.dom.log = panel.querySelector('[data-role="log"]');

    state.dom.keywordInput.value = state.scan.keyword || DEFAULT_KEYWORD;
    renderReplyTemplateEditor();
    renderReportReasonEditor();

    state.dom.toggleBtn.addEventListener('click', () => {
      state.expanded = !state.expanded;
      panel.dataset.collapsed = state.expanded ? 'false' : 'true';
      state.dom.toggleBtn.textContent = state.expanded ? '−' : '+';
    });
    state.dom.scanStartBtn.addEventListener('click', () => {
      startScan().catch((error) => handleScanError(error));
    });
    state.dom.scanStopBtn.addEventListener('click', () => {
      if (state.scan.running) {
        state.scan.stopRequested = true;
        log('已请求停止筛选，当前页处理完成后结束。');
      }
    });
    state.dom.copyOrdersBtn.addEventListener('click', () => {
      copyOrders().catch((error) => log(`复制失败：${error?.message || error}`, 'error'));
    });
    state.dom.exportCsvBtn.addEventListener('click', () => {
      exportScanCsv();
    });
    state.dom.fillOrdersBtn.addEventListener('click', () => {
      fillOrdersTextareaFromResults();
    });
    state.dom.replyStartBtn.addEventListener('click', () => {
      startReply().catch((error) => handleReplyError(error));
    });
    state.dom.replyStopBtn.addEventListener('click', () => {
      if (state.reply.running) {
        state.reply.stopRequested = true;
        state.pendingResumeTaskType = '';
        void clearActiveActionTask();
        log('已请求停止回复，当前订单处理完成后结束。');
      }
    });
    state.dom.saveRepliesBtn.addEventListener('click', () => {
      saveReplyTemplatesFromEditor().catch((error) => {
        log(`保存回复文案失败：${error?.message || error}`, 'error');
      });
    });
    state.dom.resetRepliesBtn.addEventListener('click', () => {
      resetReplyTemplates().catch((error) => {
        log(`恢复默认回复文案失败：${error?.message || error}`, 'error');
      });
    });
    state.dom.reportScanStartBtn.addEventListener('click', () => {
      startReportScan().catch((error) => handleReportScanError(error));
    });
    state.dom.reportScanStopBtn.addEventListener('click', () => {
      if (state.reportScan.running) {
        state.reportScan.stopRequested = true;
        log('已请求停止差评筛选，当前页处理完成后结束。');
      }
    });
    state.dom.copyReportOrdersBtn.addEventListener('click', () => {
      copyReportOrders().catch((error) => log(`复制举报订单号失败：${error?.message || error}`, 'error'));
    });
    state.dom.exportReportCsvBtn.addEventListener('click', () => {
      exportReportCsv();
    });
    state.dom.fillReportOrdersBtn.addEventListener('click', () => {
      fillReportOrdersTextareaFromResults();
    });
    state.dom.reportStartBtn.addEventListener('click', () => {
      startReportProcess().catch((error) => handleReportProcessError(error));
    });
    state.dom.reportStopBtn.addEventListener('click', () => {
      if (state.reportAction.running) {
        state.reportAction.stopRequested = true;
        state.pendingResumeTaskType = '';
        void clearActiveActionTask();
        log('已请求停止举报，当前订单处理完成后结束。');
      }
    });
    state.dom.saveReportReasonBtn.addEventListener('click', () => {
      saveReportReasonFromEditor().catch((error) => {
        log(`保存举报原因失败：${error?.message || error}`, 'error');
      });
    });
    state.dom.resetReportReasonBtn.addEventListener('click', () => {
      resetReportReason().catch((error) => {
        log(`恢复默认举报原因失败：${error?.message || error}`, 'error');
      });
    });

    state.mounted = true;
    renderScanResults();
    renderReportScanResults();
    updateControls();
    void loadReplyTemplates();
    void loadReportReason();
    log('评价助手已加载。建议先回到第一页，再开始筛选。');
  }

  function bindPanel(panel) {
    const header = panel.querySelector('.pdd-review-header');
    if (!header) return;

    let dragging = false;
    let startX = 0;
    let startY = 0;
    let offsetX = 0;
    let offsetY = 0;

    header.addEventListener('mousedown', (event) => {
      if (event.button !== 0) return;
      if (event.target.closest('button')) return;

      dragging = true;
      startX = event.clientX;
      startY = event.clientY;

      const rect = panel.getBoundingClientRect();
      offsetX = rect.left;
      offsetY = rect.top;

      panel.style.left = `${rect.left}px`;
      panel.style.top = `${rect.top}px`;
      panel.style.right = 'auto';
      panel.style.bottom = 'auto';
    });

    window.addEventListener('mousemove', (event) => {
      if (!dragging) return;
      const nextLeft = offsetX + (event.clientX - startX);
      const nextTop = offsetY + (event.clientY - startY);
      panel.style.left = `${Math.max(8, nextLeft)}px`;
      panel.style.top = `${Math.max(8, nextTop)}px`;
    });

    window.addEventListener('mouseup', () => {
      dragging = false;
    });
  }

  function removePanel() {
    document.getElementById(PANEL_ID)?.remove();
    state.mounted = false;
    state.dom = {};
  }

  function log(message, type = 'info') {
    const box = state.dom.log;
    const line = `[${new Date().toLocaleTimeString()}] ${message}`;
    if (type === 'error') {
      console.error('[评价助手]', message);
    } else {
      console.log('[评价助手]', message);
    }
    if (!box) return;
    box.textContent += (box.textContent ? '\n' : '') + line;
    box.scrollTop = box.scrollHeight;
  }

  function setSummary(text) {
    if (state.dom.summary) {
      state.dom.summary.textContent = text;
    }
  }

  function updateControls() {
    const hasResults = state.scan.uniqueOrders.length > 0;
    const hasReportResults = state.reportScan.uniqueOrders.length > 0;
    const anyTaskRunning = isAnyTaskRunning() || Boolean(state.pendingResumeTaskType);

    if (state.dom.scanStartBtn) state.dom.scanStartBtn.disabled = anyTaskRunning && !state.scan.running;
    if (state.dom.scanStopBtn) state.dom.scanStopBtn.disabled = !state.scan.running;
    if (state.dom.copyOrdersBtn) state.dom.copyOrdersBtn.disabled = !hasResults || state.scan.running;
    if (state.dom.exportCsvBtn) state.dom.exportCsvBtn.disabled = !state.scan.detailRows.length || state.scan.running;
    if (state.dom.fillOrdersBtn) state.dom.fillOrdersBtn.disabled = !hasResults || state.scan.running;
    if (state.dom.replyStartBtn) state.dom.replyStartBtn.disabled = anyTaskRunning && !state.reply.running;
    if (state.dom.replyStopBtn) state.dom.replyStopBtn.disabled = !state.reply.running;
    if (state.dom.replyTemplatesTextarea) state.dom.replyTemplatesTextarea.disabled = anyTaskRunning;
    if (state.dom.saveRepliesBtn) state.dom.saveRepliesBtn.disabled = anyTaskRunning;
    if (state.dom.resetRepliesBtn) state.dom.resetRepliesBtn.disabled = anyTaskRunning;
    if (state.dom.reportScanStartBtn) state.dom.reportScanStartBtn.disabled = anyTaskRunning && !state.reportScan.running;
    if (state.dom.reportScanStopBtn) state.dom.reportScanStopBtn.disabled = !state.reportScan.running;
    if (state.dom.copyReportOrdersBtn) state.dom.copyReportOrdersBtn.disabled = !hasReportResults || state.reportScan.running;
    if (state.dom.exportReportCsvBtn) state.dom.exportReportCsvBtn.disabled = !state.reportScan.detailRows.length || state.reportScan.running;
    if (state.dom.fillReportOrdersBtn) state.dom.fillReportOrdersBtn.disabled = !hasReportResults || state.reportScan.running;
    if (state.dom.reportStartBtn) state.dom.reportStartBtn.disabled = anyTaskRunning && !state.reportAction.running;
    if (state.dom.reportStopBtn) state.dom.reportStopBtn.disabled = !state.reportAction.running;
    if (state.dom.reportOrdersTextarea) state.dom.reportOrdersTextarea.disabled = anyTaskRunning;
    if (state.dom.reportReasonTextarea) state.dom.reportReasonTextarea.disabled = anyTaskRunning;
    if (state.dom.saveReportReasonBtn) state.dom.saveReportReasonBtn.disabled = anyTaskRunning;
    if (state.dom.resetReportReasonBtn) state.dom.resetReportReasonBtn.disabled = anyTaskRunning;
  }

  function normalizeReplyTemplates(source) {
    const list = Array.isArray(source)
      ? source
      : String(source || '').split(/\r?\n/);

    const normalized = [];
    const seen = new Set();

    list.forEach((item) => {
      const text = normalizeText(item);
      if (!text || seen.has(text)) return;
      seen.add(text);
      normalized.push(text);
    });

    return normalized.slice(0, 50);
  }

  function renderReplyTemplateEditor() {
    const templates = normalizeReplyTemplates(state.reply.templates);
    state.reply.templates = templates.length ? templates : DEFAULT_REPLIES.slice();

    if (state.dom.replyTemplatesTextarea) {
      state.dom.replyTemplatesTextarea.value = state.reply.templates.join('\n');
    }
    if (state.dom.replyTemplateHint) {
      state.dom.replyTemplateHint.textContent = `当前随机回复池共 ${state.reply.templates.length} 条文案，执行时会按此列表随机选择。`;
    }
  }

  function renderReportReasonEditor() {
    if (state.dom.reportReasonTextarea) {
      state.dom.reportReasonTextarea.value = state.reportAction.reason || DEFAULT_REPORT_REASON;
    }
    if (state.dom.reportReasonHint) {
      const length = normalizeText(state.reportAction.reason).length;
      state.dom.reportReasonHint.textContent = `当前举报原因 ${length} 字，提交前会自动填入举报弹窗。`;
    }
  }

  async function loadReplyTemplates() {
    if (!chrome?.storage?.local) {
      renderReplyTemplateEditor();
      return;
    }

    try {
      const data = await chrome.storage.local.get(REPLY_TEMPLATES_STORAGE_KEY);
      const stored = normalizeReplyTemplates(data?.[REPLY_TEMPLATES_STORAGE_KEY]);
      if (stored.length) {
        state.reply.templates = stored;
      }
    } catch (error) {
      console.warn('[评价助手] 读取回复文案失败', error);
    }

    renderReplyTemplateEditor();
  }

  async function saveReplyTemplatesFromEditor() {
    const nextTemplates = normalizeReplyTemplates(state.dom.replyTemplatesTextarea?.value || '');
    if (!nextTemplates.length) {
      throw new Error('至少保留一条回复文案');
    }

    state.reply.templates = nextTemplates;
    renderReplyTemplateEditor();

    if (chrome?.storage?.local) {
      await chrome.storage.local.set({
        [REPLY_TEMPLATES_STORAGE_KEY]: nextTemplates
      });
    }

    log(`已保存 ${nextTemplates.length} 条回复文案。`);
  }

  async function resetReplyTemplates() {
    state.reply.templates = DEFAULT_REPLIES.slice();
    renderReplyTemplateEditor();

    if (chrome?.storage?.local) {
      await chrome.storage.local.remove(REPLY_TEMPLATES_STORAGE_KEY);
    }

    log('已恢复默认回复文案。');
  }

  async function loadReportReason() {
    if (!chrome?.storage?.local) {
      renderReportReasonEditor();
      return;
    }

    try {
      const data = await chrome.storage.local.get(REPORT_REASON_STORAGE_KEY);
      const stored = normalizeText(data?.[REPORT_REASON_STORAGE_KEY] || '');
      if (stored) {
        state.reportAction.reason = stored;
      }
    } catch (error) {
      console.warn('[评价助手] 读取举报原因失败', error);
    }

    renderReportReasonEditor();
  }

  async function saveReportReasonFromEditor() {
    const reason = String(state.dom.reportReasonTextarea?.value || '').trim();
    if (normalizeText(reason).length < 20) {
      throw new Error('举报原因至少保留 20 个字');
    }
    if (reason.length > 500) {
      throw new Error('举报原因不能超过 500 字');
    }

    state.reportAction.reason = reason;
    renderReportReasonEditor();

    if (chrome?.storage?.local) {
      await chrome.storage.local.set({
        [REPORT_REASON_STORAGE_KEY]: reason
      });
    }

    log('已保存举报原因。');
  }

  async function resetReportReason() {
    state.reportAction.reason = DEFAULT_REPORT_REASON;
    renderReportReasonEditor();

    if (chrome?.storage?.local) {
      await chrome.storage.local.remove(REPORT_REASON_STORAGE_KEY);
    }

    log('已恢复默认举报原因。');
  }

  function setActionRunningState(type, running) {
    if (type === 'reply') {
      state.reply.running = running;
    } else if (type === 'report') {
      state.reportAction.running = running;
    }
  }

  function setActionStopState(type, stopRequested) {
    if (type === 'reply') {
      state.reply.stopRequested = stopRequested;
    } else if (type === 'report') {
      state.reportAction.stopRequested = stopRequested;
    }
  }

  function getActionStopState(type) {
    return type === 'reply' ? state.reply.stopRequested : state.reportAction.stopRequested;
  }

  function setActionResultFromTask(task) {
    const result = {
      successes: Array.isArray(task?.successes) ? task.successes.slice() : [],
      failures: Array.isArray(task?.failures) ? task.failures.slice() : []
    };

    if (task?.type === 'reply') {
      state.reply.result = result;
    } else if (task?.type === 'report') {
      state.reportAction.result = result;
    }
  }

  function getActionResultRef(type) {
    return type === 'reply' ? state.reply.result : state.reportAction.result;
  }

  function getActionLabel(type) {
    return type === 'reply' ? '批量回复' : '批量举报';
  }

  function logActionFailures(type, failures) {
    if (!Array.isArray(failures) || !failures.length) {
      return;
    }

    const lines = failures.map((item, index) => `${index + 1}. ${item.orderNo || '未知订单'} - ${item.error || '未知错误'}`);
    log(`${getActionLabel(type)}失败汇总：\n${lines.join('\n')}`, 'error');
    console.table(failures);
  }

  function buildReplyTask(orderList) {
    return {
      type: 'reply',
      orderList: orderList.slice(),
      currentIndex: 0,
      successes: [],
      failures: [],
      resumeAfter: 0,
      templates: normalizeReplyTemplates(state.reply.templates),
      createdAt: Date.now()
    };
  }

  function buildReportTask(orderList, reason) {
    return {
      type: 'report',
      orderList: orderList.slice(),
      currentIndex: 0,
      successes: [],
      failures: [],
      resumeAfter: 0,
      reason,
      createdAt: Date.now()
    };
  }

  function renderScanResults() {
    if (!state.dom.resultBody) return;

    state.dom.resultBody.innerHTML = '';
    if (!state.scan.detailRows.length) {
      const row = document.createElement('tr');
      row.innerHTML = '<td colspan="4" class="pdd-review-empty">尚未生成结果</td>';
      state.dom.resultBody.appendChild(row);
      return;
    }

    const visibleRows = state.scan.detailRows.slice(0, 80);
    visibleRows.forEach((item) => {
      const row = document.createElement('tr');
      const indexCell = document.createElement('td');
      indexCell.className = 'small';
      indexCell.textContent = String(item.index);

      const pageCell = document.createElement('td');
      pageCell.className = 'small';
      pageCell.textContent = String(item.scanPage);

      const orderCell = document.createElement('td');
      orderCell.textContent = item.orderNo;

      const reviewCell = document.createElement('td');
      reviewCell.textContent = item.reviewText;
      reviewCell.title = item.reviewText;

      row.appendChild(indexCell);
      row.appendChild(pageCell);
      row.appendChild(orderCell);
      row.appendChild(reviewCell);
      state.dom.resultBody.appendChild(row);
    });

    if (state.scan.detailRows.length > visibleRows.length) {
      const moreRow = document.createElement('tr');
      moreRow.innerHTML = `<td colspan="4" class="pdd-review-empty">仅展示前 ${visibleRows.length} 条，完整结果请复制订单号或导出 CSV。</td>`;
      state.dom.resultBody.appendChild(moreRow);
    }
  }

  function renderReportScanResults() {
    if (!state.dom.reportResultBody) return;

    state.dom.reportResultBody.innerHTML = '';
    if (!state.reportScan.detailRows.length) {
      const row = document.createElement('tr');
      row.innerHTML = '<td colspan="5" class="pdd-review-empty">尚未生成差评举报结果</td>';
      state.dom.reportResultBody.appendChild(row);
      return;
    }

    const visibleRows = state.reportScan.detailRows.slice(0, 80);
    visibleRows.forEach((item) => {
      const row = document.createElement('tr');

      const indexCell = document.createElement('td');
      indexCell.className = 'small';
      indexCell.textContent = String(item.index);

      const scoreCell = document.createElement('td');
      scoreCell.className = 'small';
      scoreCell.textContent = `${item.rating}星`;

      const pageCell = document.createElement('td');
      pageCell.className = 'small';
      pageCell.textContent = String(item.scanPage);

      const orderCell = document.createElement('td');
      orderCell.textContent = item.orderNo;

      const reviewCell = document.createElement('td');
      reviewCell.textContent = item.reviewText;
      reviewCell.title = item.reviewText;

      row.appendChild(indexCell);
      row.appendChild(scoreCell);
      row.appendChild(pageCell);
      row.appendChild(orderCell);
      row.appendChild(reviewCell);
      state.dom.reportResultBody.appendChild(row);
    });

    if (state.reportScan.detailRows.length > visibleRows.length) {
      const moreRow = document.createElement('tr');
      moreRow.innerHTML = `<td colspan="5" class="pdd-review-empty">仅展示前 ${visibleRows.length} 条，完整结果请复制订单号或导出 CSV。</td>`;
      state.dom.reportResultBody.appendChild(moreRow);
    }
  }

  function getTimestampText() {
    return new Date().toLocaleString('sv-SE').replace(/[-: ]/g, '').replace('T', '_');
  }

  function csvEscape(value) {
    const text = String(value == null ? '' : value);
    if (/[",\n]/.test(text)) {
      return `"${text.replace(/"/g, '""')}"`;
    }
    return text;
  }

  function downloadCsv(filename, rows) {
    const csvText = '\uFEFF' + rows.map((row) => row.map(csvEscape).join(',')).join('\n');
    const blob = new Blob([csvText], { type: 'text/csv;charset=utf-8;' });
    const url = URL.createObjectURL(blob);
    const link = document.createElement('a');
    link.href = url;
    link.download = filename;
    document.body.appendChild(link);
    link.click();
    link.remove();
    URL.revokeObjectURL(url);
  }

  function exportScanCsv() {
    if (!state.scan.detailRows.length) {
      log('暂无可导出的筛选结果。');
      return;
    }

    const rows = [
      ['序号', '扫描页', '订单号', '评语', '评价时间'],
      ...state.scan.detailRows.map((item) => [
        item.index,
        item.scanPage,
        item.orderNo,
        item.reviewText,
        item.replyTime
      ])
    ];
    const filename = `pdd_review_matches_${getTimestampText()}.csv`;
    downloadCsv(filename, rows);
    log(`已导出 CSV：${filename}`);
  }

  function exportReportCsv() {
    if (!state.reportScan.detailRows.length) {
      log('暂无可导出的差评举报结果。');
      return;
    }

    const rows = [
      ['序号', '星级', '扫描页', '订单号', '评语', '评价时间'],
      ...state.reportScan.detailRows.map((item) => [
        item.index,
        item.rating,
        item.scanPage,
        item.orderNo,
        item.reviewText,
        item.replyTime
      ])
    ];
    const filename = `pdd_low_star_report_matches_${getTimestampText()}.csv`;
    downloadCsv(filename, rows);
    log(`已导出差评举报 CSV：${filename}`);
  }

  async function copyText(text) {
    if (navigator?.clipboard?.writeText) {
      await navigator.clipboard.writeText(text);
      return;
    }

    const textarea = document.createElement('textarea');
    textarea.value = text;
    textarea.style.position = 'fixed';
    textarea.style.left = '-9999px';
    document.body.appendChild(textarea);
    textarea.focus();
    textarea.select();
    document.execCommand('copy');
    textarea.remove();
  }

  async function copyOrders() {
    if (!state.scan.uniqueOrders.length) {
      log('暂无可复制的订单号。');
      return;
    }
    const text = state.scan.uniqueOrders.join('\n');
    await copyText(text);
    log(`已复制 ${state.scan.uniqueOrders.length} 个订单号到剪贴板。`);
  }

  async function copyReportOrders() {
    if (!state.reportScan.uniqueOrders.length) {
      log('暂无可复制的举报订单号。');
      return;
    }

    const text = state.reportScan.uniqueOrders.join('\n');
    await copyText(text);
    log(`已复制 ${state.reportScan.uniqueOrders.length} 个举报订单号到剪贴板。`);
  }

  function fillOrdersTextareaFromResults() {
    if (!state.dom.ordersTextarea) return;
    state.dom.ordersTextarea.value = state.scan.uniqueOrders.join('\n');
    log(`已将 ${state.scan.uniqueOrders.length} 个订单号填入下方文本框。`);
  }

  function fillReportOrdersTextareaFromResults() {
    if (!state.dom.reportOrdersTextarea) return;
    state.dom.reportOrdersTextarea.value = state.reportScan.uniqueOrders.join('\n');
    log(`已将 ${state.reportScan.uniqueOrders.length} 个举报订单号填入下方文本框。`);
  }

  async function startScan() {
    if (!isEvaluationPage()) {
      throw new Error('当前不在评价列表页。');
    }
    if (state.scan.running) return;

    const keyword = normalizeText(state.dom.keywordInput?.value || DEFAULT_KEYWORD);
    if (!keyword) {
      throw new Error('请输入筛选关键词。');
    }

    state.scan.running = true;
    state.scan.stopRequested = false;
    state.scan.detailRows = [];
    state.scan.uniqueOrders = [];
    state.scan.keyword = keyword;
    renderScanResults();
    updateControls();
    setSummary(`正在筛选：评语包含“${keyword}”`);
    log(`开始自动筛选，关键词：${keyword}`);

    try {
      let scanPage = 1;
      const detailRows = [];
      const orderSet = new Set();

      while (true) {
        const rows = await waitFor(() => getTableRows().filter(isVisible), {
          timeout: 15000,
          interval: 300,
          desc: '评价列表'
        });

        let pageHitCount = 0;
        rows.forEach((row) => {
          const reviewText = getReviewText(row);
          const orderNo = getOrderNoFromRow(row);
          if (!reviewText || !orderNo) return;
          if (!reviewText.includes(keyword)) return;

          pageHitCount += 1;
          if (!orderSet.has(orderNo)) {
            orderSet.add(orderNo);
          }

          detailRows.push({
            index: detailRows.length + 1,
            scanPage,
            orderNo,
            reviewText,
            replyTime: getReplyTimeText(row)
          });
        });

        state.scan.detailRows = detailRows.slice();
        state.scan.uniqueOrders = Array.from(orderSet);
        renderScanResults();
        setSummary(`已扫描 ${scanPage} 页，命中 ${detailRows.length} 条，去重后 ${orderSet.size} 个订单号`);
        log(`第 ${scanPage} 页扫描完成，命中 ${pageHitCount} 条，累计去重订单 ${orderSet.size} 个。`);

        if (state.scan.stopRequested) {
          log('筛选任务已按请求停止。');
          break;
        }

        const nextButton = findNextButton();
        if (!nextButton || isPaginationDisabled(nextButton)) {
          log('已到最后一页，筛选结束。');
          break;
        }

        const beforeSignature = getResultSignature();
        const clicked = await smartClick(nextButton, () => {
          const nextSignature = getResultSignature();
          return Boolean(nextSignature) && nextSignature !== beforeSignature;
        });

        if (!clicked) {
          throw new Error('无法点击下一页或列表未刷新。');
        }

        scanPage += 1;
        await sleep(500);
      }
    } finally {
      state.scan.running = false;
      updateControls();
    }

    setSummary(`筛选完成：命中 ${state.scan.detailRows.length} 条，去重后 ${state.scan.uniqueOrders.length} 个订单号`);
    window.__pddReviewScanResult = {
      keyword: state.scan.keyword,
      detailRows: state.scan.detailRows.slice(),
      uniqueOrders: state.scan.uniqueOrders.slice()
    };
  }

  function handleScanError(error) {
    state.scan.running = false;
    updateControls();
    const message = error?.message || String(error);
    setSummary(`筛选失败：${message}`);
    log(`筛选失败：${message}`, 'error');
  }

  async function startReportScan() {
    if (!isEvaluationPage()) {
      throw new Error('当前不在评价列表页。');
    }
    if (state.reportScan.running) return;

    state.reportScan.running = true;
    state.reportScan.stopRequested = false;
    state.reportScan.detailRows = [];
    state.reportScan.uniqueOrders = [];
    renderReportScanResults();
    updateControls();
    if (state.dom.reportSummary) {
      state.dom.reportSummary.textContent = '正在筛选 1-3 星且带“举报”按钮的评价订单。';
    }
    log('开始自动筛选 1-3 星且可举报的评价订单。');

    try {
      let scanPage = 1;
      const detailRows = [];
      const orderSet = new Set();

      while (true) {
        const rows = await waitFor(() => getTableRows().filter(isVisible), {
          timeout: 15000,
          interval: 300,
          desc: '评价列表'
        });

        let pageHitCount = 0;
        rows.forEach((row) => {
          const rating = getRatingScoreFromRow(row);
          const orderNo = getOrderNoFromRow(row);
          const reviewText = getReviewText(row);
          const hasReportButton = Boolean(findReportAction(row));

          if (!orderNo || !reviewText || !hasReportButton) return;
          if (rating < 1 || rating > 3) return;

          pageHitCount += 1;
          orderSet.add(orderNo);
          detailRows.push({
            index: detailRows.length + 1,
            rating,
            scanPage,
            orderNo,
            reviewText,
            replyTime: getReplyTimeText(row)
          });
        });

        state.reportScan.detailRows = detailRows.slice();
        state.reportScan.uniqueOrders = Array.from(orderSet);
        renderReportScanResults();
        if (state.dom.reportSummary) {
          state.dom.reportSummary.textContent = `已扫描 ${scanPage} 页，命中 ${detailRows.length} 条，去重后 ${orderSet.size} 个可举报订单号`;
        }
        log(`差评筛选第 ${scanPage} 页完成，命中 ${pageHitCount} 条，累计去重订单 ${orderSet.size} 个。`);

        if (state.reportScan.stopRequested) {
          log('差评筛选任务已按请求停止。');
          break;
        }

        const nextButton = findNextButton();
        if (!nextButton || isPaginationDisabled(nextButton)) {
          log('差评筛选已到最后一页。');
          break;
        }

        const beforeSignature = getResultSignature();
        const clicked = await smartClick(nextButton, () => {
          const nextSignature = getResultSignature();
          return Boolean(nextSignature) && nextSignature !== beforeSignature;
        });

        if (!clicked) {
          throw new Error('无法点击下一页或列表未刷新。');
        }

        scanPage += 1;
        await sleep(500);
      }
    } finally {
      state.reportScan.running = false;
      updateControls();
    }

    if (state.dom.reportSummary) {
      state.dom.reportSummary.textContent = `差评筛选完成：命中 ${state.reportScan.detailRows.length} 条，去重后 ${state.reportScan.uniqueOrders.length} 个可举报订单号`;
    }
    window.__pddReviewReportScanResult = {
      detailRows: state.reportScan.detailRows.slice(),
      uniqueOrders: state.reportScan.uniqueOrders.slice()
    };
  }

  function handleReportScanError(error) {
    state.reportScan.running = false;
    updateControls();
    const message = error?.message || String(error);
    if (state.dom.reportSummary) {
      state.dom.reportSummary.textContent = `差评筛选失败：${message}`;
    }
    log(`差评筛选失败：${message}`, 'error');
  }

  function pickReplyText() {
    const templates = normalizeReplyTemplates(state.reply.templates);
    if (!templates.length) {
      state.reply.templates = DEFAULT_REPLIES.slice();
      return DEFAULT_REPLIES[0];
    }

    let index = Math.floor(Math.random() * templates.length);
    if (templates.length > 1 && index === state.reply.lastReplyIndex) {
      index = (index + 1) % templates.length;
    }
    state.reply.lastReplyIndex = index;
    return templates[index];
  }

  function getReplySubmitButton() {
    return getVisibleButtonsByText('回复')[0] || null;
  }

  function getReplyModalRoot() {
    const submitBtn = getReplySubmitButton();
    if (!submitBtn) return null;

    let current = submitBtn.parentElement;
    while (current && current !== document.body) {
      if (current.querySelector('textarea[data-testid="beast-core-textArea-htmlInput"]')) {
        return current;
      }
      current = current.parentElement;
    }

    return submitBtn.parentElement || null;
  }

  function getReplyTextarea() {
    const root = getReplyModalRoot() || document;
    const scoped = Array.from(root.querySelectorAll('textarea[data-testid="beast-core-textArea-htmlInput"]'))
      .find((node) => isVisible(node));
    if (scoped) return scoped;

    return Array.from(document.querySelectorAll('textarea[data-testid="beast-core-textArea-htmlInput"]'))
      .find((node) => isVisible(node)) || null;
  }

  function getReportSubmitButton() {
    const tracked = Array.from(document.querySelectorAll('button[data-tracking-viewid="new_confirm_report_btn"], button[data-testid="beast-core-modal-ok-button"]'))
      .find((node) => isVisible(node) && normalizeText(node.innerText) === '提交举报');
    if (tracked) return tracked;

    return getVisibleButtonsByText('提交举报')[0] || null;
  }

  function getReportModalRoot() {
    const submitBtn = getReportSubmitButton();
    if (!submitBtn) return null;

    let current = submitBtn.parentElement;
    while (current && current !== document.body) {
      const hasTextarea = current.querySelector('textarea[data-testid="beast-core-textArea-htmlInput"]');
      const hasReportRadio = current.querySelector('input[data-tracking-viewid="bad_peer_reviews"]');
      if (hasTextarea || hasReportRadio) {
        return current;
      }
      current = current.parentElement;
    }

    return submitBtn.parentElement || null;
  }

  function getBadPeerReviewRadioLabel() {
    const root = getReportModalRoot() || document;
    const input = Array.from(root.querySelectorAll('input[data-tracking-viewid="bad_peer_reviews"]'))
      .find((node) => node.closest('label[data-testid="beast-core-radio"]'));
    return input?.closest('label[data-testid="beast-core-radio"]') || null;
  }

  function isBadPeerReviewSelected() {
    const label = getBadPeerReviewRadioLabel();
    if (!label) return false;
    if (label.getAttribute('data-checked') === 'true') return true;
    const input = label.querySelector('input[data-tracking-viewid="bad_peer_reviews"]');
    return Boolean(input?.checked);
  }

  function getReportReasonTextarea() {
    const root = getReportModalRoot() || document;
    const scoped = Array.from(root.querySelectorAll('textarea[data-testid="beast-core-textArea-htmlInput"]'))
      .find((node) => isVisible(node));
    if (scoped) return scoped;

    return Array.from(document.querySelectorAll('textarea[data-testid="beast-core-textArea-htmlInput"]'))
      .find((node) => isVisible(node)) || null;
  }

  function getVisibleDialogCloseButton() {
    const iconClose = Array.from(document.querySelectorAll('[data-testid="beast-core-modal-close"], [data-testid="beast-core-dialog-close"], .Dialog_closeBtn_5-178-0, .Modal_closeBtn_5-178-0'))
      .filter(isVisible)[0];
    if (iconClose) return iconClose;

    return getVisibleButtonsByText('取消')[0] || null;
  }

  async function closeVisibleReplyDialog() {
    const submitBtn = getReplySubmitButton();
    if (!submitBtn) return true;

    const closeBtn = getVisibleDialogCloseButton();
    if (!closeBtn) return false;

    const closed = await smartClick(closeBtn, () => !getReplySubmitButton());
    return Boolean(closed);
  }

  async function closeVisibleReportDialog() {
    const submitBtn = getReportSubmitButton();
    if (!submitBtn) return true;

    const closeBtn = getVisibleDialogCloseButton();
    if (!closeBtn) return false;

    const closed = await smartClick(closeBtn, () => !getReportSubmitButton());
    return Boolean(closed);
  }

  async function handleReplyActionTimeout(orderNo) {
    const closed = await closeVisibleReplyDialog();
    if (closed) {
      throw createActionTimeoutError(`订单 ${orderNo} 回复弹窗超时，已关闭并跳过`);
    }
    throw createNeedReloadError(`订单 ${orderNo} 回复弹窗超时且无法关闭，准备刷新页面`);
  }

  async function handleReportActionTimeout(orderNo) {
    const closed = await closeVisibleReportDialog();
    if (closed) {
      throw createActionTimeoutError(`订单 ${orderNo} 举报弹窗超时，已关闭并跳过`);
    }
    throw createNeedReloadError(`订单 ${orderNo} 举报弹窗超时且无法关闭，准备刷新页面`);
  }

  async function resolveSearchControls() {
    const input = await waitFor(() => findSearchInput(), {
      timeout: 10000,
      interval: 200,
      desc: '订单搜索输入框'
    });
    const button = await waitFor(() => findQueryButton(), {
      timeout: 10000,
      interval: 200,
      desc: '查询按钮'
    });
    return { input, button };
  }

  async function searchOrder(orderNo) {
    const { input, button } = await resolveSearchControls();
    const beforeSignature = getResultSignature();

    input.focus();
    setNativeValue(input, '');
    await sleep(120);
    setNativeValue(input, orderNo);
    await sleep(220);

    const clicked = await smartClick(button, () => {
      const row = findRowForOrder(orderNo);
      if (row) return true;
      const afterSignature = getResultSignature();
      return Boolean(afterSignature) && afterSignature !== beforeSignature;
    });

    if (!clicked) {
      throw new Error(`订单 ${orderNo} 查询按钮点击失败`);
    }

    return waitFor(() => findRowForOrder(orderNo), {
      timeout: 15000,
      interval: 300,
      desc: `订单 ${orderNo} 的查询结果`
    });
  }

  async function openReplyDialog(row, orderNo) {
    const action = findReplyAction(row);
    if (!action) {
      throw new Error(`订单 ${orderNo} 未找到“回复/互动”按钮`);
    }

    const opened = await smartClick(action, () => Boolean(getReplyTextarea()));
    if (!opened) {
      throw new Error(`订单 ${orderNo} 打开回复弹窗失败`);
    }

    return waitFor(() => getReplyTextarea(), {
      timeout: 10000,
      interval: 250,
      desc: '回复输入框'
    });
  }

  async function openReportDialog(row, orderNo) {
    const action = findReportAction(row);
    if (!action) {
      throw new Error(`订单 ${orderNo} 未找到“举报”按钮`);
    }

    const opened = await smartClick(action, () => Boolean(getReportSubmitButton()));
    if (!opened) {
      throw new Error(`订单 ${orderNo} 打开举报弹窗失败`);
    }

    return waitFor(() => getReportSubmitButton(), {
      timeout: 10000,
      interval: 250,
      desc: '提交举报按钮'
    });
  }

  async function submitReply(textarea, replyText, orderNo, deadline) {
    try {
      if (getRemainingMs(deadline) <= 0) {
        await handleReplyActionTimeout(orderNo);
      }

      textarea.focus();
      setNativeValue(textarea, '');
      await sleep(Math.min(120, Math.max(20, getRemainingMs(deadline))));
      setNativeValue(textarea, replyText);
      await sleep(Math.min(220, Math.max(20, getRemainingMs(deadline))));

      const submitBtn = await waitForUntil(() => getReplySubmitButton(), deadline, '回复按钮', 250);
      const submitted = await smartClickUntil(submitBtn, () => !getReplySubmitButton(), deadline);
      if (!submitted) {
        await handleReplyActionTimeout(orderNo);
      }
    } catch (error) {
      if (error?.code === 'ACTION_TIMEOUT' || error?.code === 'ACTION_NEEDS_RELOAD') {
        throw error;
      }
      if (getRemainingMs(deadline) <= 0 || /等待超时/.test(error?.message || '')) {
        await handleReplyActionTimeout(orderNo);
      }
      throw error;
    }
  }

  async function selectBadPeerReviewReason(orderNo, deadline) {
    try {
      const label = await waitForUntil(() => getBadPeerReviewRadioLabel(), deadline, '同行恶意差评单选项', 250);

      if (isBadPeerReviewSelected()) {
        return;
      }

      const selected = await smartClickUntil(label, () => isBadPeerReviewSelected(), deadline);
      if (!selected) {
        await handleReportActionTimeout(orderNo);
      }
    } catch (error) {
      if (error?.code === 'ACTION_TIMEOUT' || error?.code === 'ACTION_NEEDS_RELOAD') {
        throw error;
      }
      if (getRemainingMs(deadline) <= 0 || /等待超时/.test(error?.message || '')) {
        await handleReportActionTimeout(orderNo);
      }
      throw error;
    }
  }

  async function submitReport(orderNo, reportReason, deadline) {
    try {
      const textarea = await waitForUntil(() => getReportReasonTextarea(), deadline, '举报原因输入框', 250);

      textarea.focus();
      setNativeValue(textarea, '');
      await sleep(Math.min(120, Math.max(20, getRemainingMs(deadline))));
      setNativeValue(textarea, reportReason);
      await sleep(Math.min(220, Math.max(20, getRemainingMs(deadline))));

      const submitBtn = await waitForUntil(() => getReportSubmitButton(), deadline, '提交举报按钮', 250);
      const submitted = await smartClickUntil(submitBtn, () => !getReportSubmitButton(), deadline);
      if (!submitted) {
        await handleReportActionTimeout(orderNo);
      }
    } catch (error) {
      if (error?.code === 'ACTION_TIMEOUT' || error?.code === 'ACTION_NEEDS_RELOAD') {
        throw error;
      }
      if (getRemainingMs(deadline) <= 0 || /等待超时/.test(error?.message || '')) {
        await handleReportActionTimeout(orderNo);
      }
      throw error;
    }
  }

  async function processSingleOrder(orderNo, index, total) {
    log(`(${index + 1}/${total}) 开始处理订单 ${orderNo}`);

    await closeVisibleReplyDialog();
    await closeVisibleReportDialog();

    const row = await searchOrder(orderNo);
    row.scrollIntoView?.({ block: 'center', inline: 'nearest' });
    await sleep(240);

    const textarea = await openReplyDialog(row, orderNo);
    const deadline = Date.now() + 5000;
    const replyText = pickReplyText();
    await submitReply(textarea, replyText, orderNo, deadline);
    log(`订单 ${orderNo} 回复完成。`);
    await sleep(1000 + Math.floor(Math.random() * 700));
    return replyText;
  }

  async function processSingleReport(orderNo, index, total, reportReason) {
    log(`(举报 ${index + 1}/${total}) 开始处理订单 ${orderNo}`);

    await closeVisibleReplyDialog();
    await closeVisibleReportDialog();

    const row = await searchOrder(orderNo);
    row.scrollIntoView?.({ block: 'center', inline: 'nearest' });
    await sleep(240);

    await openReportDialog(row, orderNo);
    const deadline = Date.now() + 5000;
    await selectBadPeerReviewReason(orderNo, deadline);
    await submitReport(orderNo, reportReason, deadline);
    log(`订单 ${orderNo} 举报提交完成。`);
    await sleep(1200 + Math.floor(Math.random() * 800));
    return true;
  }

  async function finalizeActionTask(task) {
    const result = getActionResultRef(task.type) || { successes: [], failures: [] };
    setActionRunningState(task.type, false);
    setActionStopState(task.type, false);
    state.pendingResumeTaskType = '';
    await clearActiveActionTask();
    updateControls();

    const successCount = result.successes.length;
    const failureCount = result.failures.length;
    const label = getActionLabel(task.type);
    log(`${label}完成。成功 ${successCount} 单，失败 ${failureCount} 单。`);
    logActionFailures(task.type, result.failures);

    if (task.type === 'reply') {
      window.__pddReviewReplyResult = {
        successes: result.successes.slice(),
        failures: result.failures.slice()
      };
    } else {
      window.__pddReviewReportActionResult = {
        successes: result.successes.slice(),
        failures: result.failures.slice()
      };
    }
  }

  async function scheduleTaskReload(task, index, orderNo, message) {
    const result = getActionResultRef(task.type);
    const failureRecord = { orderNo, error: message };
    result.failures.push(failureRecord);
    task.failures = result.failures.slice();
    task.successes = result.successes.slice();
    task.currentIndex = index + 1;
    task.resumeAfter = Date.now() + 5000;
    await saveActiveActionTask(task);

    log(`${message}，5 秒后刷新页面并继续下一条。`, 'error');
    setActionRunningState(task.type, false);
    updateControls();
    location.reload();
  }

  async function runActionTask(task, { resumed = false } = {}) {
    if (!task || !Array.isArray(task.orderList)) {
      throw new Error('任务数据无效');
    }

    if (task.type === 'reply' && Array.isArray(task.templates) && task.templates.length) {
      state.reply.templates = normalizeReplyTemplates(task.templates);
      renderReplyTemplateEditor();
    }
    if (task.type === 'report' && task.reason) {
      state.reportAction.reason = task.reason;
      renderReportReasonEditor();
    }

    state.pendingResumeTaskType = '';
    clearTimeout(state.resumeTimer);
    setActionRunningState(task.type, true);
    setActionStopState(task.type, false);
    setActionResultFromTask(task);
    updateControls();

    const label = task.type === 'reply' ? '批量回复' : '批量举报';
    if (resumed) {
      log(`${label}任务已恢复，从第 ${task.currentIndex + 1} 条继续。`);
    }

    for (let index = task.currentIndex; index < task.orderList.length; index += 1) {
      const orderNo = task.orderList[index];
      if (getActionStopState(task.type)) {
        log(`${label}任务已停止。`);
        break;
      }

      task.currentIndex = index;
      task.resumeAfter = 0;
      await saveActiveActionTask(task);

      try {
        if (task.type === 'reply') {
          const replyText = await processSingleOrder(orderNo, index, task.orderList.length);
          state.reply.result.successes.push({ orderNo, replyText });
        } else {
          await processSingleReport(orderNo, index, task.orderList.length, task.reason || state.reportAction.reason);
          state.reportAction.result.successes.push({ orderNo });
        }
      } catch (error) {
        if (error?.code === 'ACTION_NEEDS_RELOAD') {
          await scheduleTaskReload(task, index, orderNo, error.message || `订单 ${orderNo} 处理异常`);
          return;
        }

        const message = error?.message || String(error);
        getActionResultRef(task.type).failures.push({ orderNo, error: message });
        log(`订单 ${orderNo} ${task.type === 'reply' ? '处理失败' : '举报失败'}：${message}`, 'error');
        if (task.type === 'reply') {
          await closeVisibleReplyDialog();
        } else {
          await closeVisibleReportDialog();
        }
        await sleep(1000);
      }

      task.successes = getActionResultRef(task.type).successes.slice();
      task.failures = getActionResultRef(task.type).failures.slice();
      task.currentIndex = index + 1;
      task.resumeAfter = 0;
      await saveActiveActionTask(task);
    }

    await finalizeActionTask(task);
  }

  async function startReply() {
    if (!isEvaluationPage()) {
      throw new Error('当前不在评价列表页。');
    }
    if (isAnyTaskRunning() || state.pendingResumeTaskType) return;

    const orderList = Array.from(new Set(
      String(state.dom.ordersTextarea?.value || '')
        .split(/[\n,，\t ]+/)
        .map((item) => item.trim())
        .filter(Boolean)
    ));

    if (!orderList.length) {
      throw new Error('请先粘贴订单号。');
    }

    const task = buildReplyTask(orderList);
    await saveActiveActionTask(task);
    log(`开始批量回复，共 ${orderList.length} 个订单号。`);
    await runActionTask(task);
  }

  function handleReplyError(error) {
    const shouldClearTask = state.reply.running || state.pendingResumeTaskType === 'reply';
    setActionRunningState('reply', false);
    setActionStopState('reply', false);
    state.pendingResumeTaskType = '';
    clearTimeout(state.resumeTimer);
    updateControls();
    if (shouldClearTask) {
      void clearActiveActionTask();
    }
    void closeVisibleReplyDialog();
    const message = error?.message || String(error);
    log(`批量回复失败：${message}`, 'error');
  }

  async function startReportProcess() {
    if (!isEvaluationPage()) {
      throw new Error('当前不在评价列表页。');
    }
    if (isAnyTaskRunning() || state.pendingResumeTaskType) return;

    const orderList = Array.from(new Set(
      String(state.dom.reportOrdersTextarea?.value || '')
        .split(/[\n,，\t ]+/)
        .map((item) => item.trim())
        .filter(Boolean)
    ));

    if (!orderList.length) {
      throw new Error('请先粘贴需要举报的订单号。');
    }

    const reportReason = String(state.dom.reportReasonTextarea?.value || '').trim();
    if (normalizeText(reportReason).length < 20) {
      throw new Error('举报原因至少保留 20 个字。');
    }
    if (reportReason.length > 500) {
      throw new Error('举报原因不能超过 500 字。');
    }

    state.reportAction.reason = reportReason;
    renderReportReasonEditor();
    log(`开始批量举报，共 ${orderList.length} 个订单号。`);
    const task = buildReportTask(orderList, reportReason);
    await saveActiveActionTask(task);
    await runActionTask(task);
  }

  function handleReportProcessError(error) {
    const shouldClearTask = state.reportAction.running || state.pendingResumeTaskType === 'report';
    setActionRunningState('report', false);
    setActionStopState('report', false);
    state.pendingResumeTaskType = '';
    clearTimeout(state.resumeTimer);
    updateControls();
    if (shouldClearTask) {
      void clearActiveActionTask();
    }
    void closeVisibleReportDialog();
    const message = error?.message || String(error);
    log(`批量举报失败：${message}`, 'error');
  }

  function flashPanel() {
    const panel = state.dom.panel;
    if (!panel) return;

    clearTimeout(state.flashTimer);
    panel.classList.add('is-flashing');
    state.flashTimer = window.setTimeout(() => {
      panel.classList.remove('is-flashing');
    }, 1800);
  }

  function focusPanel() {
    if (!state.mounted) {
      createPanel();
    }
    state.expanded = true;
    if (state.dom.panel) {
      state.dom.panel.dataset.collapsed = 'false';
    }
    if (state.dom.toggleBtn) {
      state.dom.toggleBtn.textContent = '−';
    }
    flashPanel();
  }

  async function resumePendingActionTask() {
    if (!isEvaluationPage() || isAnyTaskRunning() || state.pendingResumeTaskType) {
      return;
    }

    let task = null;
    try {
      task = await loadActiveActionTask();
    } catch (error) {
      console.warn('[评价助手] 读取未完成任务失败', error);
      return;
    }

    if (!task || !Array.isArray(task.orderList) || !task.type) {
      return;
    }

    if ((task.currentIndex || 0) >= task.orderList.length) {
      setActionResultFromTask(task);
      await finalizeActionTask(task);
      return;
    }

    const delay = Math.max(0, Number(task.resumeAfter || 0) - Date.now());
    const label = task.type === 'reply' ? '批量回复' : '批量举报';

    state.pendingResumeTaskType = task.type;
    updateControls();
    log(`检测到未完成的${label}任务，将在 ${Math.ceil(delay / 1000)} 秒后从第 ${task.currentIndex + 1} 条继续。`);

    clearTimeout(state.resumeTimer);
    state.resumeTimer = window.setTimeout(() => {
      state.pendingResumeTaskType = '';
      runActionTask(task, { resumed: true }).catch((error) => {
        const message = error?.message || String(error);
        setActionRunningState(task.type, false);
        state.pendingResumeTaskType = '';
        updateControls();
        log(`${label}恢复失败：${message}`, 'error');
      });
    }, delay);
  }

  function ensureMounted() {
    if (isEvaluationPage()) {
      if (!state.mounted) {
        createPanel();
      }
      void resumePendingActionTask();
      return;
    }

    if (state.mounted) {
      state.scan.stopRequested = true;
      state.reportScan.stopRequested = true;
      state.reply.stopRequested = true;
      state.reportAction.stopRequested = true;
      state.pendingResumeTaskType = '';
      clearTimeout(state.resumeTimer);
      removePanel();
    }
  }

  chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
    if (request?.action === 'pddReviewGetContext') {
      sendResponse({
        success: true,
        isEvaluationPage: isEvaluationPage(),
        panelMounted: Boolean(document.getElementById(PANEL_ID))
      });
      return false;
    }

    if (request?.action === 'pddReviewFocusPanel') {
      if (!isEvaluationPage()) {
        sendResponse({ success: false, error: '当前不在评价页' });
        return false;
      }
      focusPanel();
      sendResponse({ success: true });
      return false;
    }

    return false;
  });

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

  window.setInterval(ensureMounted, 1500);
})();
