/**
 * Rich Browser Side Panel 逻辑
 */

const messagesEl = document.getElementById("messages");
const inputEl = document.getElementById("input");
const sendBtn = document.getElementById("sendBtn");
const uploadBtn = document.getElementById("uploadBtn");
const fileInput = document.getElementById("fileInput");
const imagePreviewArea = document.getElementById("imagePreviewArea");

let pendingImages = []; // store base64 strings
const statusDot = document.getElementById("statusDot");
const statusText = document.getElementById("statusText");
const agentNameEl = document.getElementById("agentName");
const contextBar = document.getElementById("contextBar");
const ctxRemove = document.getElementById("ctxRemove");

const headerArea = document.getElementById("headerArea");
const settingsPanel = document.getElementById("settingsPanel");
const tenantIdInput = document.getElementById("tenantIdInput");
const saveIdBtn = document.getElementById("saveIdBtn");

let pageContext = null; // 当前附加的页面上下文

// ─── 首次使用引导 ───

const ONBOARDING_VERSION = "0.2.22";
const onboardingOverlay = document.getElementById("onboardingOverlay");
const onboardingTitle = document.getElementById("onboardingTitle");
const onboardingText = document.getElementById("onboardingText");
const onboardingTip = document.getElementById("onboardingTip");
const onboardingProgress = document.getElementById("onboardingProgress");
const onboardingNextBtn = document.getElementById("onboardingNextBtn");
const onboardingSkipBtn = document.getElementById("onboardingSkipBtn");
const onboardingHelpBtn = document.getElementById("onboardingHelpBtn");

let onboardingStep = 0;
const onboardingSteps = [
  {
    target: "#tenantIdInput",
    title: "第一步：输入 Agent ID",
    text: "先点击顶部区域，打开 Agent ID 设置面板。把注册邮件或“我的 Agent ID”页面里的 UUID 填进去，然后点“保存并重新连接”。",
    tip: "这是必须步骤。没有 Agent ID，插件不会连接后台，也不能参与流水线任务。"
  },
  {
    target: "#statusText",
    title: "第二步：确认连接在线",
    text: "右上角显示“已连接”、绿点亮起，说明这个浏览器已经可以接收自动化任务。",
    tip: "未连接时，先检查 Agent ID 是否正确，以及网络是否能访问 bot.ecommclaw.com。"
  },
  {
    target: "#ozonConfigBtn",
    title: "第三步：配置商户/店铺",
    text: "点右侧店铺图标，维护店铺 ID、店铺名称和 API Key。流水线上架、库存、视频追加等功能会读取这里的商户配置。",
    tip: "这是必须检查项。配置保存到当前 Agent 的服务端工作目录，不只存在浏览器本地。"
  },
  {
    target: "#opsDashboardBtn",
    title: "第四步：打开编排后台",
    text: "点右侧任务编排图标，会打开 ecommclaw 编排后台，并自动带上当前 Agent ID。你可以在后台创建流水线、配置工单、查看运行进度。",
    tip: "批量运营主要在编排后台完成；这个侧边栏负责连接浏览器和执行真实页面动作。"
  },
  {
    target: "#input",
    title: "第五步：和 Agent 对话",
    text: "你可以直接输入需求，也可以上传图片或粘贴图片。Agent 会结合当前页面和上传内容工作。",
    tip: "日常批量运营建议走编排平台；临时任务和检查可以直接在这里对话。"
  }
];

function clearOnboardingHighlight() {
  document.querySelectorAll(".onboarding-highlight").forEach(el => {
    el.classList.remove("onboarding-highlight");
  });
}

function renderOnboardingStep() {
  if (!onboardingOverlay) return;
  const step = onboardingSteps[onboardingStep];
  clearOnboardingHighlight();
  if (step.target === "#tenantIdInput" && settingsPanel) {
    settingsPanel.classList.add("active");
  }
  onboardingTitle.textContent = step.title;
  onboardingText.textContent = step.text;
  onboardingTip.textContent = step.tip;
  onboardingProgress.textContent = `${onboardingStep + 1} / ${onboardingSteps.length}`;
  onboardingNextBtn.textContent = onboardingStep === onboardingSteps.length - 1 ? "完成" : "下一步";

  const target = document.querySelector(step.target);
  if (target) target.classList.add("onboarding-highlight");
}

function openOnboarding(force = false) {
  if (!onboardingOverlay) return;
  onboardingStep = 0;
  onboardingOverlay.classList.add("active");
  renderOnboardingStep();
  if (force) {
    chrome.storage.local.set({ onboardingSeenVersion: "" });
  }
}

function closeOnboarding(markSeen = true) {
  if (!onboardingOverlay) return;
  onboardingOverlay.classList.remove("active");
  clearOnboardingHighlight();
  if (markSeen) {
    chrome.storage.local.set({ onboardingSeenVersion: ONBOARDING_VERSION });
  }
}

function maybeShowOnboarding() {
  chrome.storage.local.get(["onboardingSeenVersion"], (res) => {
    if (res.onboardingSeenVersion !== ONBOARDING_VERSION) {
      setTimeout(() => openOnboarding(false), 350);
    }
  });
}

if (onboardingNextBtn) {
  onboardingNextBtn.addEventListener("click", () => {
    if (onboardingStep >= onboardingSteps.length - 1) {
      closeOnboarding(true);
      return;
    }
    onboardingStep++;
    renderOnboardingStep();
  });
}

if (onboardingSkipBtn) {
  onboardingSkipBtn.addEventListener("click", () => closeOnboarding(true));
}

if (onboardingHelpBtn) {
  onboardingHelpBtn.addEventListener("click", () => openOnboarding(true));
}

// ─── 配置管理 ───

// 切换设置面板
headerArea.addEventListener("click", () => {
  settingsPanel.classList.toggle("active");
});

// 保存 ID 并触发重连
saveIdBtn.addEventListener("click", () => {
  const id = tenantIdInput.value.trim();
  if (id) {
    chrome.storage.local.set({ tenantId: id }, () => {
      addMessage(`⚙️ 已保存租户 ID: ${id}`, "system");
      settingsPanel.classList.remove("active");
      // 通知 background 脚本更新 ID 并重连
      chrome.runtime.sendMessage({ target: "background", action: "update_tenant_id", tenantId: id });
    });
  }
});

// 初始化时加载已保存的 ID
chrome.storage.local.get(["tenantId"], (res) => {
  if (res.tenantId) {
    tenantIdInput.value = res.tenantId;
  }
});

// ─── 消息渲染 ───

let typingIndicator = null;

function showTyping() {
  if (!typingIndicator) {
    typingIndicator = document.createElement("div");
    typingIndicator.className = "msg system typing";
    typingIndicator.textContent = "OpenClaw 正在回复中...";
  }
  messagesEl.appendChild(typingIndicator);
  messagesEl.scrollTop = messagesEl.scrollHeight;
}

function hideTyping() {
  if (typingIndicator && typingIndicator.parentNode) {
    typingIndicator.parentNode.removeChild(typingIndicator);
  }
}

function parseTextToHtml(text) {
  let html = text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
  // Bold
  html = html.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
  // Inline code
  html = html.replace(/`([^`]+)`/g, '<code style="background:#eee;padding:2px 4px;border-radius:3px;">$1</code>');
  // Markdown links: [text](url)
  html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="#" class="chat-link" data-url="$2" style="color:#1677ff;text-decoration:none;">$1</a>');
  // Raw URLs (http/https/file)
  html = html.replace(/(^|[\s\n])(https?:\/\/[^\s\n]+|file:\/\/[^\s\n]+)/g, '$1<a href="#" class="chat-link" data-url="$2" style="color:#1677ff;text-decoration:none;">🔗 链接</a>');
  // Newlines
  html = html.replace(/\n/g, '<br/>');
  return html;
}

function addMessage(text, role, mediaUrls = []) {
  hideTyping();
  const div = document.createElement("div");
  div.className = `msg ${role}`;
  
  let html = parseTextToHtml(text);

  // Render media attachments if any
  if (mediaUrls && mediaUrls.length > 0) {
    html += '<div style="margin-top:8px; display:flex; flex-direction:column; gap:4px;">';
    mediaUrls.forEach(url => {
      // base64 data URI 也算图（sidepanel 用户刚上传时是 data:image/...;base64,... 形式）
      const isImg = url.match(/\.(jpeg|jpg|gif|png|webp)(\?|$)/i) || url.startsWith("data:image/");
      if (isImg) {
        // 给 base64 占位图打 data-pending，等服务端落盘回包 user_images_uploaded 时换成 URL
        const pendingAttr = url.startsWith("data:image/") ? ' data-pending="true"' : '';
        html += `<img src="${url}" style="max-width:100%; border-radius:4px; cursor:pointer;" class="chat-img" data-url="${url}"${pendingAttr} />`;
      } else if (url.match(/\.html(\?|$)/i)) {
        html += `<div style="margin: 8px 0; padding: 12px; background: #e6f4ff; border: 1px solid #91caff; border-radius: 6px; text-align: center; box-shadow: 0 2px 4px rgba(0,0,0,0.05);">
          <a href="#" class="chat-link" data-url="${url}" style="color:#1677ff;text-decoration:none;font-weight:bold;font-size:15px;display:block;">🚀 点击在当前页面打开可视化上架面板</a>
        </div>`;
      } else {
        const filename = url.split('/').pop().split('?')[0] || '附件文件';
        html += `<div>📎 <a href="#" class="chat-link" data-url="${url}" style="color:#1677ff;text-decoration:none;font-weight:bold;">${filename}</a></div>`;
      }
    });
    html += '</div>';
  }

  div.innerHTML = html;

  // Bind click events for links -> open in CURRENT ACTIVE TAB (Left Browser)
  const links = div.querySelectorAll('.chat-link');
  links.forEach(link => {
    link.addEventListener('click', (e) => {
      e.preventDefault();
      const url = link.getAttribute('data-url');
      chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
        if (tabs[0]) {
          chrome.tabs.update(tabs[0].id, { url: url });
        } else {
          chrome.tabs.create({ url: url });
        }
      });
    });
  });

  const imgs = div.querySelectorAll('.chat-img');
  imgs.forEach(img => {
    img.addEventListener('click', (e) => {
      const url = img.getAttribute('data-url');
      chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
        if (tabs[0]) chrome.tabs.update(tabs[0].id, { url: url });
      });
    });
  });

  messagesEl.appendChild(div);
  messagesEl.scrollTop = messagesEl.scrollHeight;

  // 图片/富内容加载完成后再次滚动到底部（防止图片撑高后滚不到底）
  const lazyImages = div.querySelectorAll('img');
  if (lazyImages.length > 0) {
    lazyImages.forEach(img => {
      img.addEventListener('load', () => {
        messagesEl.scrollTop = messagesEl.scrollHeight;
      });
      img.addEventListener('error', () => {
        messagesEl.scrollTop = messagesEl.scrollHeight;
      });
    });
  }
}

// ─── 发送消息 ───

function sendMessage() {
  const text = inputEl.value.trim();
  if (!text && pendingImages.length === 0) return;

  const currentImages = [...pendingImages];
  addMessage(text, "user", currentImages);

  showTyping();
  inputEl.value = "";
  inputEl.style.height = "auto";
  pendingImages = [];
  renderImagePreviews();

  // 标记来源为 Rich Browser（工具选择由 Agent 根据 SKILL.md/MEMORY.md 自行决策）
  const finalMessageText = (text ? text + "\n" : "") + "[System Context: Source=rich-browser channel.]";

  // 请求当前页面的表单上下文，然后连同消息一起发送
  chrome.runtime.sendMessage({ target: "background", action: "get_page_context" }, (contextResponse) => {
    let finalContext = contextResponse;
    if (pageContext) {
      finalContext = { ...contextResponse, ...pageContext };
    }
    
    // 注入当前浏览器所在的本地时区
    const localTz = Intl.DateTimeFormat().resolvedOptions().timeZone;
    finalContext = finalContext || {};
    finalContext.timezone = localTz;

    chrome.runtime.sendMessage(
      {
        target: "background",
        action: "send_message",
        text: finalMessageText, // 发送带有身份标识的消息
        context: finalContext || undefined,
        images: currentImages
      },
      (response) => {
        if (!response?.ok) {
          addMessage("⚠️ 未连接到 OpenClaw，请检查 Gateway 是否运行", "system");
          hideTyping();
        }
      }
    );

    // 发送后清除手动上下文
    if (pageContext) {
      pageContext = null;
      contextBar.classList.add("hidden");
    }
  });
}

// ─── 事件绑定 ───

sendBtn.addEventListener("click", sendMessage);

inputEl.addEventListener("keydown", (e) => {
  if (e.key === "Enter" && !e.shiftKey) {
    e.preventDefault();
    sendMessage();
  }
});

// textarea 自动增高
inputEl.addEventListener("input", () => {
  inputEl.style.height = "auto";
  inputEl.style.height = Math.min(inputEl.scrollHeight, 120) + "px";
});

// 移除上下文
ctxRemove.addEventListener("click", () => {
  pageContext = null;
  contextBar.classList.add("hidden");
});

// ─── 接收来自 background 的消息 ───

chrome.runtime.onMessage.addListener((msg) => {
  if (msg.type === "history") {
    // 收到历史记录，先清空面板，再按正序渲染
    messagesEl.innerHTML = "";
    if (msg.messages && msg.messages.length > 0) {
      msg.messages.forEach(m => addMessage(m.text, m.role, m.mediaUrls));
    } else {
      addMessage("👋 欢迎来到 OpenClaw，我是你的专属 Agent。", "agent");
    }
  }

  if (msg.type === "reply") {
    addMessage(msg.text, "agent", msg.mediaUrls);
  }

  if (msg.type === "system") {
    addMessage(msg.text || "", "system", msg.mediaUrls || []);
  }

  if (msg.type === "error") {
    addMessage(`❌ ${msg.message}`, "system");
  }

  if (msg.type === "status") {
    updateStatus(msg.connected);
  }

  // hello_ack 后服务端回的 agent 显示名（provision-tenant.sh 写在 config.json agents.list[].identity.name）
  if (msg.type === "agent_identity" && agentNameEl) {
    agentNameEl.textContent = msg.displayName || "";
  }

  // 服务端确认用户上传的图已落盘，把 messages 区里 data-pending 的 base64 占位换成 https URL
  if (msg.type === "user_images_uploaded" && Array.isArray(msg.urls)) {
    const pendingImgs = messagesEl.querySelectorAll('img.chat-img[data-pending="true"]');
    let i = 0;
    pendingImgs.forEach(img => {
      if (i < msg.urls.length) {
        img.src = msg.urls[i];
        img.setAttribute('data-url', msg.urls[i]);
        img.removeAttribute('data-pending');
        i++;
      }
    });
  }

  // 服务端推回的 custom models（来自 config.json，不再读 chrome.storage）
  if (msg.type === "custom_models") {
    if (!modelOverlay.classList.contains("active")) return;
    if (msg.error) {
      renderModelList([]);
      alert("拉取模型配置失败：" + msg.error);
      return;
    }
    renderModelList(msg.models || []);
  }

  // 删除成功 → 重新拉一次模型列表
  if (msg.type === "custom_model_deleted") {
    if (!modelOverlay.classList.contains("active")) return;
    if (!msg.ok) {
      alert("删除失败：" + (msg.error || "未知错误"));
      return;
    }
    chrome.runtime.sendMessage({ target: "background", action: "get_custom_models" });
  }

  // 添加 / 覆盖成功 → 重新拉一次模型列表 + 通知用户
  if (msg.type === "custom_model_set") {
    if (!msg.ok) {
      alert("保存模型配置失败：" + (msg.error || "未知错误"));
      return;
    }
    addMessage(`✅ 模型已保存：${msg.model_id}，下次会话生效`, "system");
    if (modelOverlay.classList.contains("active")) {
      chrome.runtime.sendMessage({ target: "background", action: "get_custom_models" });
    }
  }

  // 服务端推回的 shops_matrix.json 内容；只在 ⚙️ overlay 已打开时刷新
  if (msg.type === "shops_matrix") {
    if (!configOverlay.classList.contains("active")) return;
    if (msg.error) {
      alert("拉取店铺配置失败：" + msg.error);
      return;
    }
    if (msg.shops && msg.shops.length > 0) {
      currentConfig = aSchemaToFormGrouping({ shops: msg.shops });
    } else {
      currentConfig = [{
        matrix_id: "",
        matrix_name: "",
        matrix_desc: "",
        shops: [{ shop_id: "", shop_name: "", api_key: "" }]
      }];
    }
    renderConfigForm();
  }
});

function updateStatus(isConnected) {
  statusDot.classList.toggle("connected", isConnected);
  statusText.textContent = isConnected ? "已连接" : "未连接";
  sendBtn.disabled = !isConnected;
  // 断连时清空 agent 名（避免显示旧 agent 的名字误导）；重连后 hello_ack 会回填
  if (!isConnected && agentNameEl) agentNameEl.textContent = "";
}

// ─── 初始化：查询连接状态 ───

chrome.runtime.sendMessage({ target: "background", action: "get_status" }, (res) => {
  if (res) {
    updateStatus(res.connected);
    // 关掉再开 sidepanel 时拿缓存的 displayName（hello_ack 推的那条消息错过了）
    if (agentNameEl && typeof res.displayName === "string") {
      agentNameEl.textContent = res.displayName;
    }
    if (res.history) {
      messagesEl.innerHTML = "";
      if (res.history.length > 0) {
        res.history.forEach(m => addMessage(m.text, m.role, m.mediaUrls));
      } else {
        addMessage("👋 欢迎来到 OpenClaw，我是你的专属 Agent。", "agent");
      }
    }
  }
  maybeShowOnboarding();
});

// ─── 右键菜单：附加页面上下文（可选功能，后续扩展） ───
// 用户也可以在输入框前手动点按钮抓取当前页面


// ─── 右侧功能条交互 ───

const featureBar = document.getElementById("featureBar");
const featureToggle = document.getElementById("featureToggle");
const mainContent = document.getElementById("mainContent");
const ecommerceSkillBtn = document.getElementById("ecommerceSkillBtn");

let featureBarVisible = true;

// 切换功能条显示/隐藏
featureToggle.addEventListener("click", () => {
  featureBarVisible = !featureBarVisible;
  
  if (featureBarVisible) {
    featureBar.classList.remove("hidden");
    featureToggle.classList.remove("bar-hidden");
    featureToggle.classList.add("bar-visible");
    featureToggle.textContent = "«";
    mainContent.classList.remove("bar-hidden");
  } else {
    featureBar.classList.add("hidden");
    featureToggle.classList.remove("bar-visible");
    featureToggle.classList.add("bar-hidden");
    featureToggle.textContent = "»";
    mainContent.classList.add("bar-hidden");
  }
});

// 电商技能按钮点击
ecommerceSkillBtn.addEventListener("click", () => {
  // 在新标签页打开电商技能页面
  chrome.tabs.create({
    url: chrome.runtime.getURL("ecommerce-skills.html")
  });
});

// 任务编排按钮 — 新 tab 打开 ozon-ops 编排后台，带上当前 agent UUID 让目标页自动绑定
const opsDashboardBtn = document.getElementById("opsDashboardBtn");
opsDashboardBtn && opsDashboardBtn.addEventListener("click", () => {
  chrome.storage.local.get(["tenantId"], (res) => {
    const tid = (res && res.tenantId || "").trim();
    if (!tid) {
      alert("请先在顶部输入 Agent UUID 并保存");
      return;
    }
    const url = "https://www.ecommclaw.com/ops/web/index.html?agent_id=" + encodeURIComponent(tid);
    chrome.tabs.create({ url });
  });
});

// ─── Ozon 店铺配置 ───
const ozonConfigBtn = document.getElementById("ozonConfigBtn");
const configOverlay = document.getElementById("configOverlay");
const configCloseBtn = document.getElementById("configCloseBtn");
const matricesContainer = document.getElementById("matricesContainer");

// ─── 绑定事件代理 (解决 CSP 内联 onclick 限制) ───
matricesContainer.addEventListener('click', (e) => {
  const target = e.target;
  if (!target || !target.getAttribute) return;
  
  const action = target.getAttribute('data-action');
  if (!action) return;

  const mIdx = parseInt(target.getAttribute('data-midx'), 10);
  const sIdx = parseInt(target.getAttribute('data-sidx'), 10);

  if (action === 'remove-matrix') {
    removeMatrix(mIdx);
  } else if (action === 'add-shop') {
    addShop(mIdx);
  } else if (action === 'remove-shop') {
    removeShop(mIdx, sIdx);
  }
});

const addMatrixBtn = document.getElementById("addMatrixBtn");
const saveConfigBtn = document.getElementById("saveConfigBtn");

let currentConfig = [];

// 数据源：服务端 agent 工作区根 shops_matrix.json（通过 background ws 拉取）
// 不再读 chrome.storage —— 跨机器/重装会丢，且跟服务端 schema 不易对齐
ozonConfigBtn.addEventListener("click", () => {
  // 先显示加载占位
  currentConfig = [{
    matrix_id: "",
    matrix_name: "加载中...",
    matrix_desc: "",
    shops: []
  }];
  renderConfigForm();
  configOverlay.classList.add("active");

  chrome.runtime.sendMessage({ target: "background", action: "get_shops_matrix" }, (res) => {
    if (!res || !res.connected) {
      currentConfig = [{
        matrix_id: "",
        matrix_name: "",
        matrix_desc: "",
        shops: [{ shop_id: "", shop_name: "", api_key: "" }]
      }];
      renderConfigForm();
      alert("OpenClaw 未连接，无法拉取店铺配置");
    }
    // 真正的店铺数据通过 chrome.runtime.onMessage 的 shops_matrix 推送回来
  });
});

// ── schema 转换：UI 内部 B 式 (matrix→shops 分组) ↔ 存储/传输 A 式 (扁平) ──
// A 式样例（与 skill-ozon-seller/SKILL.md 文档约定一致）:
//   { "shops": [ { "matrix_id":"99-999-3856790", "name":"店铺名", "client_id":"3856790", "ozon_api_key":"...", "company":"", "category":"" } ] }
function formGroupingToASchema(bConfig) {
  var shops = [];
  for (var i = 0; i < (bConfig || []).length; i++) {
    var m = bConfig[i];
    var mId = (m.matrix_id || "default").trim() || "default";
    for (var j = 0; j < (m.shops || []).length; j++) {
      var s = m.shops[j];
      var clientId = (s.shop_id || "").trim();
      shops.push({
        matrix_id: clientId ? (mId + "-" + clientId) : mId,
        name: s.shop_name || "",
        client_id: clientId,
        ozon_api_key: s.api_key || "",
        company: s.company || m.matrix_name || "",
        category: s.category || ""
      });
    }
  }
  return { shops: shops };
}

function aSchemaToFormGrouping(aConfig) {
  // A → B：按 matrix_id 前缀（去掉最后一段 client_id）做分组
  if (!aConfig || !Array.isArray(aConfig.shops)) return [];
  var groups = {};
  for (var i = 0; i < aConfig.shops.length; i++) {
    var s = aConfig.shops[i];
    var fullId = String(s.matrix_id || "default");
    var parts = fullId.split("-");
    // 如果最后一段等于 client_id，则去掉；否则整个当矩阵 id
    var mId = (parts.length > 1 && parts[parts.length - 1] === String(s.client_id || ""))
      ? parts.slice(0, -1).join("-")
      : fullId;
    if (!groups[mId]) groups[mId] = {
      matrix_id: mId,
      matrix_name: s.company || "",
      matrix_desc: "",
      shops: []
    };
    groups[mId].shops.push({
      shop_id: s.client_id || "",
      shop_name: s.name || "",
      company: s.company || "",
      category: s.category || "",
      api_key: s.ozon_api_key || s["ozon-api-key"] || ""
    });
  }
  return Object.values(groups);
}

configCloseBtn.addEventListener("click", () => {
  configOverlay.classList.remove("active");
});

function renderConfigForm() {
  matricesContainer.innerHTML = "";
  currentConfig.forEach((matrix, mIdx) => {
    const matrixEl = document.createElement("div");
    matrixEl.className = "matrix-card";
    
    matrixEl.innerHTML = `
      <div class="matrix-header">
        <span>矩阵 ${mIdx + 1}</span>
        ${mIdx > 0 ? `<span class="remove-btn" data-action="remove-matrix" data-midx="${mIdx}">删除矩阵</span>` : ''}
      </div>
      <div class="config-form-group">
        <label>矩阵 ID (可空)</label>
        <input type="text" class="m-id" value="${matrix.matrix_id || ''}" placeholder="默认">
      </div>
      <div class="config-form-group">
        <label>矩阵名称 (可空)</label>
        <input type="text" class="m-name" value="${matrix.matrix_name || ''}" placeholder="默认名称">
      </div>
      <div class="config-form-group">
        <label>经营范围 (可空)</label>
        <input type="text" class="m-desc" value="${matrix.matrix_desc || ''}" placeholder="默认描述">
      </div>
      <div class="shops-container" id="shops-container-${mIdx}"></div>
      <div style="margin-top: 10px;">
        <button class="add-btn" data-action="add-shop" data-midx="${mIdx}">+ 添加店铺</button>
      </div>
    `;
    matricesContainer.appendChild(matrixEl);

    const shopsContainer = document.getElementById(`shops-container-${mIdx}`);
    if(!matrix.shops || matrix.shops.length === 0) {
      matrix.shops = [{ shop_id: "", shop_name: "", company: "", category: "", api_key: "" }];
    }
    matrix.shops.forEach((shop, sIdx) => {
      const shopEl = document.createElement("div");
      shopEl.className = "shop-card";
      shopEl.innerHTML = `
        <div class="shop-header">
          <span>店铺 ${sIdx + 1}</span>
          ${sIdx > 0 ? `<span class="remove-btn" data-action="remove-shop" data-midx="${mIdx}" data-sidx="${sIdx}">删除店铺</span>` : ''}
        </div>
        <div class="config-form-group">
          <label>店铺 ID *</label>
          <input type="text" class="s-id" value="${shop.shop_id || ''}" placeholder="必填">
        </div>
        <div class="config-form-group">
          <label>店铺名称 *</label>
          <input type="text" class="s-name" value="${shop.shop_name || ''}" placeholder="必填">
        </div>
        <div class="config-form-group">
          <label>公司名称(营业执照)</label>
          <input type="text" class="s-company" value="${shop.company || ''}" placeholder="如：XX科技有限公司（选填）">
        </div>
        <div class="config-form-group">
          <label>经营类目</label>
          <input type="text" class="s-cat" value="${shop.category || ''}" placeholder="如：奶嘴、手电筒（选填）">
        </div>
        <div class="config-form-group">
          <label>API Key *</label>
          <input type="text" class="s-key" value="${shop.api_key || ''}" placeholder="必填">
        </div>
      `;
      shopsContainer.appendChild(shopEl);
    });
  });
}

function syncConfigFromDOM() {
  const matrixCards = matricesContainer.querySelectorAll(".matrix-card");
  const newConfig = [];
  for (let i = 0; i < matrixCards.length; i++) {
    const mCard = matrixCards[i];
    const matrix = {
      matrix_id: mCard.querySelector(".m-id").value,
      matrix_name: mCard.querySelector(".m-name").value,
      matrix_desc: mCard.querySelector(".m-desc").value,
      shops: []
    };
    const shopCards = mCard.querySelectorAll(".shop-card");
    for (let j = 0; j < shopCards.length; j++) {
      const sCard = shopCards[j];
      matrix.shops.push({
        shop_id: sCard.querySelector(".s-id").value,
        shop_name: sCard.querySelector(".s-name").value,
        category: sCard.querySelector(".s-cat") ? sCard.querySelector(".s-cat").value : "",
        api_key: sCard.querySelector(".s-key").value
      });
    }
    newConfig.push(matrix);
  }
  currentConfig = newConfig;
}
window.removeMatrix = function(mIdx) {
  syncConfigFromDOM(); currentConfig.splice(mIdx, 1);
  renderConfigForm();
}
window.addShop = function(mIdx) {
  syncConfigFromDOM(); currentConfig[mIdx].shops.push({ shop_id: "", shop_name: "", api_key: "" });
  renderConfigForm();
}
window.removeShop = function(mIdx, sIdx) {
  syncConfigFromDOM(); currentConfig[mIdx].shops.splice(sIdx, 1);
  renderConfigForm();
}
addMatrixBtn.addEventListener("click", () => {
  syncConfigFromDOM(); currentConfig.push({
    matrix_id: "", matrix_name: "", matrix_desc: "",
    shops: [{ shop_id: "", shop_name: "", api_key: "" }]
  });
  renderConfigForm();
});


saveConfigBtn.addEventListener("click", () => {
  const matrixCards = matricesContainer.querySelectorAll(".matrix-card");
  const newConfig = [];
  const matrixIds = new Set();
  const shopIds = new Set();
  
  let hasError = false;

  for (let i = 0; i < matrixCards.length; i++) {
    const mCard = matrixCards[i];
    const mIdInput = mCard.querySelector(".m-id").value.trim();
    const mName = mCard.querySelector(".m-name").value.trim();
    const mDesc = mCard.querySelector(".m-desc").value.trim();
    
    let mId = mIdInput || "default";

    if (matrixIds.has(mId)) { 
      alert(`矩阵 ID 重复: ${mId}`); 
      hasError = true; 
      break; 
    }
    matrixIds.add(mId);

    const matrix = {
      matrix_id: mId,
      matrix_name: mName || "默认矩阵",
      matrix_desc: mDesc || "默认描述",
      shops: []
    };

    const shopCards = mCard.querySelectorAll(".shop-card");
    for (let j = 0; j < shopCards.length; j++) {
      const sCard = shopCards[j];
      const sId = sCard.querySelector(".s-id").value.trim();
      const sName = sCard.querySelector(".s-name").value.trim();
      const sKey = sCard.querySelector(".s-key").value.trim();
      const sCompany = sCard.querySelector(".s-company") ? sCard.querySelector(".s-company").value.trim() : "";
      const sCat = sCard.querySelector(".s-cat") ? sCard.querySelector(".s-cat").value.trim() : "";

      if (!sId || !sName || !sKey) {
        alert("店铺所有信息（ID、名称、API Key）不能为空！"); 
        hasError = true; 
        break;
      }
      if (shopIds.has(sId)) {
        alert(`店铺 ID 重复: ${sId}`); 
        hasError = true; 
        break;
      }
      shopIds.add(sId);

      matrix.shops.push({ shop_id: sId, shop_name: sName, company: sCompany, category: sCat, api_key: sKey });
    }
    
    if (hasError) break;
    newConfig.push(matrix);
  }

  if (hasError) return;

  // 保存：UI 内部 B 式 → 转换为 A 式落盘
  // 之前是发聊天消息给 LLM 让它写文件（不靠谱：LLM 可能没理解 / 没写 / 写错位置）。
  // 现在直接走 set_shops_matrix WS 通道让 gateway 写文件，跟 get_shops_matrix 对称
  const aSchema = formGroupingToASchema(newConfig);

  chrome.storage.local.set({ shopsMatrix: aSchema }, () => {
    chrome.runtime.sendMessage({ target: "background", action: "set_shops_matrix", shops: aSchema.shops }, (res) => {
      if (!res || !res.connected) {
        alert("OpenClaw 连接断开，无法保存到服务端");
        return;
      }
      if (!res.ok) {
        alert("保存到服务端失败: " + (res.error || "未知错误"));
        return;
      }
      // 走到这里说明 set_shops_matrix WS 发出去了，等回执 shops_matrix_saved
      // (回执在 onMessage 里处理，下面这里只是先关 overlay)
    });
    configOverlay.classList.remove("active");
  });
});

// 服务端写回执 — set_shops_matrix 后等的就是这个
window.addEventListener && document.addEventListener && chrome.runtime.onMessage.addListener((msg) => {
  if (msg && msg.type === "shops_matrix_saved") {
    if (msg.ok) {
      const userMsg = document.createElement("div");
      userMsg.className = "msg user";
      userMsg.textContent = `已保存 Ozon 店铺配置 (${msg.count || 0} 家店)`;
      const messagesEl = document.getElementById("messages");
      if (messagesEl) {
        messagesEl.appendChild(userMsg);
        messagesEl.scrollTop = messagesEl.scrollHeight;
      }
    } else {
      alert("服务端保存失败: " + (msg.error || "未知错误"));
    }
  }
});


// --- Image Upload Logic ---
function renderImagePreviews() {
  if (!imagePreviewArea) return;
  imagePreviewArea.innerHTML = '';
  if (pendingImages.length === 0) {
    imagePreviewArea.classList.add('hidden');
    return;
  }
  imagePreviewArea.classList.remove('hidden');
  pendingImages.forEach((src, idx) => {
    const div = document.createElement('div');
    div.className = 'image-preview-item';
    div.innerHTML = `<img src="${src}" /><button class="remove-btn" data-idx="${idx}">x</button>`;
    imagePreviewArea.appendChild(div);
  });
  imagePreviewArea.querySelectorAll('.remove-btn').forEach(btn => {
    btn.addEventListener('click', (e) => {
      const i = parseInt(e.target.dataset.idx);
      pendingImages.splice(i, 1);
      renderImagePreviews();
    });
  });
}

function handleFiles(files) {
  for (let i = 0; i < files.length; i++) {
    const file = files[i];
    if (!file.type.startsWith('image/')) continue;
    const reader = new FileReader();
    reader.onload = (e) => {
      pendingImages.push(e.target.result);
      renderImagePreviews();
    };
    reader.readAsDataURL(file);
  }
}

if (uploadBtn) {
  uploadBtn.addEventListener('click', () => fileInput.click());
  fileInput.addEventListener('change', (e) => {
    handleFiles(e.target.files);
    fileInput.value = '';
  });
}

if (inputEl) {
  inputEl.addEventListener('paste', (e) => {
    if (e.clipboardData && e.clipboardData.files.length > 0) {
      e.preventDefault();
      handleFiles(e.clipboardData.files);
    }
  });
}

// ─── 自定义模型配置 ───

const modelConfigBtn = document.getElementById("modelConfigBtn");
const modelOverlay   = document.getElementById("modelOverlay");
const modelCloseBtn  = document.getElementById("modelCloseBtn");
const modelList      = document.getElementById("modelList");
const modelAddForm   = document.getElementById("modelAddForm");
const modelAddOpenBtn    = document.getElementById("modelAddOpenBtn");
const modelAddCancelBtn  = document.getElementById("modelAddCancelBtn");
const modelAddConfirmBtn = document.getElementById("modelAddConfirmBtn");
const modelConfirmDialog = document.getElementById("modelConfirmDialog");
const modelConfirmText   = document.getElementById("modelConfirmText");
const modelConfirmNo     = document.getElementById("modelConfirmNo");
const modelConfirmYes    = document.getElementById("modelConfirmYes");

// 主流服务商 baseUrl + api 协议自动填充表（api 字段必须用 OpenClaw schema 合法值，
// 否则 OpenClaw 启动时 zod 校验失败拒启 —— 之前 2026-05-11 被 api:"google" 炸过一次）
const MODEL_URL_PRESETS = [
  { match: /gemini/i,          url: "https://generativelanguage.googleapis.com/v1beta/openai/", api: "openai-completions" },
  { match: /gpt|openai/i,      url: "https://api.openai.com/v1",                                 api: "openai-completions" },
  { match: /deepseek/i,        url: "https://api.deepseek.com/v1",                                api: "openai-completions" },
  { match: /qwen|dashscope/i,  url: "https://dashscope.aliyuncs.com/compatible-mode/v1",          api: "openai-completions" },
  { match: /moonshot|kimi/i,   url: "https://api.moonshot.cn/v1",                                 api: "openai-completions" },
  { match: /glm|chatglm/i,     url: "https://open.bigmodel.cn/api/paas/v4/",                      api: "openai-completions" },
  { match: /mistral/i,         url: "https://api.mistral.ai/v1",                                  api: "openai-completions" },
  { match: /claude/i,          url: "https://api.anthropic.com/v1",                               api: "anthropic-messages" },
  { match: /ollama/i,          url: "http://localhost:11434/v1",                                  api: "ollama" },
];

function autoFillBaseUrl(modelId) {
  const urlInput = document.getElementById("mf-base-url");
  const apiSelect = document.getElementById("mf-api-type");
  for (const preset of MODEL_URL_PRESETS) {
    if (preset.match.test(modelId)) {
      if (urlInput && !urlInput.value.trim()) urlInput.value = preset.url;
      // api 永远跟着模型名走（select.value 默认 openai-completions，不算"用户填过"）
      if (apiSelect) apiSelect.value = preset.api;
      break;
    }
  }
}

document.getElementById("mf-model-id").addEventListener("blur", (e) => {
  autoFillBaseUrl(e.target.value.trim());
});

// 渲染模型列表
function renderModelList(models) {
  modelList.innerHTML = "";
  // 缓存给删除按钮按 idx 索引时用（取 model_id 走 ws 删除）
  currentDisplayedModels = Array.isArray(models) ? models : [];
  // 渲染列表时一律重置 "添加" 按钮显示 + 隐藏新增表单（防止 add 表单残留 / 按钮失踪）
  if (modelAddForm) modelAddForm.classList.remove("active");
  if (modelAddOpenBtn) modelAddOpenBtn.style.display = "";
  if (!models || models.length === 0) {
    const empty = document.createElement("div");
    empty.className = "model-empty";
    empty.textContent = "暂无自定义模型配置";
    modelList.appendChild(empty);
    return;
  }
  models.forEach((m, idx) => {
    const item = document.createElement("div");
    item.className = "model-item";
    item.innerHTML = `
      <div class="model-item-info">
        <div class="model-item-name">${m.model_id}</div>
        <div class="model-item-url">${m.provider_base_url || "(系统模型)"}</div>
      </div>
      <button class="model-delete-btn" data-idx="${idx}" title="删除">🗑</button>
    `;
    modelList.appendChild(item);
  });
}

// 打开面板：走 ws 拉服务端 config.json，不再依赖 chrome.storage
modelConfigBtn.addEventListener("click", () => {
  renderModelList([{ model_id: "加载中...", provider_base_url: "" }]);
  modelAddForm.classList.remove("active");
  modelOverlay.classList.add("active");
  chrome.runtime.sendMessage({ target: "background", action: "get_custom_models" }, (res) => {
    if (!res || !res.connected) {
      renderModelList([]);
      alert("OpenClaw 未连接，无法拉取模型配置");
    }
    // 实际数据通过 chrome.runtime.onMessage 的 custom_models 推送回来
  });
});

// 关闭面板
modelCloseBtn.addEventListener("click", () => {
  modelOverlay.classList.remove("active");
});

// 打开新增表单
modelAddOpenBtn.addEventListener("click", () => {
  document.getElementById("mf-model-id").value = "";
  document.getElementById("mf-api-key").value = "";
  document.getElementById("mf-base-url").value = "";
  document.getElementById("mf-api-type").value = "openai-completions";
  modelAddForm.classList.add("active");
  modelAddOpenBtn.style.display = "none";
  document.getElementById("mf-model-id").focus();
});

// 取消新增
modelAddCancelBtn.addEventListener("click", () => {
  modelAddForm.classList.remove("active");
  modelAddOpenBtn.style.display = "";
});

// 确认新增 —— 走 ws 直接写服务端 config.json（不再用 fetch 127.0.0.1，那会写本机 OpenClaw 不到 GCP）
modelAddConfirmBtn.addEventListener("click", () => {
  const modelId    = document.getElementById("mf-model-id").value.trim();
  const apiKey     = document.getElementById("mf-api-key").value.trim();
  const baseUrl    = document.getElementById("mf-base-url").value.trim();
  const apiType    = document.getElementById("mf-api-type").value.trim() || "openai-completions";

  if (!modelId) { alert("请填写模型名称"); return; }
  if (!apiKey)  { alert("请填写 API Key"); return; }
  if (!baseUrl) { alert("请填写 Base URL"); return; }

  modelAddForm.classList.remove("active");
  modelAddOpenBtn.style.display = "";

  chrome.runtime.sendMessage({
    target: "background",
    action: "set_custom_model",
    model_id: modelId,
    provider_base_url: baseUrl,
    provider_api_key: apiKey,
    provider_api: apiType,
  }, (res) => {
    if (!res || !res.connected) {
      alert("OpenClaw 未连接，无法保存模型配置");
    }
    // 实际结果走 chrome.runtime.onMessage 的 custom_model_set 推回
  });
});

// 删除按钮事件代理 —— 走 ws 直接改服务端 config.json，不再依赖 chrome.storage
let pendingDeleteModelId = "";
let currentDisplayedModels = [];
modelList.addEventListener("click", (e) => {
  const btn = e.target.closest(".model-delete-btn");
  if (!btn) return;
  const idx = parseInt(btn.getAttribute("data-idx"), 10);
  const m = currentDisplayedModels[idx];
  if (!m) return;
  pendingDeleteModelId = m.model_id;
  modelConfirmText.textContent = `确认删除模型「${m.model_id}」的配置？删除后如该模型是当前 agent.model，会回退到 google/gemini-3-flash-preview。`;
  modelConfirmDialog.classList.add("active");
});

modelConfirmNo.addEventListener("click", () => {
  modelConfirmDialog.classList.remove("active");
  pendingDeleteModelId = "";
});

modelConfirmYes.addEventListener("click", () => {
  if (!pendingDeleteModelId) return;
  const targetId = pendingDeleteModelId;
  pendingDeleteModelId = "";
  modelConfirmDialog.classList.remove("active");
  chrome.runtime.sendMessage(
    { target: "background", action: "delete_custom_model", model_id: targetId },
    (res) => {
      if (!res || !res.connected) {
        alert("OpenClaw 未连接，无法删除");
      }
      // 实际结果走 chrome.runtime.onMessage 的 custom_model_deleted 推回
    }
  );
});
