(() => {
  "use strict";

  const sleep = milliseconds => new Promise(resolve => setTimeout(resolve, milliseconds));
  const text = node => String(node?.innerText || node?.textContent || "").trim();
  const cancelledTaskIds = new Set();

  function taskCancelledError() {
    const error = new Error("编辑页面已关闭，AI任务已终止");
    error.code = "EDITOR_TASK_CANCELLED";
    return error;
  }

  function assertTaskActive(taskId) {
    if (cancelledTaskIds.has(taskId)) throw taskCancelledError();
  }

  function currentTool() {
    const host = location.hostname.toLowerCase();
    if (host.includes("deepseek.com")) return "deepseek";
    if (host.includes("doubao.com")) return "doubao";
    if (host.includes("chatgpt.com")) return "chatgpt";
    return "";
  }

  function findComposer() {
    const selectors = [
      "#prompt-textarea",
      "textarea[placeholder]",
      "textarea",
      '[contenteditable="true"][role="textbox"]',
      '[contenteditable="true"]'
    ];
    for (const selector of selectors) {
      const nodes = [...document.querySelectorAll(selector)];
      const node = nodes.find(item => {
        const rect = item.getBoundingClientRect();
        return rect.width > 80 && rect.height > 20 && getComputedStyle(item).visibility !== "hidden";
      });
      if (node) return node;
    }
    return null;
  }

  function setComposerValue(node, value) {
    node.focus();
    if (node instanceof HTMLTextAreaElement || node instanceof HTMLInputElement) {
      const prototype = node instanceof HTMLTextAreaElement ? HTMLTextAreaElement.prototype : HTMLInputElement.prototype;
      const setter = Object.getOwnPropertyDescriptor(prototype, "value")?.set;
      if (setter) setter.call(node, value);
      else node.value = value;
      node.dispatchEvent(new Event("input", { bubbles: true }));
      node.dispatchEvent(new Event("change", { bubbles: true }));
      return;
    }
    node.textContent = "";
    document.execCommand("insertText", false, value);
    if (!text(node)) node.textContent = value;
    node.dispatchEvent(new InputEvent("input", { bubbles: true, inputType: "insertText", data: value }));
  }

  function visibleButton(selectors) {
    for (const selector of selectors) {
      const nodes = [...document.querySelectorAll(selector)];
      const button = nodes.find(node => {
        const rect = node.getBoundingClientRect();
        return !node.disabled && rect.width > 10 && rect.height > 10 && getComputedStyle(node).visibility !== "hidden";
      });
      if (button) return button;
    }
    return null;
  }

  async function submitPrompt(composer, taskId) {
    const selectors = [
      'button[data-testid="send-button"]',
      'button[aria-label*="Send"]',
      'button[aria-label*="发送"]',
      'button[class*="send"]',
      'button[type="submit"]'
    ];
    // Give controlled composers a bounded render window.  This stays below one
    // second while avoiding a click before DeepSeek has committed the new value.
    for (let attempt = 0; attempt < 10; attempt++) {
      assertTaskActive(taskId);
      const button = visibleButton(selectors);
      if (button) {
        button.click();
        return true;
      }
      if (attempt < 9) await sleep(100);
    }
    assertTaskActive(taskId);
    composer.dispatchEvent(new KeyboardEvent("keydown", { key: "Enter", code: "Enter", bubbles: true }));
    composer.dispatchEvent(new KeyboardEvent("keyup", { key: "Enter", code: "Enter", bubbles: true }));
    return true;
  }

  function assistantMessages() {
    const selectors = [
      '[data-message-author-role="assistant"]',
      '[data-testid*="receive_message"]',
      '[class*="assistant"] [class*="markdown"]',
      '[class*="assistant"][class*="message"]',
      '[class*="message"][class*="receive"]',
      ".ds-markdown",
      '[class*="markdown"]'
    ];
    const nodes = [];
    const seen = new Set();
    selectors.forEach(selector => document.querySelectorAll(selector).forEach(node => {
      if (!seen.has(node) && text(node).length > 20) {
        seen.add(node);
        nodes.push(node);
      }
    }));
    return nodes;
  }

  function generating() {
    return Boolean(visibleButton([
      'button[data-testid="stop-button"]',
      'button[aria-label*="Stop"]',
      'button[aria-label*="停止"]',
      'button[class*="stop"]'
    ]));
  }

  function hasBalancedJsonObject(value) {
    const source = String(value || "");
    const start = source.indexOf("{");
    if (start < 0) return false;
    let depth = 0;
    let quoted = false;
    let escaped = false;
    for (let index = start; index < source.length; index++) {
      const character = source[index];
      if (escaped) {
        escaped = false;
        continue;
      }
      if (quoted && character === "\\") {
        escaped = true;
        continue;
      }
      if (character === '"') {
        quoted = !quoted;
        continue;
      }
      if (quoted) continue;
      if (character === "{") depth += 1;
      if (character === "}") {
        depth -= 1;
        if (depth === 0) return true;
      }
    }
    return false;
  }

  async function openPreferredConversation(title = "优化亚马逊产品信息", taskId = "") {
    // The composer is normally ready before a pinned/history conversation is
    // rendered.  Do a short best-effort lookup, but never hold the prompt for
    // the old six-second sidebar polling window.
    for (let attempt = 0; attempt < 4; attempt++) {
      assertTaskActive(taskId);
      const candidates = [...document.querySelectorAll('a,button,[role="link"],[role="button"]')]
        .filter(node => String(node.textContent || "").replace(/\s+/g, " ").trim().includes(title));
      const visibleTarget = candidates.find(node => {
        const rect = node.getBoundingClientRect();
        return rect.width > 20 && rect.height > 10 && getComputedStyle(node).visibility !== "hidden";
      });
      const target = visibleTarget || candidates[0];
      if (target) {
        target.scrollIntoView({ block: "center" });
        target.dispatchEvent(new MouseEvent("click", { bubbles: true, cancelable: true, view: window }));
        await sleep(120);
        return true;
      }
      if (findComposer()) return false;
      await sleep(100);
    }
    return false;
  }

  async function waitForAnswer(previousTexts, taskId) {
    const started = Date.now();
    let candidate = "";
    let stableSince = 0;
    while (Date.now() - started < 180000) {
      assertTaskActive(taskId);
      const messages = assistantMessages().map(text).filter(Boolean);
      const newest = [...messages].reverse().find(item => !previousTexts.has(item) && item.includes("{")) || "";
      if (newest && newest === candidate) {
        const stableFor = Date.now() - stableSince;
        // DeepSeek occasionally leaves its stop button visible after the answer
        // has completed.  A balanced, stable JSON object is a stronger signal
        // than that stale button state and is safe to return immediately.
        if (hasBalancedJsonObject(newest) && stableFor > 900) return newest;
        if (!generating() && stableFor > 1500) return newest;
        // Never leave the editor spinning forever when the provider renders a
        // malformed but already-finished response; let the editor show its
        // normal JSON error instead.
        if (stableFor > 6000) return newest;
      } else if (newest) {
        candidate = newest;
        stableSince = Date.now();
      }
      await sleep(250);
    }
    throw new Error("等待AI生成超时，请在AI页面完成后重新点击");
  }

  async function finish(task, payload) {
    if (cancelledTaskIds.has(task.id)) return;
    const { activeAiTask } = await chrome.storage.local.get("activeAiTask");
    if (activeAiTask?.id !== task.id) return;
    const message = {
      type: "AI_TASK_RESULT", taskId: task.id, returnTabId: task.returnTabId,
      keepAiTabActive: Boolean(task.keepAiTabActive), ...payload
    };
    await chrome.storage.local.remove("activeAiTask");
    // Storage is the durable delivery path for a sleeping MV3 service worker;
    // the runtime message keeps the already-open editor responsive.
    await chrome.storage.local.set({ lastAiTaskResult: message });
    try { await chrome.runtime.sendMessage(message); } catch {}
  }

  async function run(task) {
    if (window.__shandianmaoAiBridgeTask === task.id) return;
    window.__shandianmaoAiBridgeTask = task.id;
    try {
      assertTaskActive(task.id);
      await openPreferredConversation(task.conversationTitle, task.id);
      const before = new Set(assistantMessages().map(text));
      let composer = null;
      for (let attempt = 0; attempt < 300; attempt++) {
        assertTaskActive(task.id);
        composer = findComposer();
        if (composer) break;
        await sleep(100);
      }
      if (!composer) throw new Error("没有找到AI输入框，请确认已经登录");
      assertTaskActive(task.id);
      setComposerValue(composer, task.prompt);
      await sleep(120);
      assertTaskActive(task.id);
      await submitPrompt(composer, task.id);
      const answer = await waitForAnswer(before, task.id);
      assertTaskActive(task.id);
      await finish(task, { ok: true, text: answer });
    } finally {
      if (window.__shandianmaoAiBridgeTask === task.id) window.__shandianmaoAiBridgeTask = "";
    }
  }

  function cancelTask(taskId) {
    if (!taskId) return false;
    cancelledTaskIds.add(taskId);
    if (window.__shandianmaoAiBridgeTask !== taskId) return true;
    const stop = visibleButton([
      'button[data-testid="stop-button"]', 'button[aria-label*="Stop"]',
      'button[aria-label*="停止"]', 'button[class*="stop"]'
    ]);
    if (stop) stop.click();
    const composer = findComposer();
    if (composer && !generating()) setComposerValue(composer, "");
    return true;
  }

  async function boot() {
    const { activeAiTask: task } = await chrome.storage.local.get("activeAiTask");
    if (!task || task.tool !== currentTool()) return;
    try {
      await run(task);
    } catch (error) {
      if (error?.code === "EDITOR_TASK_CANCELLED") return;
      await finish(task, { ok: false, error: error.message || "AI页面自动化失败" });
    }
  }

  chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
    if (message?.type === "CANCEL_AI_TASK") {
      sendResponse({ ok: true, cancelled: cancelTask(message.taskId) });
      return false;
    }
    if (message?.type !== "START_AI_TASK" || !message.task || message.task.tool !== currentTool()) return;
    sendResponse({ ok: true, started: true });
    run(message.task).catch(async error => {
        if (error?.code === "EDITOR_TASK_CANCELLED") return;
        await finish(message.task, { ok: false, error: error.message || "AI页面自动化失败" });
      });
    return false;
  });

  boot();
})();
