// === Logen courier shipping label / invoice ===
function ShippingScreen({ orders, setOrders, ordersSource, reloadOrders }) {
  const { PRODUCTS } = window.MOCK;
  const isLive = ordersSource === "live";
  const paidOrders = orders.filter(o => o.status === "paid");
  const [selected, setSelected] = useState(new Set(paidOrders.map(o => o.id)));
  const [previewMode, setPreviewMode] = useState("table"); // table / labels

  // 라이브에서 페이지 진입 시 paid가 0건이면 안내 — 자동 로드는 OrdersScreen에서 처리됨

  const orderTotal = (o) => {
    if (o.totalPaymentAmount) return o.totalPaymentAmount;
    return o.items.reduce((s, it) => {
      if (it.price) return s + it.price * it.qty;
      const p = PRODUCTS.find(x => x.id === it.pid);
      return s + (p ? p.price * it.qty : 0);
    }, 0);
  };

  const itemsLabel = (o) => o.items.map(it => {
    if (it.name) return `${it.name} ${it.qty}개`;
    const p = PRODUCTS.find(x => x.id === it.pid);
    return `${p?.name || it.pid} ${it.qty}개`;
  }).join(" + ");

  const toggleOne = (id) => {
    const s = new Set(selected);
    if (s.has(id)) s.delete(id); else s.add(id);
    setSelected(s);
  };
  const toggleAll = () => {
    if (selected.size === paidOrders.length) setSelected(new Set());
    else setSelected(new Set(paidOrders.map(o => o.id)));
  };

  const selectedOrders = paidOrders.filter(o => selected.has(o.id));

  // 발송 처리 모달 state
  const [dispatchOpen, setDispatchOpen] = useState(false);
  const [trackingMap, setTrackingMap] = useState({});  // pid -> trackingNumber
  const [bulkPaste, setBulkPaste] = useState("");
  const [deliveryCompanyCode, setDeliveryCompanyCode] = useState("KGB");
  const [dispatchSaving, setDispatchSaving] = useState(false);
  const [dispatchResult, setDispatchResult] = useState(null);

  // 로젠 API 발급 state
  const [logenOpen, setLogenOpen] = useState(false);
  const [logenStep, setLogenStep] = useState(null); // null | "registering" | "querying" | "done" | "error"
  const [logenResult, setLogenResult] = useState(null);
  const logenReady = window.API.logenConfigured && window.API.logenConfigured();

  const setTracking = (pid, value) => setTrackingMap(prev => ({ ...prev, [pid]: value }));
  const applyBulkPaste = () => {
    // 한 줄에 하나씩 송장번호. 선택된 주문 순서대로 매핑.
    const lines = bulkPaste.split(/\r?\n/).map(s => s.trim()).filter(Boolean);
    const mapping = {};
    selectedOrders.forEach((o, i) => {
      if (lines[i]) mapping[o.id] = lines[i];
    });
    setTrackingMap(prev => ({ ...prev, ...mapping }));
    setBulkPaste("");
    toast(`${Object.keys(mapping).length}개 송장번호 일괄 입력`);
  };

  const submitDispatch = async () => {
    const list = selectedOrders.map(o => ({
      productOrderId: o.productOrderId || o.id,
      trackingNumber: (trackingMap[o.id] || "").trim(),
      deliveryCompanyCode,
    })).filter(d => d.trackingNumber);
    if (list.length === 0) { toast("입력된 송장번호가 없습니다"); return; }
    if (list.length < selectedOrders.length) {
      if (!confirm(`송장번호가 ${selectedOrders.length - list.length}건 비어있습니다. 입력된 ${list.length}건만 처리할까요?`)) return;
    }
    setDispatchSaving(true);
    setDispatchResult(null);
    try {
      const result = await window.API.naverDispatchOrders(list);
      setDispatchResult({ ok: true, list, response: result });
      // 성공 시 로컬에서 해당 주문 status를 shipped로 (라이브는 사용자가 새로고침해서 다시 가져와도 됨)
      setOrders(prev => prev.map(o => {
        const matched = list.find(d => d.productOrderId === (o.productOrderId || o.id));
        if (!matched) return o;
        return { ...o, status: "shipped", trackingNumber: matched.trackingNumber };
      }));
      toast(`${list.length}건 발송 처리 완료`);
    } catch (err) {
      console.error("[dispatch]", err);
      setDispatchResult({ ok: false, error: err.message });
      toast("발송 처리 실패: " + err.message);
    } finally {
      setDispatchSaving(false);
    }
  };

  // 보내는분 정보 — 라이브 모드면 첫 주문의 takingAddress, 없으면 기본값
  const senderInfo = useMemo(() => {
    if (isLive) {
      const first = orders.find(o => o.sender);
      if (first?.sender) return first.sender;
    }
    return {
      name: "스마트팜",
      tel: "02-1234-5678",
      address: "서울시 송파구 위례성대로 1",
      zipcode: "",
    };
  }, [orders, isLive]);

  const printNow = () => {
    window.print();
  };

  const downloadXlsx = () => {
    // 로젠택배 일반 양식 (실제 양식 받아오면 컬럼 조정 가능)
    const headers = ["수령인명", "수령인전화번호", "수령인우편번호", "수령인주소", "품목명", "수량", "박스수량", "운임구분", "배송메시지", "주문번호"];
    const rows = selectedOrders.map(o => [
      o.buyer, o.phone, o.zipcode, o.address, itemsLabel(o), o.items.reduce((s, it) => s + it.qty, 0), 1, "선불", o.memo || "", o.productOrderId || o.id
    ]);
    const csv = "﻿" + [headers, ...rows].map(r => r.map(x => `"${String(x).replace(/"/g, '""')}"`).join(",")).join("\r\n");
    const blob = new Blob([csv], { type: "text/csv;charset=utf-8;" });
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    a.download = `LOGEN_송장_${new Date().toISOString().slice(0, 10)}.csv`;
    a.click();
    URL.revokeObjectURL(url);
    toast(`로젠 송장 양식 ${selectedOrders.length}건 다운로드 완료`);
  };

  // 로젠 API 송장 발급 워크플로우: 등록 → 송장번호 조회 → 출력 팝업
  const runLogenWorkflow = async () => {
    if (selectedOrders.length === 0) { toast("선택된 주문이 없습니다"); return; }
    setLogenOpen(true);
    setLogenResult(null);
    setLogenStep("registering");
    try {
      // STEP 1: 주문 등록 (복수건)
      const reg = await window.API.logenRegisterOrders(selectedOrders);
      const takeDt = reg.takeDt;
      const perOrder = reg.perOrder || [];
      const fixTakeNos = perOrder.filter(r => r.fixTakeNo).map(r => ({ fixTakeNo: r.fixTakeNo }));
      const failedReg = perOrder.filter(r => r.resultCd !== "TRUE");

      // STEP 2: 송장번호 조회 (등록된 fixTakeNo 기준)
      setLogenStep("querying");
      let slipMap = {};
      if (fixTakeNos.length > 0) {
        try {
          const inq = await window.API.logenInquirySlipNo(fixTakeNos);
          // 응답을 fixTakeNo → slipNo로 매핑
          const inqData = inq.data || inq.results || [];
          inqData.forEach(it => {
            const slipNo = (it.data1 || it.slips || [])[0]?.slipNo || it.slipNo;
            if (it.fixTakeNo && slipNo) slipMap[it.fixTakeNo] = slipNo;
          });
        } catch (err) {
          console.warn("[logen inquiry skip]", err.message);
        }
      }

      // STEP 3: 출력 팝업 URL (새 탭으로 열기는 사용자 액션 후 실행)
      let popupUrl = null;
      try {
        const popup = await window.API.logenGetPrintPopupUrl(takeDt);
        popupUrl = popup.url || popup.popupUrl || popup.data?.url || null;
      } catch (err) {
        console.warn("[logen popup URL skip]", err.message);
      }

      setLogenStep("done");
      setLogenResult({
        ok: true,
        takeDt,
        registered: perOrder,
        failedReg,
        slipMap,
        popupUrl,
        ordersById: Object.fromEntries(selectedOrders.map((o, i) => [o.id, perOrder[i]])),
      });
    } catch (err) {
      console.error("[logen workflow]", err);
      setLogenStep("error");
      setLogenResult({ ok: false, error: err.message });
    }
  };

  // 로젠에서 받은 송장번호를 발송 처리 모달의 trackingMap에 자동 매핑
  const applyLogenSlipNos = () => {
    if (!logenResult?.slipMap) return;
    const mapping = {};
    selectedOrders.forEach((o, i) => {
      const reg = logenResult.registered[i];
      const fixTakeNo = reg?.fixTakeNo;
      const slip = fixTakeNo && logenResult.slipMap[fixTakeNo];
      if (slip) mapping[o.id] = slip;
    });
    const cnt = Object.keys(mapping).length;
    if (cnt === 0) { toast("매핑할 송장번호가 없습니다"); return; }
    setTrackingMap(prev => ({ ...prev, ...mapping }));
    setLogenOpen(false);
    setDispatchOpen(true);
    setBulkPaste("");
    setDispatchResult(null);
    toast(`${cnt}건 송장번호 자동 입력 — 네이버 발송 처리 모달로 이동`);
  };

  const markShipped = () => {
    if (isLive) {
      // 라이브 모드: 송장번호 입력 모달 띄움
      setDispatchOpen(true);
      setTrackingMap({});
      setBulkPaste("");
      setDispatchResult(null);
      return;
    }
    setOrders(prev => prev.map(o => selected.has(o.id) ? { ...o, status: "shipped" } : o));
    setSelected(new Set());
    toast(`${selectedOrders.length}건 발송 처리되었습니다`);
  };

  return (
    <>
      <div className="page no-print">
        <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 ? "네이버 라이브 · " : ""}발송 대기 {paidOrders.length}건 · 로젠택배 표준 양식 (수령인/주소/연락처/품목/수량)
            </p>
            {isLive && senderInfo.tel !== "02-1234-5678" && (
              <p className="text-xs muted" style={{ marginTop: 4 }}>
                보내는분: <strong style={{ color: "var(--text)" }}>{senderInfo.name}</strong> ({senderInfo.tel}) · {senderInfo.address}
              </p>
            )}
          </div>
          <div className="row gap-2">
            {isLive && reloadOrders && (
              <button className="btn" onClick={() => reloadOrders({})} title="네이버에서 최신 주문 다시 조회">
                <window.I.Refresh size={14} /> 새로고침
              </button>
            )}
            <button className="btn" onClick={downloadXlsx} disabled={selected.size === 0}>
              <window.I.Download size={14} /> XLSX 다운로드
            </button>
            <button className="btn" onClick={printNow} disabled={selected.size === 0}>
              <window.I.Print size={14} /> 송장 인쇄
            </button>
            {logenReady && (
              <button className="btn"
                      style={{ background: "#fef3c7", color: "#92400e", borderColor: "#fcd34d" }}
                      onClick={runLogenWorkflow} disabled={selected.size === 0}
                      title="로젠 API로 주문 등록 + 송장번호 자동 발급 + 출력 팝업">
                <window.I.Truck size={14} /> 로젠 API 발급
              </button>
            )}
            <button className="btn btn-primary" onClick={markShipped} disabled={selected.size === 0}>
              <window.I.Check size={14} /> {selected.size}건 발송 처리
            </button>
          </div>
        </div>

        <div className="toolbar">
          <div className="row gap-2">
            <input type="checkbox" className="checkbox" checked={paidOrders.length > 0 && selected.size === paidOrders.length} onChange={toggleAll} />
            <span className="text-sm">전체 선택 ({selected.size}/{paidOrders.length})</span>
          </div>
          <div className="toolbar-divider" />
          <div className="seg">
            <button className={"seg-btn " + (previewMode === "table" ? "active" : "")} onClick={() => setPreviewMode("table")}>표</button>
            <button className={"seg-btn " + (previewMode === "labels" ? "active" : "")} onClick={() => setPreviewMode("labels")}>송장 라벨</button>
          </div>
          <div className="right text-sm muted">
            로젠택배 운임구분: 선불 · 박스 1박스 기본
          </div>
        </div>

        {paidOrders.length === 0 ? (
          <div className="card">
            <div className="empty">
              <window.I.Truck size={32} className="empty-icon" />
              <div style={{ marginBottom: 4 }}>발송 대기 중인 주문이 없습니다</div>
              <div className="text-sm">
                {isLive
                  ? "주문/판매 내역에서 기간을 늘려서 다시 조회해보세요 (PAYED 상태 주문만 표시)"
                  : "결제완료 상태의 주문이 여기에 표시됩니다"}
              </div>
            </div>
          </div>
        ) : previewMode === "table" ? (
          <div className="table-wrap">
            <table className="table">
              <thead>
                <tr style={{ background: "var(--accent-soft)" }}>
                  <th className="table-checkbox"></th>
                  <th>수령인명</th>
                  <th>전화번호</th>
                  <th>우편번호</th>
                  <th>주소</th>
                  <th>품목명</th>
                  <th className="numeric">수량</th>
                  <th>운임</th>
                  <th>배송메시지</th>
                  <th>주문번호</th>
                </tr>
              </thead>
              <tbody>
                {paidOrders.map(o => (
                  <tr key={o.id} className={selected.has(o.id) ? "selected" : ""}>
                    <td><input type="checkbox" className="checkbox" checked={selected.has(o.id)} onChange={() => toggleOne(o.id)} /></td>
                    <td style={{ fontWeight: 500 }}>{o.buyer}</td>
                    <td className="font-mono text-sm">{o.phone}</td>
                    <td className="font-mono text-sm">{o.zipcode}</td>
                    <td style={{ minWidth: 280, fontSize: 12 }}>{o.address}</td>
                    <td style={{ fontSize: 12, maxWidth: 300, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }} title={itemsLabel(o)}>
                      {itemsLabel(o)}
                    </td>
                    <td className="numeric tabular">{o.items.reduce((s, it) => s + it.qty, 0)}</td>
                    <td className="text-sm">선불</td>
                    <td className="text-xs muted" style={{ maxWidth: 160 }}>{o.memo || "-"}</td>
                    <td className="font-mono text-xs">{o.productOrderId || o.id}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        ) : (
          <div style={{ display: "grid", gridTemplateColumns: "repeat(2, 1fr)", gap: 12 }}>
            {selectedOrders.map(o => <InvoiceLabel key={o.id} order={o} itemsLabel={itemsLabel(o)} sender={senderInfo} />)}
          </div>
        )}
      </div>

      {/* 발송 처리 모달 (라이브) */}
      <Modal open={dispatchOpen} onClose={() => !dispatchSaving && setDispatchOpen(false)}
             size="lg" title="네이버 발송 처리 (송장번호 등록)"
             footer={
               dispatchResult?.ok ? (
                 <button className="btn btn-primary" onClick={() => { setDispatchOpen(false); setDispatchResult(null); }}>닫기</button>
               ) : (
                 <>
                   <button className="btn" onClick={() => setDispatchOpen(false)} disabled={dispatchSaving}>취소</button>
                   <button className="btn btn-primary" onClick={submitDispatch} disabled={dispatchSaving}>
                     <window.I.Check size={14} /> {dispatchSaving ? "처리 중..." : selectedOrders.length + "건 네이버에 발송 처리"}
                   </button>
                 </>
               )
             }>
        {dispatchResult ? (
          <div style={{ padding: 12, background: dispatchResult.ok ? "rgba(34,197,94,0.08)" : "rgba(239,68,68,0.08)", borderRadius: 6, border: "1px solid " + (dispatchResult.ok ? "rgba(34,197,94,0.35)" : "rgba(239,68,68,0.35)") }}>
            {dispatchResult.ok ? (
              <>
                <div style={{ fontWeight: 600, marginBottom: 6 }}>✓ {dispatchResult.list.length}건 발송 처리 완료</div>
                <div className="text-xs muted">네이버 응답:</div>
                <pre style={{ marginTop: 6, padding: 8, background: "var(--bg-elev)", borderRadius: 4, fontSize: 11, maxHeight: 200, overflow: "auto" }}>
                  {JSON.stringify(dispatchResult.response, null, 2)}
                </pre>
              </>
            ) : (
              <div style={{ color: "var(--danger)" }}>
                <strong>실패:</strong> {dispatchResult.error}
                <div className="text-xs muted" style={{ marginTop: 8 }}>
                  네이버 dispatch endpoint/payload 추측이 틀렸을 수 있습니다.
                  콘솔에서 _raw_post로 다양한 path 시도해서 정확한 spec 찾기:
                  <pre style={{ marginTop: 4, padding: 6, background: "var(--bg-elev)", borderRadius: 4, fontSize: 10 }}>
{`fetch(c.proxy_url+'/naver/_raw_post', {
  method:'POST', headers:{...,'Content-Type':'application/json'},
  body:JSON.stringify({_path:'/external/...', payload:{...}})
})`}
                  </pre>
                </div>
              </div>
            )}
          </div>
        ) : (
          <>
            <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12, marginBottom: 12 }}>
              <Field label="택배사">
                <select className="select" value={deliveryCompanyCode} onChange={e => setDeliveryCompanyCode(e.target.value)}>
                  <option value="KGB">로젠택배</option>
                  <option value="CJGLS">CJ대한통운</option>
                  <option value="HANJIN">한진택배</option>
                  <option value="EPOST">우체국택배</option>
                  <option value="LOTTE">롯데택배</option>
                </select>
              </Field>
              <Field label="발송일자">
                <input className="input" value={new Date().toISOString().slice(0, 10)} readOnly />
              </Field>
            </div>
            <Field label="송장번호 일괄 붙여넣기 (한 줄에 하나씩, 선택 순서대로 매핑)">
              <textarea className="input textarea" rows={3} value={bulkPaste} onChange={e => setBulkPaste(e.target.value)} placeholder="1234567890&#10;1234567891&#10;..." />
              <div className="row gap-2" style={{ marginTop: 6 }}>
                <button className="btn btn-sm" onClick={applyBulkPaste} disabled={!bulkPaste.trim()}>일괄 적용</button>
                <span className="text-xs muted" style={{ alignSelf: "center" }}>또는 아래 표에서 직접 입력</span>
              </div>
            </Field>
            <div style={{ maxHeight: 320, overflow: "auto", marginTop: 12, border: "1px solid var(--border)", borderRadius: 6 }}>
              <table className="table">
                <thead>
                  <tr>
                    <th>수령인</th>
                    <th style={{ maxWidth: 200 }}>상품</th>
                    <th>송장번호</th>
                  </tr>
                </thead>
                <tbody>
                  {selectedOrders.map(o => (
                    <tr key={o.id}>
                      <td>
                        <div style={{ fontWeight: 500 }}>{o.buyer}</div>
                        <div className="text-xs muted">{o.phone}</div>
                      </td>
                      <td style={{ maxWidth: 240, fontSize: 12, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }} title={itemsLabel(o)}>{itemsLabel(o)}</td>
                      <td>
                        <input className="input font-mono"
                               value={trackingMap[o.id] || ""}
                               onChange={e => setTracking(o.id, e.target.value.replace(/[^0-9-]/g, ""))}
                               placeholder="1234567890" />
                      </td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
            <div style={{ marginTop: 12, padding: "10px 12px", borderRadius: 6, background: "rgba(245, 158, 11, 0.08)", border: "1px solid rgba(245, 158, 11, 0.35)", fontSize: 12, color: "var(--text-muted)" }}>
              <window.I.AlertCircle size={14} stroke="var(--warning)" style={{ verticalAlign: "middle", marginRight: 6 }} />
              <strong style={{ color: "var(--text)" }}>주의:</strong> 발송 처리는 네이버에 즉시 반영되며 되돌릴 수 없습니다.
              dispatch endpoint/payload는 추측 기반 — 첫 시도 실패 가능성 있음 (실패 시 응답 보고 조정).
            </div>
          </>
        )}
      </Modal>

      {/* 로젠 API 발급 모달 */}
      <Modal open={logenOpen} onClose={() => { if (logenStep !== "registering" && logenStep !== "querying") setLogenOpen(false); }}
             size="lg" title="로젠택배 API · 송장 발급"
             footer={
               logenStep === "done" ? (
                 <>
                   <button className="btn" onClick={() => setLogenOpen(false)}>닫기</button>
                   {logenResult?.popupUrl && (
                     <a className="btn btn-primary"
                        href={logenResult.popupUrl}
                        target="_blank" rel="noopener noreferrer"
                        onClick={() => setLogenOpen(false)}>
                       <window.I.ExternalLink size={14} /> 로젠 출력 팝업 열기
                     </a>
                   )}
                   {Object.keys(logenResult?.slipMap || {}).length > 0 && (
                     <button className="btn btn-primary" onClick={applyLogenSlipNos}>
                       <window.I.Check size={14} /> 송장번호로 네이버 발송 처리
                     </button>
                   )}
                 </>
               ) : logenStep === "error" ? (
                 <button className="btn" onClick={() => setLogenOpen(false)}>닫기</button>
               ) : (
                 <button className="btn" disabled>처리 중...</button>
               )
             }>
        {!logenResult ? (
          <div style={{ padding: 20, textAlign: "center" }}>
            <div style={{ marginBottom: 12 }}>
              {logenStep === "registering" && "📦 로젠택배에 주문 등록 중..."}
              {logenStep === "querying" && "🔢 송장번호 조회 중..."}
            </div>
            <div style={{ height: 6, background: "var(--border)", borderRadius: 3, overflow: "hidden" }}>
              <div style={{
                height: "100%",
                width: logenStep === "registering" ? "40%" : logenStep === "querying" ? "80%" : "100%",
                background: "var(--accent)",
                transition: "width 0.3s",
              }} />
            </div>
          </div>
        ) : logenResult.ok ? (
          <>
            <div style={{ padding: "12px 14px", background: "rgba(34,197,94,0.08)", border: "1px solid rgba(34,197,94,0.35)", borderRadius: 6, marginBottom: 12 }}>
              <div style={{ fontWeight: 600, marginBottom: 4 }}>✓ 로젠 처리 완료</div>
              <div className="text-sm">
                접수일자: <strong>{logenResult.takeDt}</strong> ·
                등록 성공 <strong>{logenResult.registered.filter(r => r.resultCd === "TRUE").length}</strong> /
                실패 <strong style={{ color: "var(--danger)" }}>{logenResult.failedReg.length}</strong> ·
                채번 <strong>{Object.keys(logenResult.slipMap || {}).length}</strong>건
              </div>
            </div>

            {logenResult.failedReg.length > 0 && (
              <div style={{ marginBottom: 12, padding: 10, background: "rgba(239,68,68,0.06)", border: "1px solid rgba(239,68,68,0.3)", borderRadius: 6, fontSize: 12 }}>
                <div style={{ fontWeight: 600, marginBottom: 4 }}>등록 실패 ({logenResult.failedReg.length}건):</div>
                {logenResult.failedReg.slice(0, 5).map((f, i) => (
                  <div key={i} className="muted">· {f.resultMsg || f.sttsMsg || "(메시지 없음)"}</div>
                ))}
              </div>
            )}

            <div style={{ maxHeight: 280, overflow: "auto", border: "1px solid var(--border)", borderRadius: 6 }}>
              <table className="table">
                <thead>
                  <tr>
                    <th>수령인</th>
                    <th>로젠 주문번호</th>
                    <th>송장번호</th>
                    <th>상태</th>
                  </tr>
                </thead>
                <tbody>
                  {selectedOrders.map((o, i) => {
                    const reg = logenResult.registered[i] || {};
                    const slip = logenResult.slipMap[reg.fixTakeNo];
                    return (
                      <tr key={o.id}>
                        <td>
                          <div>{o.buyer}</div>
                          <div className="text-xs muted">{o.phone}</div>
                        </td>
                        <td className="font-mono text-xs">{reg.fixTakeNo || "—"}</td>
                        <td className="font-mono text-sm" style={{ fontWeight: 600 }}>{slip || "—"}</td>
                        <td>
                          {reg.resultCd === "TRUE"
                            ? <span className="badge badge-success">성공</span>
                            : <span className="badge badge-danger" title={reg.resultMsg}>실패</span>}
                        </td>
                      </tr>
                    );
                  })}
                </tbody>
              </table>
            </div>

            <div style={{ marginTop: 12, padding: "10px 12px", borderRadius: 6, background: "var(--accent-soft)", border: "1px solid var(--accent)", fontSize: 12 }}>
              <strong>다음 단계:</strong>
              <ol style={{ margin: "6px 0 0", paddingLeft: 18 }}>
                {logenResult.popupUrl
                  ? <li>"로젠 출력 팝업 열기" 클릭 → 새 탭에서 송장 인쇄</li>
                  : <li>로젠 사이트(<a href="https://logis.ilogen.com/" target="_blank" rel="noopener noreferrer">logis.ilogen.com</a>)에서 오늘 접수분 인쇄</li>}
                {Object.keys(logenResult.slipMap || {}).length > 0
                  ? <li>"송장번호로 네이버 발송 처리" 클릭 → 자동 매핑된 모달에서 확인 후 전송</li>
                  : <li>인쇄 완료 후 송장번호를 수동으로 네이버에 등록</li>}
              </ol>
            </div>
          </>
        ) : (
          <div style={{ padding: 12, background: "rgba(239,68,68,0.08)", border: "1px solid rgba(239,68,68,0.35)", borderRadius: 6 }}>
            <div style={{ fontWeight: 600, marginBottom: 6, color: "var(--danger)" }}>로젠 API 호출 실패</div>
            <div className="text-sm" style={{ whiteSpace: "pre-wrap" }}>{logenResult.error}</div>
            <div className="text-xs muted" style={{ marginTop: 8 }}>
              자격증명 확인 (API 설정 → 로젠) 또는 로젠 콜센터(1588-9988)에서 API 활성화 상태 확인하세요.
              개발 환경(topenapi)으로 먼저 테스트 후 운영 전환 권장.
            </div>
          </div>
        )}
      </Modal>

      {/* Print-only labels */}
      <div className="print-only">
        {selectedOrders.map(o => (
          <div key={o.id} className="invoice-page" style={{ padding: 24 }}>
            <InvoiceLabel order={o} itemsLabel={itemsLabel(o)} sender={senderInfo} />
          </div>
        ))}
      </div>
    </>
  );
}

function InvoiceLabel({ order, itemsLabel, sender }) {
  const orderRefNo = order.productOrderId || order.id;
  return (
    <div style={{
      background: "white", color: "black", border: "2px solid black", borderRadius: 4,
      padding: 14, fontFamily: "'Pretendard', sans-serif", fontSize: 12, position: "relative",
    }}>
      <div style={{ display: "flex", alignItems: "center", borderBottom: "2px solid black", paddingBottom: 8, marginBottom: 8 }}>
        <div style={{ background: "#e60012", color: "white", fontWeight: 800, padding: "4px 8px", fontSize: 14, marginRight: 8 }}>LOGEN</div>
        <div style={{ fontSize: 11 }}>로젠택배 주식회사 · 1588-9988</div>
        <div style={{ marginLeft: "auto", fontSize: 11 }}>운임: 선불</div>
      </div>
      <div style={{ display: "grid", gridTemplateColumns: "70px 1fr", gap: "4px 10px", fontSize: 11 }}>
        <div style={{ fontWeight: 600 }}>받는분</div>
        <div><strong style={{ fontSize: 14 }}>{order.buyer}</strong> &nbsp; <span style={{ fontFamily: "monospace" }}>{order.phone}</span></div>
        <div style={{ fontWeight: 600 }}>주소</div>
        <div>[{order.zipcode}] {order.address}</div>
        <div style={{ fontWeight: 600 }}>품목</div>
        <div>{itemsLabel}</div>
        <div style={{ fontWeight: 600 }}>주문번호</div>
        <div style={{ fontFamily: "monospace" }}>{orderRefNo}</div>
        {order.memo && <>
          <div style={{ fontWeight: 600 }}>메시지</div>
          <div>{order.memo}</div>
        </>}
      </div>
      <div style={{ marginTop: 10, paddingTop: 8, borderTop: "1px dashed #999", display: "flex", alignItems: "center" }}>
        <div style={{ fontSize: 10 }}>
          보내는분: {sender?.name || "—"} ({sender?.tel || "—"}) · {sender?.address || "—"}
        </div>
        <div style={{ marginLeft: "auto" }}>
          <svg width="100" height="32" viewBox="0 0 100 32">
            {[...Array(28)].map((_, i) => (
              <rect key={i} x={i * 3.5} y={0} width={Math.random() > 0.5 ? 1.5 : 2.5} height="32" fill="black" />
            ))}
          </svg>
        </div>
      </div>
    </div>
  );
}

window.ShippingScreen = ShippingScreen;
