(() => {
  "use strict";

  const APP_ID = "mabang-sku-image-exporter";
  const VERSION = "1.1.0";
  const state = {
    running: false,
    pages: 0,
    scannedRows: 0,
    results: [],
    seenKeys: new Set(),
    lastPageItems: [],
    logs: []
  };

  const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
  const clean = (value) => String(value || "").replace(/\s+/g, " ").trim();

  function log(message) {
    state.logs.unshift(`[${new Date().toLocaleTimeString()}] ${message}`);
    state.logs = state.logs.slice(0, 60);
    render();
  }

  function isVisible(el) {
    if (!el) return false;
    const style = getComputedStyle(el);
    const rect = el.getBoundingClientRect();
    return style.display !== "none" && style.visibility !== "hidden" && rect.width > 0 && rect.height > 0;
  }

  function toAbsUrl(value) {
    const raw = clean(value);
    if (!raw || raw === "#" || raw.startsWith("javascript:") || raw.startsWith("data:")) return "";
    try {
      return new URL(raw, location.href).href;
    } catch {
      return raw;
    }
  }

  function isProductImageUrl(url) {
    return Boolean(url && /stock-cos\.mabangerp\.com|\/\d+\/[^/?#]+\.(?:jpg|jpeg|png|webp|gif)/i.test(url));
  }

  function getImageUrl(el) {
    if (!el) return "";
    const attrs = ["src", "data-src", "data-original", "data-lazy-src", "data-url", "jqimg", "href"];
    for (const attr of attrs) {
      const url = toAbsUrl(el.getAttribute?.(attr));
      if (url && isProductImageUrl(url)) return url;
    }

    const styleValue = el.getAttribute?.("style") || "";
    const match = styleValue.match(/url\((["']?)(.*?)\1\)/i);
    const styleUrl = match?.[2] ? toAbsUrl(match[2]) : "";
    return isProductImageUrl(styleUrl) ? styleUrl : "";
  }

  function firstImage(scope) {
    if (!scope) return "";
    const selectors = [
      "span.photo img[src]",
      "span[data-field='photo'] img[src]",
      "img[src*='stock-cos']",
      "img[src]",
      "img[data-src]",
      "img[data-original]"
    ];

    for (const selector of selectors) {
      const nodes = Array.from(scope.querySelectorAll(selector));
      for (const node of nodes) {
        const url = getImageUrl(node);
        if (url) return url;
      }
    }

    const html = scope.innerHTML || "";
    const match = html.match(/https?:\/\/[^"'<> \t\r\n]+?\.(?:jpg|jpeg|png|webp|gif)(?:\?[^"'<> \t\r\n]*)?/i);
    return isProductImageUrl(match?.[0]) ? match[0] : "";
  }

  function normalizeSku(value) {
    const raw = clean(value);
    if (!raw) return "";
    const token = raw.match(/[A-Za-z0-9][A-Za-z0-9._\-\/]{2,80}/);
    return token?.[0] || "";
  }

  function stockLinkSelector() {
    return [
      "a[shopstock]",
      "a[shopStock]",
      "a[id^='Stock_']",
      "a[href*='stock.modify']",
      "a[href*='mod=stock.modify']"
    ].join(",");
  }

  function looksLikeSkuLink(link) {
    if (!link) return false;
    const text = clean(link.textContent || link.innerText);
    const href = link.getAttribute("href") || "";
    const id = link.id || "";
    return Boolean(
      /^Stock_\d+/.test(id) ||
      /stock\.modify|mod=stock\.modify/i.test(href) ||
      link.hasAttribute("shopstock") ||
      link.hasAttribute("shopStock") ||
      (/^[A-Za-z0-9][A-Za-z0-9._\-\/]{3,40}$/.test(text) && link.closest("#warehousestocklist,.warehouseStockList,.list-body,.panel"))
    );
  }

  function getStockLinks() {
    const direct = Array.from(document.querySelectorAll(stockLinkSelector()));
    const broad = Array.from(document.querySelectorAll("#warehousestocklist a, a[id^='Stock_'], a[href*='stock.modify']")).filter(looksLikeSkuLink);
    return [...new Set([...direct, ...broad])].filter((link) => normalizeSku(link.textContent || link.innerText));
  }

  function getInventoryRows() {
    const list = document.querySelector("#warehousestocklist");
    if (list) {
      const directRows = Array.from(list.children).filter((row) => {
        if (!row || row.nodeType !== 1) return false;
        return Boolean(
          row.querySelector(stockLinkSelector()) ||
          row.querySelector("span.photo img,img[src*='stock-cos']") ||
          clean(row.textContent).match(/[A-Za-z0-9][A-Za-z0-9._\-\/]{3,40}/)
        );
      });
      if (directRows.length) return directRows;
    }

    const rowCandidates = Array.from(document.querySelectorAll(
      "#warehousestocklist li, .list-body > li, .autoscrolllist .list-body > li, table tbody tr"
    )).filter((row) => row.querySelector?.(stockLinkSelector()));
    return [...new Set(rowCandidates)];
  }

  function skuFromRow(row) {
    const link = row.querySelector(stockLinkSelector()) ||
      Array.from(row.querySelectorAll("a")).find(looksLikeSkuLink);
    return normalizeSku(link?.textContent || link?.innerText || link?.getAttribute("title") || row.textContent);
  }

  function imageFromRow(row) {
    return firstImage(row);
  }

  function imageNearLink(link) {
    const li = link.closest("li");

    const sameCell = firstImage(li);
    if (sameCell) return sameCell;

    let node = li?.previousElementSibling || null;
    for (let step = 0; node && step < 35; step += 1, node = node.previousElementSibling) {
      if (node.querySelector(stockLinkSelector())) break;
      const url = firstImage(node);
      if (url) return url;
    }

    node = li?.nextElementSibling || null;
    for (let step = 0; node && step < 8; step += 1, node = node.nextElementSibling) {
      if (node.querySelector(stockLinkSelector())) break;
      const url = firstImage(node);
      if (url) return url;
    }

    const rowish = link.closest(".skulabel,.list-row,.row,ul,table,tr,div");
    return firstImage(rowish);
  }

  function extractCurrentPage() {
    const rows = getInventoryRows();
    let items = rows.map((row, index) => ({
      sku: skuFromRow(row),
      imgurl: imageFromRow(row),
      page: state.pages + 1,
      row: index + 1,
      url: location.href
    })).filter((item) => item.sku);

    if (!items.length) {
      const links = getStockLinks();
      items = links.map((link, index) => ({
        sku: normalizeSku(link.textContent || link.innerText || link.getAttribute("title")),
        imgurl: imageNearLink(link),
        page: state.pages + 1,
        row: index + 1,
        url: location.href
      })).filter((item) => item.sku);
    }

    state.scannedRows += items.length;
    state.lastPageItems = items.slice(0, 20);
    const pageKey = currentPageNumber() || pageSignature();
    for (const item of items) {
      const key = `${pageKey}::${item.row}::${item.sku}`;
      if (state.seenKeys.has(key)) continue;
      state.seenKeys.add(key);
      state.results.push(item);
    }

    render();
    return items;
  }

  function pageSignature() {
    const links = getStockLinks().slice(0, 8).map((link) => clean(link.textContent || link.innerText));
    const pageText = clean(document.body?.innerText || "").slice(-800);
    return `${location.href}::${links.join("|")}::${pageText}`;
  }

  function currentPageNumber() {
    const candidates = Array.from(document.querySelectorAll("a,button,li,span"));
    const active = candidates.find((el) => {
      const text = clean(el.textContent || el.innerText);
      const cls = String(el.className || "");
      return /^\d+$/.test(text) && /active|current|selected|on|hover|focus|layui-laypage-curr/i.test(cls);
    });
    if (active) return Number(clean(active.textContent || active.innerText));

    const colored = candidates.find((el) => {
      const text = clean(el.textContent || el.innerText);
      if (!/^\d+$/.test(text) || !isVisible(el)) return false;
      const color = getComputedStyle(el).color;
      return /rgb\(25,\s*103,\s*210\)|rgb\(64,\s*128,\s*255\)|rgb\(0,\s*123,\s*255\)|rgb\(30,\s*136,\s*229\)|rgb\(255,\s*0,\s*0\)|rgb\(220,\s*53,\s*69\)|rgb\(245,\s*108,\s*108\)/.test(color);
    });
    return colored ? Number(clean(colored.textContent || colored.innerText)) : 0;
  }

  function disabled(el) {
    const cls = String(el.className || "");
    const text = clean(el.textContent || el.innerText);
    return Boolean(
      el.disabled ||
      el.getAttribute("disabled") !== null ||
      el.getAttribute("aria-disabled") === "true" ||
      /disabled|disable|layui-disabled|paginationjs-disabled|page-disabled/i.test(cls) ||
      /没有下一页|末页/.test(text)
    );
  }

  function findNextButton() {
    const pageNo = currentPageNumber() || state.pages;
    if (pageNo) {
      const nextPage = Array.from(document.querySelectorAll("a,button,li,span")).find((el) => {
        return isVisible(el) && !disabled(el) && clean(el.textContent || el.innerText) === String(pageNo + 1);
      });
      if (nextPage) return nextPage;
    }

    const pageWrap = Array.from(document.querySelectorAll("div,ul,p,span")).find((el) => {
      const text = clean(el.textContent || el.innerText);
      return /每页|页码|跳转/.test(text) && Array.from(el.querySelectorAll("a,button,li,span")).some((child) => clean(child.textContent || child.innerText) === "1");
    });
    const scoped = pageWrap ? Array.from(pageWrap.querySelectorAll("a,button,li,span,i")) : [];
    const iconNext = scoped.find((el) => {
      if (!isVisible(el) || disabled(el)) return false;
      const text = clean(el.textContent || el.innerText);
      const cls = String(el.className || "");
      const title = el.getAttribute("title") || "";
      return /下一页|next/i.test(title) || /right|next|angle-right|chevron-right|arrow-right|page-next/i.test(cls) || text === ">";
    });
    if (iconNext) return iconNext.closest("a,button,li,span") || iconNext;

    const all = Array.from(document.querySelectorAll("a,button,li,span,div"));
    const candidates = all.filter((el) => {
      if (!isVisible(el) || disabled(el)) return false;
      const text = clean(el.textContent || el.innerText);
      const cls = String(el.className || "");
      const title = el.getAttribute("title") || "";
      const aria = el.getAttribute("aria-label") || "";
      return (
        /^(下一页|下页|Next|next|>|›|»)$/.test(text) ||
        /下一页|next/i.test(title) ||
        /下一页|next/i.test(aria) ||
        /\b(next|pagination-next|laypage-next|page-next)\b/i.test(cls)
      );
    });

    return candidates.find((el) => {
      const rect = el.getBoundingClientRect();
      return rect.top > 0 && rect.left >= 0;
    }) || null;
  }

  async function waitForPageChange(before) {
    const started = Date.now();
    while (Date.now() - started < 15000) {
      await sleep(400);
      if (pageSignature() !== before) return true;
    }
    return false;
  }

  async function start() {
    if (state.running) return;
    state.running = true;
    log("开始采集。");

    while (state.running) {
      const items = extractCurrentPage();
      state.pages += 1;
      log(`第 ${state.pages} 页：采集 ${items.length} 条，累计 ${state.results.length} 条记录。`);

      await sleep(3000);
      const next = findNextButton();
      if (!next) {
        log("没有找到下一页按钮，已停止。");
        break;
      }

      const before = pageSignature();
      next.scrollIntoView({ block: "center", inline: "center" });
      next.click();
      log("已点击下一页，等待列表变化。");
      await waitForPageChange(before);
      await sleep(1200);
    }

    state.running = false;
    render();
  }

  function pause() {
    state.running = false;
    log("已暂停。");
  }

  function reset() {
    state.running = false;
    state.pages = 0;
    state.scannedRows = 0;
    state.results = [];
    state.seenKeys.clear();
    state.lastPageItems = [];
    state.logs = [];
    render();
  }

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

  function download(format) {
    const rows = state.results.slice().sort((a, b) => {
      if (a.page !== b.page) return a.page - b.page;
      if (a.row !== b.row) return a.row - b.row;
      return String(a.sku).localeCompare(String(b.sku));
    });
    if (!rows.length) {
      log("没有可导出的数据。");
      return;
    }

    const stamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
    let content;
    let type;
    let ext;

    if (format === "json") {
      content = JSON.stringify(rows, null, 2);
      type = "application/json;charset=utf-8";
      ext = "json";
    } else {
      const header = ["sku", "imgurl", "page", "row", "url"];
      content = "\ufeff" + [header.join(","), ...rows.map((item) => header.map((key) => csvEscape(item[key])).join(","))].join("\n");
      type = "text/csv;charset=utf-8";
      ext = "csv";
    }

    const link = document.createElement("a");
    link.href = URL.createObjectURL(new Blob([content], { type }));
    link.download = `mabang_sku_imgurls_${stamp}.${ext}`;
    document.body.appendChild(link);
    link.click();
    URL.revokeObjectURL(link.href);
    link.remove();
  }

  function hasInventoryDom() {
    return Boolean(
      document.querySelector("#warehousestocklist") ||
      document.querySelector(stockLinkSelector()) ||
      document.querySelector("span.photo img,img[src*='stock-cos']") ||
      getInventoryRows().length
    );
  }

  function render() {
    const panel = document.getElementById(APP_ID);
    if (!panel) return;

    panel.querySelector("[data-pages]").textContent = state.pages;
    panel.querySelector("[data-count]").textContent = state.results.length;
    panel.querySelector("[data-rows]").textContent = state.scannedRows;
    panel.querySelector("[data-preview]").textContent = state.lastPageItems.length
      ? state.lastPageItems.map((item) => `${item.sku} => ${item.imgurl || "(无图片)"}`).join("\n")
      : `等待预览。\n库存行: ${getInventoryRows().length}\nSKU链接: ${getStockLinks().length}\n商品图: ${Array.from(document.querySelectorAll("span.photo img,img[src*='stock-cos']")).map(getImageUrl).filter(Boolean).length}`;
    panel.querySelector("[data-log]").textContent = state.logs.join("\n") || "等待操作。";
  }

  function createPanel() {
    if (document.getElementById(APP_ID)) return;
    const panel = document.createElement("div");
    panel.id = APP_ID;
    panel.innerHTML = `
      <style>
        #${APP_ID} {
          position: fixed;
          z-index: 2147483647;
          right: 18px;
          top: 18px;
          width: min(420px, calc(100vw - 36px));
          max-height: calc(100vh - 36px);
          display: grid;
          grid-template-rows: auto auto auto 1fr;
          border: 1px solid #c9d4e5;
          border-radius: 8px;
          background: #fff;
          color: #172033;
          box-shadow: 0 16px 44px rgba(15, 23, 42, .22);
          font: 13px/1.45 -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
          overflow: hidden;
        }
        #${APP_ID} * { box-sizing: border-box; }
        #${APP_ID} .mi-head {
          display: flex;
          justify-content: space-between;
          align-items: center;
          gap: 8px;
          padding: 10px 12px;
          background: #f7f9fc;
          border-bottom: 1px solid #e2e8f0;
          font-weight: 800;
        }
        #${APP_ID} .mi-close {
          border: 1px solid #cbd5e1;
          border-radius: 6px;
          background: #fff;
          width: 28px;
          height: 28px;
          cursor: pointer;
        }
        #${APP_ID} .mi-body {
          padding: 10px 12px;
          border-bottom: 1px solid #e2e8f0;
        }
        #${APP_ID} .mi-stats {
          display: grid;
          grid-template-columns: repeat(3, 1fr);
          gap: 8px;
          margin-bottom: 10px;
        }
        #${APP_ID} .mi-stat {
          border: 1px solid #dbe3ef;
          border-radius: 6px;
          padding: 7px;
          background: #f8fbff;
        }
        #${APP_ID} .mi-stat b {
          display: block;
          font-size: 18px;
          color: #1559bd;
          line-height: 1.1;
        }
        #${APP_ID} .mi-stat span {
          color: #64748b;
          font-size: 12px;
        }
        #${APP_ID} .mi-actions {
          display: flex;
          flex-wrap: wrap;
          gap: 7px;
        }
        #${APP_ID} button {
          min-height: 30px;
          border: 1px solid #cbd5e1;
          border-radius: 6px;
          padding: 5px 8px;
          background: #fff;
          color: #1f2937;
          cursor: pointer;
          font: inherit;
          font-weight: 700;
        }
        #${APP_ID} button.mi-primary {
          background: #1967d2;
          border-color: #1967d2;
          color: #fff;
        }
        #${APP_ID} button.mi-danger {
          color: #b42318;
        }
        #${APP_ID} .mi-preview,
        #${APP_ID} .mi-log {
          padding: 10px 12px;
          overflow: auto;
          font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
          font-size: 12px;
          white-space: pre-wrap;
        }
        #${APP_ID} .mi-preview {
          max-height: 170px;
          border-bottom: 1px solid #e2e8f0;
        }
        #${APP_ID} .mi-log {
          min-height: 110px;
          color: #334155;
        }
      </style>
      <div class="mi-head">
        <div>SKU 图片 URL 导出</div>
        <button class="mi-close" title="隐藏">×</button>
      </div>
      <div class="mi-body">
        <div class="mi-stats">
          <div class="mi-stat"><b data-pages>0</b><span>页数</span></div>
          <div class="mi-stat"><b data-count>0</b><span>记录</span></div>
          <div class="mi-stat"><b data-rows>0</b><span>扫描</span></div>
        </div>
        <div class="mi-actions">
          <button data-action="preview">预览当前页</button>
          <button class="mi-primary" data-action="start">开始翻页</button>
          <button data-action="pause">暂停</button>
          <button data-action="csv">导出 CSV</button>
          <button data-action="json">导出 JSON</button>
          <button class="mi-danger" data-action="reset">清空</button>
        </div>
      </div>
      <div class="mi-preview" data-preview></div>
      <div class="mi-log" data-log></div>
    `;

    panel.querySelector(".mi-close").onclick = () => {
      panel.style.display = "none";
    };
    panel.querySelector('[data-action="preview"]').onclick = () => {
      const items = extractCurrentPage();
      log(`预览当前页：${items.length} 条。`);
    };
    panel.querySelector('[data-action="start"]').onclick = start;
    panel.querySelector('[data-action="pause"]').onclick = pause;
    panel.querySelector('[data-action="csv"]').onclick = () => download("csv");
    panel.querySelector('[data-action="json"]').onclick = () => download("json");
    panel.querySelector('[data-action="reset"]').onclick = reset;
    document.documentElement.appendChild(panel);
    log(`插件已加载 v${VERSION}`);
    render();
  }

  function boot() {
    if (hasInventoryDom()) {
      createPanel();
      return;
    }

    const observer = new MutationObserver(() => {
      if (hasInventoryDom()) {
        observer.disconnect();
        createPanel();
      }
    });
    observer.observe(document.documentElement, { childList: true, subtree: true });
    setTimeout(() => observer.disconnect(), 30000);
  }

  boot();
})();
