/* ============================================================
   바른셈 — 급여대장 / 급여명세서 / 회계 연계 export
   ============================================================ */
function Documents({ ctx }) {
  const { clients, statusOf, go, addAudit, toast, period, hrChangesOf, transition } = ctx;
  const client = clients.find((c) => c.id === (ctx.params.clientId || clients[0].id)) || clients[0];
  const st = statusOf(client.id);
  const canApprove = ctx.canApprove(client);
  const canReview = ctx.canReview;
  const [rejectOpen, setRejectOpen] = useState(false);
  const [rejectText, setRejectText] = useState("");
  const idx = window.BARUNSEM.FLOW.findIndex((f) => f.key === st);
  const previewMode = idx < 4; // 계산·확정 전 — 초안 미리보기(발송은 게이팅)
  const [tab, setTab] = useState("ledger");
  const [sent, setSent] = useState({});
  const [selId, setSelId] = useState(client.employees[0].id);
  useEffect(() => { setSent({}); setSelId(client.employees[0].id); }, [client.id]);

  const rs = ctx.rulesetFor(client.id);
  const runs = useMemo(() => client.employees.map((e) => ({ e, run: window.BARUNSEM.runFor(e, client, period.ym, rs) })), [client.id, period.ym, JSON.stringify([rs.npRate, rs.npHigh, rs.workHours, rs.roundPolicy])]);
  const tot = runs.reduce((a, r) => ({ gross: a.gross + r.run.gross, ded: a.ded + r.run.deductTotal, net: a.net + r.run.net }), { gross: 0, ded: 0, net: 0 });


  /* W2-3 확정 — 총액·인원·룰셋 버전을 보여주고 한 번 묻는다. 되돌리려면 권한 + 사유가 든다(C7). */
  const [confirmOpen, setConfirmOpen] = useState(false);
  const [uncOpen, setUncOpen] = useState(false);
  const [uncReason, setUncReason] = useState("");
  const snapNow = ctx.snapOf(client.id) || ctx.appliedRuleset();
  // 상신을 거친 건은 '2차 승인'으로, 자동승인·직접확정 건은 '급여 확정'으로 감사로그에 남긴다 (§08-A 2단 결재 구분).
  const confirm = () => { const r = st === "pending_approval" ? ctx.approveClient(client.id) : transition(client.id, "confirmed", { label: "급여 확정 — 명세서 발송 가능", toastText: "급여를 확정했습니다" }); if (r.ok) setConfirmOpen(false); };
  const unconfirm = () => {
    if (!uncReason.trim()) return;
    const r = ctx.unlockForRecalc(client.id, uncReason.trim());
    if (r && r.ok) { setUncOpen(false); setUncReason(""); }
  };
  const distributeAll = () => {
    const r = transition(client.id, "distributed", { label: `급여명세서 ${client.employees.length}건 일괄 발송 (이메일·SMS)`, toastText: `명세서 ${client.employees.length}건을 발송했습니다` });
    if (!r.ok) return;
    const all = {}; client.employees.forEach((e) => all[e.id] = true); setSent(all);
  };
  const sentCount = Object.values(sent).filter(Boolean).length;

  return (
    <div className="page page-wide rise">
      {previewMode && (
        <div className="card card-pad" style={{ display: "flex", alignItems: "center", gap: 13, marginBottom: 16, background: "var(--accent-soft)", border: "1px solid var(--accent-line)" }}>
          <Ms name="visibility" style={{ color: "var(--accent)", fontSize: 22, flex: "none" }} />
          <div style={{ flex: 1 }}>
            <div style={{ fontWeight: 650, fontSize: 13.5, color: "var(--accent-ink)" }}>발송 전 미리보기 — 급여대장·명세서 초안</div>
            <div style={{ fontSize: 12.5, color: "var(--accent-ink)", opacity: .85, marginTop: 2, lineHeight: 1.5 }}>
              아직 계산·확정 전입니다. 확정 후에만 명세서를 발송할 수 있으며, 지금 수치는 현재 입력 기준 <b>추정 초안</b>입니다.
            </div>
          </div>
          <button className="btn btn-primary btn-sm" onClick={() => go(idx < 3 ? "review" : "calc", { clientId: client.id })}>
            <Ms name="arrow_forward" />{idx < 3 ? "변동분 검수로" : "급여 계산으로"}
          </button>
        </div>
      )}
      <div className="seg" style={{ marginBottom: 18 }}>
        <button className={tab === "ledger" ? "on" : ""} onClick={() => setTab("ledger")}>급여대장</button>
        <button className={tab === "payslip" ? "on" : ""} onClick={() => setTab("payslip")}>급여명세서</button>
        <button className={tab === "accounting" ? "on" : ""} onClick={() => setTab("accounting")}>회계 연계</button>
      </div>

      {tab === "ledger" && (
        <div className="card">
          <CardHead icon="table_chart" title="급여대장" sub={`${client.name} · ${period.label}`}
            right={<div style={{ display: "flex", gap: 8 }}><button className="btn btn-default btn-sm" onClick={() => toast("급여대장 PDF를 생성했습니다")}><Ms name="picture_as_pdf" />PDF</button><button className="btn btn-default btn-sm" onClick={() => toast("Excel(.xlsx)로 내보냈습니다")}><Ms name="grid_on" />Excel</button></div>} />
          <div style={{ overflowX: "auto" }}>
            <table className="tbl tbl-cards" style={{ minWidth: 880 }}>
              <thead><tr><th>근로자</th><th>급여체계</th><th className="r">기본급</th><th className="r">수당</th><th className="r">가산수당</th><th className="r">총지급</th><th className="r">공제계</th><th className="r">실지급</th></tr></thead>
              <tbody>
                {runs.map(({ e, run }) => {
                  const allow = e.allowances.reduce((s, a) => s + a.amount, 0);
                  return (
                    <tr key={e.id} className="clickable" onClick={() => { setSelId(e.id); setTab("payslip"); }}>
                      <td className="tc-name"><div style={{ fontWeight: 650 }}>{e.name}</div><div className="muted" style={{ fontSize: 11.5 }}>{e.dept}</div></td>
                      <td data-label="급여체계"><SchemeTag scheme={e.scheme} /></td>
                      <td className="r num" data-label="기본급">{KRW(e.base)}</td>
                      <td className="r num" data-label="수당">{allow ? KRW(allow) : "—"}</td>
                      <td className="r num" data-label="가산수당">{run.premiumTotal ? KRW(run.premiumTotal) : "—"}</td>
                      <td className="r num" data-label="총지급" style={{ fontWeight: 600 }}>{KRW(run.gross)}</td>
                      <td className="r num" data-label="공제계" style={{ color: "var(--danger)" }}>−{KRW(run.deductTotal)}</td>
                      <td className="r num" data-label="실지급" style={{ fontWeight: 700 }}>{KRW(run.net)}</td>
                    </tr>
                  );
                })}
              </tbody>
              <tfoot><tr className="tbl-foot"><td className="tc-name" colSpan="5">합계 · {runs.length}명</td><td className="r num" data-label="총지급">{KRW(tot.gross)}</td><td className="r num" data-label="공제계" style={{ color: "var(--danger)" }}>−{KRW(tot.ded)}</td><td className="r num" data-label="실지급">{KRW(tot.net)}</td></tr></tfoot>
            </table>
          </div>
        </div>
      )}

      {tab === "payslip" && (
        <div className="l-master" style={{ "--master": "240px" }}>
          <div className="card">
            <CardHead icon="groups" title="명세서" sub={`발송 ${sentCount}/${runs.length}`} />
            <div className="pick-list" style={{ maxHeight: 560, overflowY: "auto" }}>
              {runs.map(({ e, run }) => (
                <button key={e.id} onClick={() => setSelId(e.id)} style={{ display: "flex", alignItems: "center", gap: 10, padding: "11px 16px", width: "100%", textAlign: "left", border: "none", borderBottom: "1px solid var(--line)", background: e.id === selId ? "var(--accent-soft)" : "transparent", cursor: "pointer", borderLeft: e.id === selId ? "3px solid var(--accent)" : "3px solid transparent" }}>
                  <div style={{ flex: 1 }}><div style={{ fontWeight: 650, fontSize: 13 }}>{e.name}</div><div className="muted num" style={{ fontSize: 11 }}>{KRW(run.net)}원</div></div>
                  {sent[e.id] ? <Ms name="mark_email_read" style={{ color: "var(--ok)", fontSize: 18 }} /> : <Ms name="schedule_send" style={{ color: "var(--ink-4)", fontSize: 18 }} />}
                </button>
              ))}
            </div>
          </div>
          <Payslip client={client} entry={runs.find((r) => r.e.id === selId)} period={period} sent={!!sent[selId]}
            snap={ctx.snapOf(client.id) || ctx.appliedRuleset()}
            canSend={st === "confirmed" || st === "distributed"}
            onSend={() => { setSent((s) => ({ ...s, [selId]: true })); toast("명세서를 발송했습니다", "ok"); }} />
        </div>
      )}

      {/* W1-8 — 4대보험 취득·상실·원천세 서식은 '신고 센터' 하나로 단일화했다.
          여기 있던 export 버튼은 토스트만 띄우는 가짜였고, 같은 산출물이 두 진입점에 있었다(B8·E3). */}
      {tab === "accounting" && <div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
        <div className="card card-pad" style={{ display: "flex", gap: 13, alignItems: "center", background: "var(--accent-soft)", border: "1px solid var(--accent-line)" }}>
          <Ms name="fact_check" style={{ color: "var(--accent)", fontSize: 22, flex: "none" }} />
          <div style={{ fontSize: 12.5, color: "var(--accent-ink)", lineHeight: 1.6, flex: 1 }}>
            <b>4대보험 취득·상실 · 원천징수이행상황 · 간이지급명세서</b>는 <b>신고 센터</b>에서 실제 파일로 내려받습니다. 이 탭은 회계사무소 전달용(더존 급여대장·전표)입니다.
          </div>
          <button className="btn btn-default btn-sm" onClick={() => go("filings", { clientId: client.id })}><Ms name="arrow_forward" />신고 센터로</button>
        </div>
        <AccountingExport client={client} runs={runs} ym={period.ym} confirmed={idx >= 6} toast={toast} onAudit={(msg) => addAudit(client.id, msg)} />
      </div>}

      {/* 워크플로 액션 바 — §08-A 2단 결재 */}
      <div className="card card-pad action-bar" style={{ display: "flex", alignItems: "center", gap: 16, marginTop: 18, position: "sticky", bottom: 16, boxShadow: "var(--sh-2)" }}>
        {st === "calculated" && (canApprove || client.autoApprove ? (<>
          <Ms name="rule" style={{ color: "var(--teal)", fontSize: 26 }} />
          <div style={{ flex: 1 }}><div style={{ fontWeight: 650 }}>계산 검토 완료 후 확정하세요{client.autoApprove ? " (자동승인)" : ""}</div><div className="muted" style={{ fontSize: 12.5 }}>{client.autoApprove ? "승인 단계 생략 — 검토 후 바로 확정합니다." : "확정 권한이 있어 바로 확정할 수 있습니다 (§8)."}</div></div>
          <button className="btn btn-primary btn-lg" disabled={!canReview} title={canReview ? "" : "노무사만 확정 가능"}
            onClick={() => setConfirmOpen(true)}>
            <Ms name="verified" />급여 확정
          </button>
        </>) : (<>
          <Ms name="how_to_reg" style={{ color: "var(--teal)", fontSize: 26 }} />
          <div style={{ flex: 1 }}><div style={{ fontWeight: 650 }}>1차 검수 완료 — 팀장 승인 상신</div><div className="muted" style={{ fontSize: 12.5 }}>확정은 팀장·대표의 2차 승인이 필요합니다. 상신하면 승인 대기열에 올라갑니다.</div></div>
          <button className="btn btn-primary btn-lg" disabled={!canReview} title={canReview ? "" : "상신은 노무사만 가능"}
            onClick={() => { if (!canReview) { toast("승인 상신은 노무사만 가능합니다"); return; } ctx.submitForApproval(client.id); }}>
            <Ms name="send" />승인 상신
          </button>
        </>))}
        {st === "pending_approval" && (canApprove ? (<>
          <Ms name="approval" style={{ color: "var(--warn)", fontSize: 26 }} />
          <div style={{ flex: 1 }}><div style={{ fontWeight: 650 }}>2차 승인 대기 — 담당 노무사가 상신했습니다</div><div className="muted" style={{ fontSize: 12.5 }}>계산 결과를 검토하고 승인하면 확정됩니다. 반려하면 담당 검수큐로 돌아갑니다.</div></div>
          <button className="btn btn-danger" onClick={() => setRejectOpen(true)}><Ms name="undo" />반려</button>
          <button className="btn btn-primary btn-lg" onClick={() => setConfirmOpen(true)}><Ms name="verified" />승인 · 급여 확정</button>
        </>) : (<>
          <Ms name="hourglass_top" style={{ color: "var(--warn)", fontSize: 26 }} />
          <div style={{ flex: 1 }}><div style={{ fontWeight: 650 }}>승인 대기 중 — 팀장·대표 승인 필요</div><div className="muted" style={{ fontSize: 12.5 }}>2차 승인 후 확정됩니다. 담당 노무사는 승인 결과를 기다립니다.</div></div>
          <span className="pill pill-warn" style={{ height: 30, fontSize: 13, padding: "0 14px" }}><Ms name="schedule" />승인 대기</span>
        </>))}
        {st === "confirmed" && (<>
          <Ms name="send" style={{ color: "var(--accent)", fontSize: 26 }} />
          <div style={{ flex: 1 }}><div style={{ fontWeight: 650 }}>확정됨 — 명세서 발송 가능</div><div className="muted" style={{ fontSize: 12.5 }}>이메일·SMS로 근로자에게 일괄 발송합니다 (사무직원 포함 전원 가능).</div></div>
          <button className="btn btn-default" disabled={!canApprove} title={canApprove ? "" : "확정 취소는 팀장·대표만 가능"} onClick={() => setUncOpen(true)}><Ms name="undo" />확정 취소</button>
          <button className="btn btn-primary btn-lg" onClick={distributeAll}>
            <Ms name="send" />명세서 일괄 발송 ({runs.length}건)
          </button>
        </>)}
        {st === "distributed" && (<>
          <Ms name="task_alt" style={{ color: "var(--ok)", fontSize: 26 }} />
          <div style={{ flex: 1 }}><div style={{ fontWeight: 650 }}>{period.label} 급여 마감 완료</div><div className="muted" style={{ fontSize: 12.5 }}>명세서 {runs.length}건 발송 · 신고 센터에서 서식 내려받기 가능</div></div>
          <button className="btn btn-default" onClick={() => go("filings", { clientId: client.id })}><Ms name="upload_file" />신고 센터 열기</button>
        </>)}
      </div>

      {/* W2-3 확정 확인 — 무엇을 확정하는지(총액·인원·룰셋 버전)를 보여주고 묻는다. 1클릭 확정은 되돌리기 비용이 크다(C7). */}
      {confirmOpen && (
        <Modal title="급여 확정" onClose={() => setConfirmOpen(false)}
          footer={<><button className="btn btn-default" onClick={() => setConfirmOpen(false)}>취소</button>
            <button className="btn btn-primary" onClick={confirm}><Ms name="verified" />확정</button></>}>
          <p style={{ marginBottom: 12 }}>확정하면 이 결과가 명세서·신고 산출물의 기준이 됩니다. 되돌리려면 <b>팀장·대표 권한과 사유</b>가 필요합니다.</p>
          <div className="dl" style={{ fontSize: 13 }}>
            <dt>고객사</dt><dd>{client.name}</dd>
            <dt>귀속월</dt><dd>{period.label}</dd>
            <dt>대상 인원</dt><dd className="num">{runs.length}명</dd>
            <dt>총지급액</dt><dd className="num" style={{ fontWeight: 700 }}>{KRW(tot.gross)}원</dd>
            <dt>공제 합계</dt><dd className="num">{KRW(tot.ded)}원</dd>
            <dt>실지급 합계</dt><dd className="num" style={{ fontWeight: 700 }}>{KRW(tot.net)}원</dd>
            <dt>적용 룰셋</dt><dd className="num">{snapNow ? `${snapNow.firmId} (표준 ${snapNow.stdId})` : window.BARUNSEM.RULESET.id}</dd>
          </div>
        </Modal>
      )}
      {uncOpen && (
        <Modal title="확정 취소" onClose={() => setUncOpen(false)}
          footer={<><button className="btn btn-default" onClick={() => setUncOpen(false)}>취소</button>
            <button className="btn btn-danger" disabled={!uncReason.trim()} onClick={unconfirm}><Ms name="undo" />확정 취소</button></>}>
          <p style={{ marginBottom: 10 }}>확정을 취소하면 <b>계산</b> 상태로 돌아갑니다. 사유는 감사로그에 남습니다.</p>
          <textarea className="input" style={{ height: 84, padding: 10, resize: "none", width: "100%" }} placeholder="예: 강남점 연장 3h 누락분 반영 위해 재계산" value={uncReason} onChange={(e) => setUncReason(e.target.value)} />
        </Modal>
      )}

      {rejectOpen && (
        <Modal title="승인 반려" onClose={() => setRejectOpen(false)}
          footer={<><button className="btn btn-default" onClick={() => setRejectOpen(false)}>취소</button><button className="btn btn-danger" onClick={() => { ctx.rejectApproval(client.id, rejectText.trim() || "재검토 요청"); setRejectOpen(false); setRejectText(""); }}><Ms name="undo" />반려 — 담당 검수큐로</button></>}>
          <p style={{ marginBottom: 10 }}><b>{client.name}</b> 급여 확정을 반려합니다. 상태가 <b>계산</b>으로 돌아가고 담당 노무사 검수큐에 재노출됩니다.</p>
          <textarea className="input" style={{ height: 84, padding: 10, resize: "none", width: "100%" }} placeholder="반려 사유 (예: 강남점 연장수당 재확인 후 재계산 요청)" value={rejectText} onChange={(e) => setRejectText(e.target.value)} />
        </Modal>
      )}
    </div>
  );
}

/* 급여명세서 (종이 문서 느낌) */
function Payslip({ client, entry, period, onSend, sent, canSend, snap }) {
  if (!entry) return null;
  const { e, run } = entry;
  // 항목별 산식은 커널(payslipFormula)에서 파생 — 근기법 §48② '계산 방법'. 화면이 식을 조립하지 않는다.
  const formula = (x) => window.BARUNSEM.payslipFormula(x, run, e);
  const H = run.hours;
  const hourRows = [
    ["소정근로시간", `${H.scheduled}h`], ["연장근로시간", `${H.overtime}h`], ["야간근로시간", `${H.night}h`],
    ["휴일근로시간", `${H.holiday}h`], ["휴일연장시간", `${H.holidayOver}h`], ["결근일수", `${H.absentDays}일`],
  ];
  const th = { padding: "5px 8px", background: "var(--surface-2)", fontWeight: 700, fontSize: 11.5, border: "1px solid var(--line)", textAlign: "left" };
  const td = { padding: "5px 8px", fontSize: 11.5, border: "1px solid var(--line)", verticalAlign: "top" };
  return (
    <div className="card" style={{ overflow: "hidden" }}>
      <div style={{ display: "flex", alignItems: "center", gap: 12, padding: "14px 22px", borderBottom: "1px solid var(--line)" }}>
        <Ms name="receipt_long" style={{ color: "var(--ink-3)" }} />
        <h3 style={{ fontSize: 14.5, fontWeight: 650 }}>급여명세서 미리보기</h3>
        <div style={{ flex: 1 }} />
        {sent ? <span className="pill pill-ok"><Ms name="check" />발송됨</span>
          : <button className="btn btn-primary btn-sm" disabled={!canSend} title={canSend ? "" : "확정 완료 + 전사총괄 권한 필요"} onClick={onSend}><Ms name="send" />발송</button>}
      </div>
      <div className="payslip-pad" style={{ padding: 28, background: "var(--surface-2)" }}>
        <div className="payslip-doc" style={{ background: "#fff", border: "1px solid var(--line-2)", borderRadius: 8, padding: "30px 34px", maxWidth: 600, margin: "0 auto", boxShadow: "var(--sh-1)", fontSize: 13 }}>
          <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", paddingBottom: 16, borderBottom: "2px solid var(--ink)" }}>
            <div><div style={{ fontSize: 19, fontWeight: 800, letterSpacing: "-0.02em" }}>급여명세서</div><div className="muted" style={{ fontSize: 12, marginTop: 2 }}>{period.label} 귀속 · 지급일 {period.payDate}</div></div>
            <div style={{ textAlign: "right" }}><div style={{ fontWeight: 700 }}>{client.name}</div><div className="muted" style={{ fontSize: 11.5 }}>사업자 {client.bizNo}</div></div>
          </div>
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "4px 24px", padding: "14px 0", borderBottom: "1px solid var(--line)", fontSize: 12.5 }}>
            <Row k="성명" v={e.name} /><Row k="부서/직위" v={`${e.dept} · ${e.title}`} />
            <Row k="입사일" v={e.joined || "—"} /><Row k="급여체계" v={e.scheme} />
          </div>
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 0, marginTop: 14 }}>
            <div style={{ paddingRight: 16 }}>
              <div style={{ fontWeight: 700, fontSize: 12, color: "var(--accent-ink)", marginBottom: 6 }}>지급 내역</div>
              {/* W3-4 — 근기법 §48② 임금명세서는 '계산 방법'을 적어야 한다. 시간(18h)만으로는 근로자가 검산할 수 없다. */}
              {run.earnings.map((x) => <Row key={x.key} k={x.name} v={KRW(x.amount)} small />)}
              <div style={{ display: "flex", justifyContent: "space-between", borderTop: "1px solid var(--line)", marginTop: 6, paddingTop: 6, fontWeight: 700 }}><span>지급계</span><span className="num">{KRW(run.gross)}</span></div>
            </div>
            <div style={{ paddingLeft: 16, borderLeft: "1px solid var(--line)" }}>
              <div style={{ fontWeight: 700, fontSize: 12, color: "var(--danger)", marginBottom: 6 }}>공제 내역</div>
              {run.deductions.map((x) => <Row key={x.key} k={x.name} v={KRW(x.amount)} small />)}
              <div style={{ display: "flex", justifyContent: "space-between", borderTop: "1px solid var(--line)", marginTop: 6, paddingTop: 6, fontWeight: 700 }}><span>공제계</span><span className="num">{KRW(run.deductTotal)}</span></div>
            </div>
          </div>
          <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginTop: 16, padding: "12px 16px", background: "var(--accent-soft)", borderRadius: 8 }}>
            <span style={{ fontWeight: 700, color: "var(--accent-ink)" }}>실지급액</span>
            <span className="num" style={{ fontSize: 20, fontWeight: 800, color: "var(--accent-ink)" }}>{KRW(run.net)}원</span>
          </div>
          {/* 근기법 §48② — 근로시간과 계산 방법이 명세서 안에 명시되어야 한다. 소항목 옆 작은 글씨로는 검산이 안 된다. */}
          <div style={{ marginTop: 16, fontWeight: 700, fontSize: 12, marginBottom: 6 }}>근로시간</div>
          <div style={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)", border: "1px solid var(--line)", borderRight: 0, borderBottom: 0 }}>
            {hourRows.map(([k, v]) => (
              <div key={k} style={{ display: "flex", justifyContent: "space-between", padding: "5px 8px", fontSize: 11.5, borderRight: "1px solid var(--line)", borderBottom: "1px solid var(--line)" }}>
                <span className="muted">{k}</span><span className="num" style={{ fontWeight: 600 }}>{v}</span>
              </div>
            ))}
          </div>
          <div style={{ marginTop: 14, fontWeight: 700, fontSize: 12, marginBottom: 6 }}>계산방법</div>
          <table style={{ width: "100%", borderCollapse: "collapse" }}>
            <thead><tr><th style={{ ...th, width: 108 }}>구분</th><th style={th}>산출식 또는 산출방법</th><th style={{ ...th, width: 96, textAlign: "right" }}>금액</th></tr></thead>
            <tbody>
              {run.earnings.map((x) => <tr key={x.key}><td style={td}>{x.name}</td><td style={td}>{formula(x)}</td><td className="num" style={{ ...td, textAlign: "right" }}>{KRW(x.amount)}</td></tr>)}
              {run.deductions.map((x) => <tr key={x.key}><td style={{ ...td, color: "var(--danger)" }}>{x.name}</td><td style={td}>{formula(x)}</td><td className="num" style={{ ...td, textAlign: "right" }}>−{KRW(x.amount)}</td></tr>)}
            </tbody>
          </table>
          <div className="muted" style={{ fontSize: 10.5, marginTop: 14, lineHeight: 1.6 }}>
            · 통상시급 {KRW(run.ordinaryHourly)}원 기준 산정 · 비과세 {KRW(run.nonTax)}원 · 적용 룰셋 {snap ? `${snap.firmId} (표준 ${snap.stdId})` : window.BARUNSEM.RULESET.id}<br />
            · 근로기준법 제48조에 따른 임금명세서 — 발급 한결노동법률사무소
          </div>
        </div>
        <div style={{ display: "flex", justifyContent: "center", gap: 10, marginTop: 18 }}>
          <span className="tag"><Ms name="mail" style={{ fontSize: 13 }} />{e.name.toLowerCase()}@example.com</span>
          <span className="tag"><Ms name="sms" style={{ fontSize: 13 }} />SMS 알림 발송</span>
        </div>
      </div>
    </div>
  );
}
function Row({ k, v, small, note }) {
  return (
    <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", gap: 8, padding: small ? "3px 0" : "2px 0" }}>
      <span className="muted" style={{ minWidth: 0 }}>{k}{note && <span style={{ display: "block", fontSize: 10, opacity: .75, lineHeight: 1.4 }}>{note}</span>}</span>
      <span className="num" style={{ fontWeight: 600, flex: "none" }}>{v}</span>
    </div>
  );
}

window.Documents = Documents;

/* ============================================================
   §07 회계사무소 전달 — 더존 급여대장 CSV + 회계전표(분개) CSV
   (spec-07-accounting-export.md). 확정 결과의 읽기전용 파생 · 자동 전송 금지(파일 생성까지).
   ============================================================ */
function AccountingExport({ client, runs, ym, confirmed, toast, onAudit }) {
  const B = window.BARUNSEM;
  const [view, setView] = useState("paybook");
  const pb = useMemo(() => B.douzonePaybook(runs, client), [client.id, runs.length, JSON.stringify(runs.map((x) => x.run.net))]);
  // F3 — 회사부담 4대보험을 포함할지는 고객사 회계정책이다. 빠지면 인건비가 실제보다 작게 잡힌다.
  const [withEmployer, setWithEmployer] = useState(!!(client.accountMap && client.accountMap.includeEmployerShare));
  const jrClient = { ...client, accountMap: { ...(client.accountMap || {}), includeEmployerShare: withEmployer, payDay: (client.accountMap && client.accountMap.payDay) || client.payDay } };
  const jr = useMemo(() => B.payrollJournal(runs, jrClient, ym), [client.id, runs.length, ym, withEmployer, JSON.stringify(runs.map((x) => x.run.net))]);

  const dl = (name, headers, rows, auditMsg) => {
    const csv = B.toCSV(headers, rows);
    const blob = new Blob(["\ufeff" + csv], { type: "text/csv;charset=utf-8" });
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a"); a.href = url; a.download = name; document.body.appendChild(a); a.click(); a.remove();
    setTimeout(() => URL.revokeObjectURL(url), 800);
    toast && toast("CSV를 내려받았습니다 · " + name, "ok");
    onAudit && onAudit(auditMsg);
  };
  const pbName = `더존급여대장_${client.short}_${ym}.csv`;
  const jrName = `회계전표_${client.short}_${ym}.csv`;
  const jcsv = B.journalCSV(jr);
  const KW = (n) => window.BARUNSEM.krw(n);

  return (
    <div className="card">
      <CardHead icon="account_balance" title="회계사무소 전달" sub="세무회계(더존) 업로드 양식 · 회계전표 분개 — 수기 재입력 없이 바로 전달"
        right={confirmed
          ? <span className="tag tag-ok" style={{ height: 22 }}><Ms name="lock_open" style={{ fontSize: 13 }} />확정 — 전달 가능</span>
          : <span className="tag tag-warn" style={{ height: 22 }}><Ms name="lock" style={{ fontSize: 13 }} />확정 후 활성</span>} />
      <div className="card-pad">
        <div className="seg" style={{ marginBottom: 14, maxWidth: 360 }}>
          <button className={view === "paybook" ? "on" : ""} onClick={() => setView("paybook")}>더존 급여대장</button>
          <button className={view === "journal" ? "on" : ""} onClick={() => setView("journal")}>회계전표(분개)</button>
        </div>

        {view === "paybook" ? (
          <div>
            <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 10, flexWrap: "wrap" }}>
              <span className="muted" style={{ fontSize: 12 }}>지급·공제 항목을 고객사 수당 구성대로 자동 컬럼화 — <b>{pb.payNames.length}개 지급 · {pb.dedNames.length}개 공제</b> 컬럼</span>
              <div style={{ flex: 1 }} />
              <button className="btn btn-primary btn-sm" disabled={!confirmed} title={confirmed ? "" : "확정 후 다운로드 가능"}
                onClick={() => dl(pbName, pb.headers, pb.rows, "회계사무소 전달 — 더존 급여대장 CSV (" + runs.length + "명)")}><Ms name="download" />더존 급여대장 CSV</button>
            </div>
            <div style={{ overflowX: "auto", border: "1px solid var(--line)", borderRadius: 10 }}>
              <table className="tbl" style={{ fontSize: 12, minWidth: 720, whiteSpace: "nowrap" }}>
                <thead><tr>{pb.headers.map((h, i) => <th key={i} className={i >= 3 ? "r" : ""} style={{ background: ["지급총액", "공제총액", "차인지급액"].includes(h) ? "var(--bg-sunken)" : null }}>{h}</th>)}</tr></thead>
                <tbody>
                  {pb.rows.slice(0, 6).map((r, i) => (
                    <tr key={i}>{r.map((c, j) => <td key={j} className={j >= 3 ? "r num" : (j === 0 ? "num muted" : "")} style={j === 1 ? { fontWeight: 600 } : null}>{j >= 3 ? KW(c) : c}</td>)}</tr>
                  ))}
                  {pb.rows.length > 6 && <tr><td colSpan={pb.headers.length} className="muted" style={{ textAlign: "center", padding: "9px 0" }}>… 외 {pb.rows.length - 6}명 (CSV에 전체 포함)</td></tr>}
                </tbody>
              </table>
            </div>
          </div>
        ) : (
          <div>
            <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 10, flexWrap: "wrap" }}>
              {jr.balanced
                ? <span className="tag tag-ok" style={{ height: 22 }}><Ms name="check_circle" style={{ fontSize: 13 }} />차·대 일치 {KW(jr.debit)}</span>
                : <span className="tag tag-danger" style={{ height: 22 }}><Ms name="error" style={{ fontSize: 13 }} />차변 {KW(jr.debit)} ≠ 대변 {KW(jr.credit)} (차이 {KW(jr.diff)})</span>}
              {jr.undefinedDepts.length > 0 && <span className="muted" style={{ fontSize: 11 }}>기본 비목 합산: {jr.undefinedDepts.join(", ")}</span>}
              <span className="tag"><Ms name="event" style={{ fontSize: 12 }} />지급일 {jr.payDate}</span>
              {jr.unionDues > 0 && <span className="tag tag-accent"><Ms name="groups" style={{ fontSize: 12 }} />조합비 예수금 분리 {KW(jr.unionDues)}</span>}
              <label style={{ display: "flex", alignItems: "center", gap: 6, fontSize: 11.5, cursor: "pointer" }}>
                <input type="checkbox" checked={withEmployer} onChange={(ev) => setWithEmployer(ev.target.checked)} />
                회사부담 4대보험 포함{jr.employerShare > 0 ? ` (${KW(jr.employerShare)})` : ""}
              </label>
              <div style={{ flex: 1 }} />
              <button className="btn btn-primary btn-sm" disabled={!confirmed} title={confirmed ? "" : "확정 후 다운로드 가능"}
                onClick={() => dl(jrName, jcsv.headers, jcsv.rows, "회계사무소 전달 — 회계전표 분개 CSV" + (jr.balanced ? " (차·대 일치)" : " (불균형 경고)"))}><Ms name="download" />회계전표 CSV</button>
            </div>
            {!jr.balanced && (
              <div style={{ display: "flex", gap: 8, alignItems: "center", marginBottom: 10, padding: "9px 12px", background: "var(--danger-soft)", border: "1px solid var(--danger-line)", borderRadius: 8 }}>
                <Ms name="warning" style={{ color: "var(--danger)", fontSize: 16 }} />
                <span style={{ fontSize: 11.5, color: "var(--danger)" }}>차·대 불일치 — 단수처리·기타공제 확인 필요. 다운로드는 가능하나 노무사 확인 후 전달하세요.</span>
              </div>
            )}
            <div style={{ overflowX: "auto", border: "1px solid var(--line)", borderRadius: 10 }}>
              <table className="tbl" style={{ fontSize: 12.5, minWidth: 520 }}>
                <thead><tr><th>일자</th><th style={{ width: 56 }}>차/대</th><th>코드</th><th>계정과목</th><th className="r">금액</th><th>적요</th></tr></thead>
                <tbody>
                  {jr.entries.map((x, i) => (
                    <tr key={i}>
                      <td className="num muted">{x.date}</td>
                      <td>{x.side === "debit" ? <span className="tag" style={{ height: 19, background: "var(--accent-soft)", color: "var(--accent-ink)", borderColor: "var(--accent-line)" }}>차변</span> : <span className="tag" style={{ height: 19 }}>대변</span>}</td>
                      <td className="num muted">{x.code}</td>
                      <td style={{ fontWeight: 600 }}>{x.account}</td>
                      <td className="r num" style={{ fontWeight: 600 }}>{KW(x.amount)}</td>
                      <td className="muted" style={{ fontSize: 11.5 }}>{x.memo}</td>
                    </tr>
                  ))}
                </tbody>
                <tfoot>
                  <tr className="tbl-foot"><td colSpan="4">차변 합계</td><td className="r num">{KW(jr.debit)}</td><td></td></tr>
                  <tr className="tbl-foot"><td colSpan="4">대변 합계</td><td className="r num" style={{ color: jr.balanced ? "var(--ok)" : "var(--danger)" }}>{KW(jr.credit)}</td><td className="muted" style={{ fontSize: 11 }}>{jr.balanced ? "복식부기 평형 ✓" : "불일치"}</td></tr>
                </tfoot>
              </table>
            </div>
          </div>
        )}

        <div style={{ display: "flex", gap: 9, alignItems: "flex-start", marginTop: 12, fontSize: 11.5, color: "var(--ink-3)", lineHeight: 1.55 }}>
          <Ms name="info" style={{ fontSize: 15, color: "var(--ink-4)", flex: "none", marginTop: 1 }} />
          <span>세무회계사무소 90%가 더존을 사용합니다. 확정 급여를 <b>업로드 CSV 1종</b>으로 내보내 협업 마찰을 없앱니다. 계정·부문 매핑은 고객사 설정(<span className="num">accountMap</span>), 금액은 계산 결과의 읽기전용 파생입니다. <b>자동 전송은 하지 않습니다</b> — 책임 경계.</span>
        </div>
      </div>
    </div>
  );
}
Object.assign(window, { AccountingExport });
