// === Bulk price edit ===
function BulkEditScreen({ products, setProducts, productsSource,
                          allProducts, setAllProducts, allProductsLoading, allProductsProgress,
                          loadAllProducts, productsMeta }) {
  const { CATEGORIES } = window.MOCK;
  const isLive = productsSource === "live";

  // 라이브 모드에서는 페이지된 100개가 아니라 전체 상품(allProducts) 사용.
  // 아직 안 불러왔으면 자동으로 한 번 페치.
  const totalInStore = productsMeta?.totalElements || products.length;
  const workingSet = isLive ? (allProducts || products) : products;
  const isPartial = isLive && !allProducts && totalInStore > products.length;

  useEffect(() => {
    if (!isLive) return;
    if (allProducts) return;          // 이미 로드됨
    if (allProductsLoading) return;   // 로딩 중
    if (!loadAllProducts) return;
    // 전체 상품이 페이지 1개에 다 들어가면 굳이 페이징 안 함 (이미 products에 다 있음)
    if (totalInStore <= products.length) return;
    loadAllProducts().catch(err => {
      console.error("[bulk loadAllProducts]", err);
      toast("전체 상품 로드 실패: " + err.message);
    });
  }, [isLive, allProducts, allProductsLoading, totalInStore]);

  const [keyword, setKeyword] = useState("");
  const [selectedCats, setSelectedCats] = useState(new Set());
  const [target, setTarget] = useState("salePrice"); // "salePrice" | "discount"
  const [mode, setMode] = useState("percent"); // percent / fixed / set
  const [direction, setDirection] = useState("up");
  const [value, setValue] = useState(10);
  const [previewOpen, setPreviewOpen] = useState(false);
  const [naverSaving, setNaverSaving] = useState(false);
  const [saveProgress, setSaveProgress] = useState(null);
  const abortRef = useRef(false);

  // 라이브 모드면 카테고리 chip을 실제 상품 데이터에서 동적 생성 (전체 상품 기준)
  const effectiveCategories = useMemo(() => {
    if (!isLive) return CATEGORIES;
    const seen = new Map();
    workingSet.forEach(p => {
      if (p.category && !seen.has(p.category)) {
        seen.set(p.category, { id: p.category, name: p.category, icon: "" });
      }
    });
    return Array.from(seen.values()).sort((a, b) => a.name.localeCompare(b.name, "ko"));
  }, [workingSet, isLive]);

  const matched = useMemo(() => {
    return workingSet.filter(p => {
      if (keyword && !p.name.toLowerCase().includes(keyword.toLowerCase())) return false;
      if (selectedCats.size > 0 && !selectedCats.has(p.category)) return false;
      return true;
    });
  }, [workingSet, keyword, selectedCats]);

  // 일반화된 계산 — 판매가/즉시할인 양쪽에 사용. 네이버는 10원 단위만 받음.
  const calcNew = (oldValue) => {
    let nv;
    if (mode === "set") nv = value;
    else {
      const sign = direction === "up" ? 1 : -1;
      if (mode === "percent") nv = oldValue * (1 + sign * value / 100);
      else nv = oldValue + sign * value;
    }
    nv = Math.max(0, Math.round(nv / 10) * 10);  // 10원 단위 반올림
    return nv;
  };

  // 미리보기 행: target에 따라 다른 필드를 계산
  const previewRows = matched.map(p => {
    const oldPrice = p.price;
    const oldDiscount = oldPrice - (p.discountedPrice || oldPrice);
    if (target === "salePrice") {
      // 판매가 변경, 즉시할인은 유지 → 할인가는 자동 변동
      const newPrice = calcNew(oldPrice);
      const newDiscountedPrice = Math.max(0, newPrice - oldDiscount);
      return {
        ...p, oldPrice, newPrice, oldDiscount, newDiscount: oldDiscount,
        oldDiscountedPrice: p.discountedPrice || oldPrice, newDiscountedPrice,
        priceChanged: newPrice !== oldPrice, discountChanged: false,
      };
    }
    // discount 변경, 판매가는 유지 → 할인가도 함께 변동
    const newDiscount = Math.min(calcNew(oldDiscount), oldPrice); // 할인은 판매가 초과 불가
    const newDiscountedPrice = Math.max(0, oldPrice - newDiscount);
    return {
      ...p, oldPrice, newPrice: oldPrice, oldDiscount, newDiscount,
      oldDiscountedPrice: p.discountedPrice || oldPrice, newDiscountedPrice,
      priceChanged: false, discountChanged: newDiscount !== oldDiscount,
    };
  }).filter(p => p.priceChanged || p.discountChanged);

  const totalDelta = previewRows.reduce((s, p) => {
    // 매출 영향: 할인가 변동분 (실판매가) 기준
    return s + (p.newDiscountedPrice - p.oldDiscountedPrice);
  }, 0);

  const toggleCat = (id) => {
    const s = new Set(selectedCats);
    if (s.has(id)) s.delete(id); else s.add(id);
    setSelectedCats(s);
  };

  const apply = async () => {
    if (!isLive) {
      // mock 모드: 즉시 로컬 반영
      setProducts(prev => prev.map(p => {
        const m = previewRows.find(r => r.id === p.id);
        if (!m) return p;
        return { ...p, price: m.newPrice, discountedPrice: m.newDiscountedPrice };
      }));
      toast(`${previewRows.length}개 상품 수정 완료`);
      setPreviewOpen(false);
      return;
    }
    // 라이브 모드: 순차 PUT (target에 따라 salePrice 또는 immediateDiscount 변경)
    abortRef.current = false;
    setNaverSaving(true);
    const succeeded = [];
    const failed = [];
    setSaveProgress({ done: 0, total: previewRows.length, succeeded: [], failed: [] });
    for (let i = 0; i < previewRows.length; i++) {
      if (abortRef.current) break;
      const item = previewRows[i];
      try {
        if (!item.originProductNo) throw new Error("originProductNo 누락");
        const changes = {};
        if (item.priceChanged) changes.salePrice = item.newPrice;
        if (item.discountChanged) changes.immediateDiscount = item.newDiscount;
        await window.API.naverChangeProductPricing(item.originProductNo, changes);
        succeeded.push(item);
      } catch (err) {
        console.error("[bulk-saveToNaver]", item.id, err);
        failed.push({ ...item, error: err.message });
      }
      setSaveProgress({ done: i + 1, total: previewRows.length, succeeded: [...succeeded], failed: [...failed], aborted: abortRef.current });
    }
    // 성공한 것만 products + allProducts(전체 캐시)에 반영
    if (succeeded.length > 0) {
      const okIds = new Set(succeeded.map(x => x.id));
      const applyUpdate = (p) => {
        if (!okIds.has(p.id)) return p;
        const item = succeeded.find(x => x.id === p.id);
        return { ...p, price: item.newPrice, discountedPrice: item.newDiscountedPrice };
      };
      setProducts(prev => prev.map(applyUpdate));
      if (setAllProducts && allProducts) {
        setAllProducts(prev => prev ? prev.map(applyUpdate) : prev);
      }
    }
    setNaverSaving(false);
    if (failed.length === 0 && !abortRef.current) {
      toast(`${succeeded.length}개 가격이 네이버에 반영되었습니다`);
    } else if (abortRef.current) {
      toast(`중지됨 — ${succeeded.length}개 적용, ${previewRows.length - succeeded.length - failed.length}개 미적용`);
    } else {
      toast(`${succeeded.length}개 성공, ${failed.length}개 실패`);
    }
  };

  const abortBulk = () => { abortRef.current = true; };
  const closePreview = () => {
    if (naverSaving) return;
    setPreviewOpen(false);
    setSaveProgress(null);
  };

  return (
    <div className="page">
      <div className="page-header" style={{ display: "flex", alignItems: "flex-end", gap: 16 }}>
        <div style={{ flex: 1 }}>
          <h1 className="page-title">일괄 가격 수정</h1>
          <p className="page-desc">
            {isLive
              ? (allProductsLoading
                  ? `네이버 전체 상품 불러오는 중... ${allProductsProgress?.done || 0} / ${allProductsProgress?.total || "?"}개`
                  : allProducts
                    ? `네이버 라이브 전체 ${workingSet.length}개 상품에서 필터 → 미리보기 → 일괄 적용 (10원 단위 반올림)`
                    : `현재 ${products.length}개 표시 (전체 ${totalInStore}개 자동 로드 중)`)
              : "키워드와 카테고리로 상품을 필터링한 뒤 일괄로 가격을 변경합니다"}
          </p>
        </div>
        {isLive && (
          <button className="btn"
                  onClick={() => loadAllProducts && loadAllProducts().catch(err => toast("로드 실패: " + err.message))}
                  disabled={allProductsLoading}>
            <window.I.Refresh size={14} /> {allProductsLoading ? "로드 중..." : allProducts ? "다시 로드" : "전체 로드"}
          </button>
        )}
      </div>

      {isLive && allProductsLoading && (
        <div style={{
          marginBottom: 12, padding: "8px 14px", borderRadius: 8,
          background: "var(--accent-soft)", border: "1px solid var(--accent)",
          fontSize: 12, display: "flex", alignItems: "center", gap: 10
        }}>
          <div style={{ flex: 1 }}>
            네이버에서 전체 상품 불러오는 중 · 페이지 {allProductsProgress?.page || 1}/{allProductsProgress?.totalPages || "?"}
            <div style={{ height: 6, background: "var(--border)", borderRadius: 3, marginTop: 6, overflow: "hidden" }}>
              <div style={{
                height: "100%",
                width: allProductsProgress?.total
                  ? `${Math.min(100, (allProductsProgress.done / allProductsProgress.total) * 100)}%`
                  : "10%",
                background: "var(--accent)",
                transition: "width 0.3s",
              }} />
            </div>
          </div>
          <span className="tabular muted">{allProductsProgress?.done || 0}/{allProductsProgress?.total || "?"}</span>
        </div>
      )}

      <div style={{ display: "grid", gridTemplateColumns: "380px 1fr", gap: 12, alignItems: "flex-start" }} className="dash-grid">
        {/* Left: rule builder */}
        <div className="flex-col gap-3">
          <div className="card">
            <div className="card-header">
              <div className="card-title">1. 대상 상품 필터</div>
            </div>
            <div className="card-body" style={{ display: "flex", flexDirection: "column", gap: 14 }}>
              <Field label="상품명 키워드 포함">
                <div className="input-with-icon">
                  <window.I.Search size={14} className="input-icon" />
                  <input className="input" placeholder="예: 티셔츠, 청소기..." value={keyword} onChange={e => setKeyword(e.target.value)} />
                </div>
              </Field>
              <Field label={`카테고리 (다중 선택)${isLive ? " · 라이브 데이터에서 추출" : ""}`}>
                <div className="row gap-2" style={{ flexWrap: "wrap" }}>
                  {effectiveCategories.map(c => (
                    <button key={c.id} className={"chip " + (selectedCats.has(c.id) ? "active" : "")} onClick={() => toggleCat(c.id)}>
                      {c.icon} {c.name}
                    </button>
                  ))}
                </div>
                <div className="text-xs muted" style={{ marginTop: 6 }}>선택 안 함 = 전체 카테고리</div>
              </Field>
            </div>
          </div>

          <div className="card">
            <div className="card-header">
              <div className="card-title">2. 변경 방식</div>
            </div>
            <div className="card-body" style={{ display: "flex", flexDirection: "column", gap: 14 }}>
              <Field label="수정 대상">
                <div className="seg" style={{ width: "100%" }}>
                  <button className={"seg-btn " + (target === "salePrice" ? "active" : "")} onClick={() => setTarget("salePrice")} style={{ flex: 1 }}>판매가</button>
                  <button className={"seg-btn " + (target === "discount" ? "active" : "")} onClick={() => setTarget("discount")} style={{ flex: 1 }} disabled={!isLive} title={isLive ? "" : "라이브 모드 전용"}>
                    즉시할인 {!isLive && "(라이브만)"}
                  </button>
                </div>
              </Field>

              <Field label="수정 방식">
                <div className="seg" style={{ width: "100%" }}>
                  <button className={"seg-btn " + (mode === "percent" ? "active" : "")} onClick={() => setMode("percent")} style={{ flex: 1 }}>정률 (%)</button>
                  <button className={"seg-btn " + (mode === "fixed" ? "active" : "")} onClick={() => setMode("fixed")} style={{ flex: 1 }}>정액 (₩)</button>
                  <button className={"seg-btn " + (mode === "set" ? "active" : "")} onClick={() => setMode("set")} style={{ flex: 1 }}>고정값</button>
                </div>
              </Field>

              {mode !== "set" && (
                <Field label="방향">
                  <div className="seg" style={{ width: "100%" }}>
                    <button className={"seg-btn " + (direction === "up" ? "active" : "")} onClick={() => setDirection("up")} style={{ flex: 1 }}>
                      <window.I.ArrowUp size={12} /> {target === "discount" ? "할인↑ (실판매가↓)" : "인상"}
                    </button>
                    <button className={"seg-btn " + (direction === "down" ? "active" : "")} onClick={() => setDirection("down")} style={{ flex: 1 }}>
                      <window.I.ArrowDown size={12} /> {target === "discount" ? "할인↓ (실판매가↑)" : "인하"}
                    </button>
                  </div>
                </Field>
              )}

              <Field label={mode === "percent" ? "비율 (%)" : (target === "discount" ? "할인 금액 (₩)" : "금액 (₩)")}>
                <div className="row gap-2">
                  <input className="input tabular" type="number" value={value} onChange={e => setValue(parseInt(e.target.value) || 0)} />
                  {mode === "percent" && (
                    <div className="row gap-2">
                      {[5, 10, 15].map(v => (
                        <button key={v} className="btn btn-sm" onClick={() => setValue(v)}>{v}%</button>
                      ))}
                    </div>
                  )}
                </div>
              </Field>

              <div style={{ background: "var(--bg-elev)", padding: "10px 12px", borderRadius: 6, fontSize: 12 }}>
                <strong>요약:</strong>{" "}
                {target === "salePrice"
                  ? (mode === "set" ? `모든 판매가를 ${formatKRW(value)}로 통일` :
                     mode === "percent" ? `판매가의 ${value}% ${direction === "up" ? "인상" : "인하"} (10원 단위)` :
                     `판매가 ${formatKRW(value)} ${direction === "up" ? "인상" : "인하"}`)
                  : (mode === "set" ? `즉시할인을 ${formatNum(value)}원으로 통일 ${value === 0 ? "(할인 제거)" : ""}` :
                     mode === "percent" ? `즉시할인의 ${value}% ${direction === "up" ? "인상" : "인하"} (10원 단위)` :
                     `즉시할인 ${formatNum(value)}원 ${direction === "up" ? "인상" : "인하"}`)}
              </div>
            </div>
          </div>

          <button className="btn btn-primary btn-lg" disabled={matched.length === 0} onClick={() => setPreviewOpen(true)}>
            <window.I.Eye size={14} /> {matched.length}개 상품 미리보기 후 적용
          </button>
        </div>

        {/* Right: matched preview */}
        <div className="card">
          <div className="card-header">
            <div className="card-title">매칭된 상품 ({matched.length}{previewRows.length !== matched.length && ` · 변경 ${previewRows.length}`})</div>
            <div className="right text-sm muted">실판매가 변동 합계: <strong className="tabular" style={{ color: totalDelta >= 0 ? "var(--accent)" : "var(--danger)" }}>
              {totalDelta >= 0 ? "+" : ""}{formatKRW(totalDelta)}
            </strong></div>
          </div>
          <div style={{ maxHeight: 600, overflow: "auto" }}>
            {previewRows.length === 0 ? (
              <div className="empty">
                <window.I.Filter size={32} className="empty-icon" />
                <div>변경할 상품이 없습니다 (필터 결과 또는 변경값이 동일)</div>
              </div>
            ) : (
              <table className="table">
                <thead>
                  <tr>
                    <th>상품</th>
                    <th className="numeric">{target === "salePrice" ? "판매가" : "즉시할인"}</th>
                    <th className="numeric">실판매가</th>
                  </tr>
                </thead>
                <tbody>
                  {previewRows.map(p => {
                    const targetDiff = target === "salePrice" ? (p.newPrice - p.oldPrice) : (p.newDiscount - p.oldDiscount);
                    const dpDiff = p.newDiscountedPrice - p.oldDiscountedPrice;
                    return (
                      <tr key={p.id}>
                        <td>
                          <div className="product-cell">
                            <ProductThumb tone={p.tone} name={p.name} size={32} imageUrl={p.imageUrl} />
                            <div className="product-name" style={{ fontSize: 13 }}>{p.name}</div>
                          </div>
                        </td>
                        <td className="numeric" style={{ fontSize: 12 }}>
                          {target === "salePrice" ? (
                            <>
                              <div className="muted">{formatKRW(p.oldPrice)}</div>
                              <div style={{ fontWeight: 600 }}>→ {formatKRW(p.newPrice)}</div>
                            </>
                          ) : (
                            <>
                              <div className="muted">{formatNum(p.oldDiscount)}원</div>
                              <div style={{ fontWeight: 600 }}>→ {formatNum(p.newDiscount)}원</div>
                            </>
                          )}
                          <div className="tabular" style={{ color: targetDiff >= 0 ? "var(--accent)" : "var(--danger)", fontSize: 11 }}>
                            {targetDiff >= 0 ? "+" : ""}{formatNum(targetDiff)}
                          </div>
                        </td>
                        <td className="numeric" style={{ fontSize: 12 }}>
                          <div className="muted">{formatKRW(p.oldDiscountedPrice)}</div>
                          <div style={{ fontWeight: 600, color: "var(--accent)" }}>→ {formatKRW(p.newDiscountedPrice)}</div>
                          <div className="tabular" style={{ color: dpDiff >= 0 ? "var(--accent)" : "var(--danger)", fontSize: 11 }}>
                            {dpDiff >= 0 ? "+" : ""}{formatNum(dpDiff)}
                          </div>
                        </td>
                      </tr>
                    );
                  })}
                </tbody>
              </table>
            )}
          </div>
        </div>
      </div>

      {/* Confirm modal */}
      <Modal open={previewOpen} onClose={closePreview} title={isLive ? "네이버에 일괄 가격 변경 적용" : "일괄 가격 수정 확인"}
             size="lg"
             footer={
               !saveProgress ? (
                 <>
                   <button className="btn" onClick={closePreview}>취소</button>
                   <button className="btn btn-primary" onClick={apply} disabled={previewRows.length === 0}>
                     <window.I.Check size={14} /> {previewRows.length}개 {isLive ? "네이버에 적용" : "가격 수정 적용"}
                   </button>
                 </>
               ) : naverSaving ? (
                 <button className="btn btn-danger" onClick={abortBulk}>
                   <window.I.X size={14} /> 중지 ({saveProgress.done}/{saveProgress.total})
                 </button>
               ) : (
                 <button className="btn btn-primary" onClick={closePreview}>닫기</button>
               )
             }>
        {!saveProgress ? (
          <>
            <div style={{ padding: "12px 14px", background: "var(--accent-soft)", borderRadius: 8, marginBottom: 12 }}>
              <div style={{ fontWeight: 600, marginBottom: 4 }}>
                {previewRows.length}개 상품 · {target === "salePrice" ? "판매가 변경" : "즉시할인 변경"} (10원 단위)
              </div>
              <div className="text-sm muted">
                실판매가 변동 합계: <strong className="tabular" style={{ color: totalDelta >= 0 ? "var(--accent)" : "var(--danger)" }}>
                  {totalDelta >= 0 ? "+" : ""}{formatKRW(totalDelta)}
                </strong>
              </div>
            </div>
            <div style={{
              padding: "10px 12px", borderRadius: 6, marginBottom: 12,
              background: isLive ? "rgba(245, 158, 11, 0.08)" : "var(--bg-elev)",
              border: isLive ? "1px solid rgba(245, 158, 11, 0.35)" : "1px solid var(--border)",
              fontSize: 12, color: "var(--text-muted)", display: "flex", gap: 8, alignItems: "flex-start"
            }}>
              <window.I.AlertCircle size={14} stroke={isLive ? "var(--warning)" : "currentColor"} style={{ flexShrink: 0, marginTop: 1 }} />
              <span>
                {isLive
                  ? <><strong style={{ color: "var(--text)" }}>주의:</strong> 적용은 즉시 네이버 스토어에 반영됩니다. 순차 처리되며 중간에 "중지" 가능하지만 이미 적용된 항목은 자동 원복되지 않습니다. (10원 단위 정규화)</>
                  : "이 작업은 되돌릴 수 없습니다. 변경 후에도 30일간 가격 변경 이력에서 확인 가능."}
              </span>
            </div>
            {previewRows.length > 0 && (
              <div style={{ maxHeight: 280, overflow: "auto", border: "1px solid var(--border)", borderRadius: 6 }}>
                <table className="table">
                  <thead>
                    <tr>
                      <th>상품</th>
                      <th className="numeric">판매가</th>
                      <th className="numeric">즉시할인</th>
                      <th className="numeric">실판매가</th>
                    </tr>
                  </thead>
                  <tbody>
                    {previewRows.slice(0, 50).map(p => (
                      <tr key={p.id}>
                        <td style={{ maxWidth: 260, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }} title={p.name}>{p.name}</td>
                        <td className="numeric" style={{ fontSize: 12 }}>
                          {p.priceChanged ? (
                            <><div className="muted">{formatKRW(p.oldPrice)}</div><div style={{ fontWeight: 600 }}>→ {formatKRW(p.newPrice)}</div></>
                          ) : <span className="muted">{formatKRW(p.oldPrice)}</span>}
                        </td>
                        <td className="numeric" style={{ fontSize: 12 }}>
                          {p.discountChanged ? (
                            <><div className="muted">{formatNum(p.oldDiscount)}원</div><div style={{ fontWeight: 600 }}>→ {formatNum(p.newDiscount)}원</div></>
                          ) : <span className="muted">{formatNum(p.oldDiscount)}원</span>}
                        </td>
                        <td className="numeric" style={{ fontSize: 12 }}>
                          <div className="muted">{formatKRW(p.oldDiscountedPrice)}</div>
                          <div style={{ fontWeight: 600, color: "var(--accent)" }}>→ {formatKRW(p.newDiscountedPrice)}</div>
                        </td>
                      </tr>
                    ))}
                    {previewRows.length > 50 && (
                      <tr><td colSpan="4" className="muted text-sm" style={{ textAlign: "center", padding: 10 }}>
                        ... 외 {previewRows.length - 50}개 (전체 적용됩니다)
                      </td></tr>
                    )}
                  </tbody>
                </table>
              </div>
            )}
          </>
        ) : (
          <>
            <div style={{ marginBottom: 12 }}>
              <div style={{ display: "flex", justifyContent: "space-between", marginBottom: 6, fontSize: 13 }}>
                <span>{naverSaving ? "네이버에 적용 중..." : saveProgress.aborted ? "중지됨" : "완료"}</span>
                <span>{saveProgress.done} / {saveProgress.total}</span>
              </div>
              <div style={{ height: 8, background: "var(--border)", borderRadius: 4, overflow: "hidden" }}>
                <div style={{
                  height: "100%",
                  width: `${saveProgress.total ? (saveProgress.done / saveProgress.total * 100) : 0}%`,
                  background: "var(--accent)",
                  transition: "width 0.2s",
                }} />
              </div>
            </div>
            <div style={{ display: "flex", gap: 16, fontSize: 13, marginBottom: 12 }}>
              <span><strong style={{ color: "var(--success)" }}>{saveProgress.succeeded.length}</strong> 성공</span>
              <span><strong style={{ color: "var(--danger)" }}>{saveProgress.failed.length}</strong> 실패</span>
              {saveProgress.aborted && <span className="muted">· 사용자 중지</span>}
            </div>
            {saveProgress.failed.length > 0 && (
              <div style={{ marginTop: 8, padding: 10, background: "rgba(239, 68, 68, 0.05)", border: "1px solid rgba(239, 68, 68, 0.2)", borderRadius: 6, fontSize: 12, maxHeight: 200, overflow: "auto" }}>
                <div style={{ fontWeight: 600, marginBottom: 6 }}>실패 목록:</div>
                {saveProgress.failed.map(f => (
                  <div key={f.id} style={{ marginBottom: 6 }}>
                    <div style={{ color: "var(--text)" }}>{f.name}</div>
                    <div className="muted" style={{ paddingLeft: 8, fontSize: 11 }}>{f.error}</div>
                  </div>
                ))}
              </div>
            )}
          </>
        )}
      </Modal>
    </div>
  );
}

window.BulkEditScreen = BulkEditScreen;
