/* ============================================================
   바른셈 — 신고 센터 (4대보험 취득·상실 · 원천세 · 간이지급명세서)
   Spec 03. 확정 계산결과의 읽기전용 파생. 생성·CSV export까지(자동 제출 금지).
   ============================================================ */
function Filings({ ctx }) {
  const B = window.BARUNSEM;
  const { clients, period } = ctx;
  const confirmed = ["confirmed", "distributed"].includes(ctx.statusOf(ctx.params.clientId || clients[0].id));
  const client = clients.find((c) => c.id === (ctx.params.clientId || clients[0].id)) || clients[0];
  const rs = ctx.rulesetFor(client.id);
  const hr = ctx.hrChangesOf(client.id);
  const joins = hr.filter((x) => x.kind === "join");
  const leaves = hr.filter((x) => x.kind === "leave");
  const TODAY = "2026-06-08";

  const runs = useMemo(() => client.employees.map((emp) => {
    const r = B.runFor(emp, client, period.ym, rs); return { ...r, e: emp };
  }), [client.id, JSON.stringify([rs.npRate, rs.workHours])]);
  const pick = (r, key) => { const d = (r.deductions || []).find((x) => x.key === key); return d ? d.amount : 0; };

  const wr = B.withholdingReport(runs, period.ym);
  const sps = B.simplePaymentStatement(runs, period.ym);
  const acqs = joins.map((j) => B.acquisitionReport({ id: j.id, name: j.name, joined: j.date, base: j.base || 0, allowances: [], insurance: { np: true, hi: true, ei: true, wc: true }, rrn: maskRrn(j.id) }, period.ym));
  const losses = leaves.map((l) => B.lossReport({ id: l.id, name: l.name, left: l.date, rrn: maskRrn(l.id) }, period.ym, 0));

  // W3-2 — 계산 전에는 이 화면의 숫자가 '추정'이다. 왜인지와 다음 행동을 말한다(E2).
  const flowIdx = window.BARUNSEM.FLOW.findIndex((f) => f.key === ctx.statusOf(client.id));
  const preCalc = flowIdx < 4;

  const [generated, setGenerated] = useState({});
  const [submitted, setSubmitted] = useState({});
  const [open, setOpen] = useState(null);

  const gen = (key) => { setGenerated((g) => ({ ...g, [key]: true })); setOpen(key); ctx.toast("신고 서식을 생성했습니다", "ok"); };
  const toggleSub = (key) => setSubmitted((s) => ({ ...s, [key]: !s[key] }));
  const download = (name, headers, rows) => {
    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);
    ctx.toast("CSV를 내려받았습니다 · " + name, "ok");
  };

  /* F1 반기납부 — 상시 20명 이하 + 승인 사업장은 1·7월에 반기분을 한 번에 낸다.
     매월 신고로만 보여주면 기한도 금액도 틀린다. 고객사 설정(halfYearWithholding)으로 분기. */
  const halfYear = !!client.halfYearWithholding;
  const hw = halfYear ? B.withholdingHalfYear(halfMonthlyReports(client, period.ym, wr), period.ym) : null;
  // F1 수정신고 — 원신고를 덮어쓰지 않고 증감만 신고한다.
  const AMEND_LABEL = { incomeTax: "소득세", localTax: "지방소득세", totalPay: "과세 지급액", headcount: "인원" };
  const [amendOpen, setAmendOpen] = useState(false);
  const [amend, setAmend] = useState({ incomeTax: "", localTax: "", reason: "" });
  // 수정 대상은 '실제로 낸 신고건'이다. 반기납부 사업장에서 월 기준을 쓰면 화면 KPI와 어긋난다.
  const amendBase = () => (client.halfYearWithholding && hw ? hw : wr);
  const amendResult = amendOpen ? (() => { const base = amendBase(); return B.amendedReport(base, {
    ...base,
    incomeTax: amend.incomeTax === "" ? base.incomeTax : Number(amend.incomeTax),
    localTax: amend.localTax === "" ? base.localTax : Number(amend.localTax),
  }, amend.reason); })() : null;

  // 신고 정의 (생성·export까지 — 자동 제출 금지)
  const defs = [
    {
      key: "wh", icon: "receipt_long",
      title: halfYear ? "원천징수이행상황신고서 (반기납부)" : "원천징수이행상황신고서",
      legal: halfYear ? `소득세령 §186 반기납부 · ${hw.from}~${hw.to}분` : "소득세법 · 지급월 다음 달 10일",
      due: halfYear ? hw.due : wr.due,
      countLabel: halfYear ? `${hw.months.length}개월분 합산 · 소득세 ${KRW(hw.incomeTax)}원` : `근로자 ${runs.length}명 · 총지급 ${KRW(wr.totalPay)}원`,
      enabled: runs.length > 0,
      csvName: `원천세${halfYear ? "_반기" : ""}_${client.short}_${period.ym}.csv`,
      csvHeaders: ["성명", "과세지급액", "소득세", "지방소득세"],
      csvRows: () => runs.map((r) => [r.e.name, r.taxable, pick(r, "it"), pick(r, "lt")]).concat([["합계", wr.totalPay, wr.incomeTax, wr.localTax]]),
      preview: () => (
        <div>
          {halfYear && (
            <div className="card card-pad" style={{ marginBottom: 12, display: "flex", gap: 11, alignItems: "center", background: "var(--accent-soft)", border: "1px solid var(--accent-line)" }}>
              <Ms name="calendar_month" style={{ color: "var(--accent)", fontSize: 20, flex: "none" }} />
              <div style={{ fontSize: 12.5, color: "var(--accent-ink)", lineHeight: 1.55 }}>
                <b>반기납부 승인 사업장</b> — {hw.half}기({hw.from}~{hw.to})분을 <b>{hw.due}</b>에 한 번에 신고·납부합니다. 매월 신고하지 않습니다.
              </div>
            </div>
          )}
          <div style={{ display: "flex", gap: 12, flexWrap: "wrap", marginBottom: 12 }}>
            <MiniKpi label="인원" value={wr.headcount + "명"} />
            <MiniKpi label="과세 지급액" value={KRW(wr.totalPay) + "원"} />
            <MiniKpi label="소득세 합계" value={KRW(halfYear ? hw.incomeTax : wr.incomeTax) + "원"} accent />
            <MiniKpi label="지방소득세" value={KRW(halfYear ? hw.localTax : wr.localTax) + "원"} />
          </div>
          <div className="muted" style={{ fontSize: 11.5, marginBottom: 10 }}>
            총지급액은 <b>비과세를 뺀 과세 지급액</b>입니다 (총액 {KRW(wr.grossPay)}원 − 비과세 {KRW(wr.nonTaxPay)}원).
          </div>
          <PreviewTable cols={["성명", "과세지급", "소득세", "지방세"]} align={[0, 1, 1, 1]}
            rows={runs.slice(0, 6).map((r) => [r.e.name, KRW(r.taxable), KRW(pick(r, "it")), KRW(pick(r, "lt"))])}
            more={runs.length > 6 ? runs.length - 6 : 0} />
          <div style={{ marginTop: 12 }}>
            <button className="btn btn-default btn-sm" onClick={() => setAmendOpen(true)}><Ms name="edit_note" />수정신고 작성</button>
          </div>
        </div>
      ),
    },
    {
      key: "sps", icon: "description", title: "간이지급명세서 (근로소득)", legal: "2026~ 매월 의무 · 지급월 다음 달 말일",
      due: sps.due, countLabel: `${sps.rows.length}명 · 인별 총지급+소득세`, enabled: sps.rows.length > 0,
      csvName: `간이지급명세서_${client.short}_${period.ym}.csv`,
      csvHeaders: ["성명", "총지급액", "소득세"],
      csvRows: () => sps.rows.map((r) => [r.name, r.totalPay, r.incomeTax]),   // 과세 지급액 기준(A12)
      preview: () => <PreviewTable cols={["성명", "총지급", "소득세"]} align={[0, 1, 1]}
        rows={sps.rows.slice(0, 6).map((r) => [r.name, KRW(r.totalPay), KRW(r.incomeTax)])}
        more={sps.rows.length > 6 ? sps.rows.length - 6 : 0} />,
    },
    {
      key: "acq", icon: "login", title: "4대보험 자격취득 신고", legal: "사유발생 다음 달 15일 · 입사자",
      due: acqs.length ? acqs[0].due : B.nextMonthDay(period.ym, 15), countLabel: joins.length ? `입사 ${joins.length}명` : "이번 달 입사 없음", enabled: acqs.length > 0,
      csvName: `4대보험취득_${client.short}_${period.ym}.csv`,
      csvHeaders: ["성명", "주민번호", "취득일", "국민연금", "건강보험", "고용·산재"],
      csvRows: () => acqs.map((a) => [a.name, a.rrn, a.acquireDate, a.wage.np, a.wage.hi, a.wage.ew]),
      preview: () => acqs.length ? (
        <PreviewTable cols={["성명", "취득일", "국민연금 보수월액", "건강 보수월액", "고용·산재 보수총액"]} align={[0, 0, 1, 1, 1]}
          rows={acqs.map((a) => [a.name, a.acquireDate, KRW(a.wage.np), KRW(a.wage.hi), KRW(a.wage.ew)])} />
      ) : <Empty icon="login" title="이번 달 입사자 없음" />,
      note: "보험별 보수월액 산입정의가 달라 따로 산출됩니다 (국민연금=비과세 제외·천원절사 / 건강=보수월액 / 고용·산재=보수총액).",
    },
    {
      key: "loss", icon: "logout", title: "4대보험 자격상실 신고", legal: "상실일 다음 달 15일 · 퇴사자",
      due: losses.length ? losses[0].due : B.nextMonthDay(period.ym, 15), countLabel: leaves.length ? `퇴사 ${leaves.length}명` : "이번 달 퇴사 없음", enabled: losses.length > 0,
      csvName: `4대보험상실_${client.short}_${period.ym}.csv`,
      csvHeaders: ["성명", "주민번호", "퇴사일", "상실일", "연간 보수총액"],
      csvRows: () => losses.map((l) => [l.name, l.rrn, l.leftDate, l.lossDate, l.yearWageTotal]),
      preview: () => losses.length ? (
        <div>
          <PreviewTable cols={["성명", "퇴사일", "상실일", "연간 보수총액"]} align={[0, 0, 0, 1]}
            rows={losses.map((l) => [l.name, l.leftDate, l.lossDate, l.yearWageTotal ? KRW(l.yearWageTotal) : "미산정"])} />
          <div style={{ display: "flex", gap: 7, alignItems: "center", marginTop: 10, padding: "8px 11px", background: "var(--warn-soft)", border: "1px solid var(--warn-line)", borderRadius: 8 }}>
            <Ms name="info" style={{ fontSize: 16, color: "color-mix(in oklab, var(--warn) 65%, black)" }} />
            <span style={{ fontSize: 11.5, color: "color-mix(in oklab, var(--warn) 50%, black)" }}>고용·산재 정산용 <b>연간 보수총액</b> 확정 후 입력해야 신고 완성 (퇴사자 보수총액 미산정 시 검수 신호 대상).</span>
          </div>
        </div>
      ) : <Empty icon="logout" title="이번 달 퇴사자 없음" />,
    },
  ];

  return (
    <div className="page page-wide rise">
      {preCalc && (
        <GateNotice icon="hourglass_top" title="아직 급여가 계산되지 않았습니다"
          body="여기 보이는 신고 금액은 현재 입력 기준 추정치입니다. 검수 → 잠금 → 급여 계산을 마치면 확정 결과로 갱신됩니다."
          actionLabel="변동분 검수로" onAction={() => ctx.go("review", { clientId: client.id })} />
      )}
      <div className="page-head" style={{ marginBottom: 14 }}>
        <div className="page-title" style={{ fontSize: 19 }}>신고 센터</div>
        <div className="page-desc">확정 급여에서 신고 서식을 자동 생성 — 매월 다른 도구로 다시 만들 필요 없음. 사업장관리번호 {client.managedNo} · 사업자 {client.bizNo} · <b style={{ color: "var(--accent-ink)" }}>{period.label}</b> 귀속</div>
      </div>

      {/* 신고 캘린더 — 기한 한눈에 */}
      <div className="card card-pad" style={{ marginBottom: 18 }}>
        <div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: 12 }}>
          <Ms name="event" style={{ color: "var(--accent)", fontSize: 20 }} />
          <h3 style={{ fontSize: 14, fontWeight: 650 }}>이번 달 신고 기한</h3>
          <span className="muted" style={{ fontSize: 12 }}>주말·공휴일 자동 보정 (영업일)</span>
        </div>
        <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(180px, 1fr))", gap: 12 }}>
          {defs.map((d) => <DueChip key={d.key} title={d.title} due={d.due} today={TODAY} done={submitted[d.key]} muted={!d.enabled} />)}
        </div>
      </div>

      {/* 신고별 카드 */}
      <div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
        {defs.map((d) => {
          const isGen = generated[d.key];
          const isSub = submitted[d.key];
          const isOpen = open === d.key;
          return (
            <div key={d.key} className="card" style={{ opacity: d.enabled ? 1 : .72 }}>
              <div className="card-hd">
                <Ms name={d.icon} />
                <div className="card-hd-main">
                  <h3>{d.title}</h3>
                  <span className="card-hd-sub">{d.legal} · {d.countLabel}</span>
                </div>
                <div className="card-hd-right" style={{ display: "flex", alignItems: "center", gap: 8 }}>
                  {isSub ? <span className="pill pill-ok"><Ms name="task_alt" style={{ fontSize: 13 }} />제출 표시</span>
                    : isGen ? <span className="pill pill-info"><Ms name="draft" style={{ fontSize: 13 }} />생성됨</span>
                      : <span className="pill pill-gray"><Ms name="radio_button_unchecked" style={{ fontSize: 13 }} />미생성</span>}
                  {!isGen
                    ? <button className="btn btn-primary btn-sm" disabled={!d.enabled} onClick={() => gen(d.key)}><Ms name="auto_awesome" />생성</button>
                    : <>
                        <button className="btn btn-default btn-sm" onClick={() => setOpen(isOpen ? null : d.key)}><Ms name={isOpen ? "expand_less" : "expand_more"} />{isOpen ? "접기" : "미리보기"}</button>
                        <button className="btn btn-default btn-sm" onClick={() => download(d.csvName, d.csvHeaders, d.csvRows())}><Ms name="download" />CSV</button>
                        <button className={"btn btn-sm " + (isSub ? "btn-default" : "btn-primary")} onClick={() => toggleSub(d.key)}><Ms name={isSub ? "undo" : "check"} />{isSub ? "표시 해제" : "제출 표시"}</button>
                      </>}
                </div>
              </div>
              {isGen && isOpen && (
                <div className="card-pad" style={{ paddingTop: 4 }}>
                  {d.note && <div className="muted" style={{ fontSize: 11.5, marginBottom: 10, lineHeight: 1.5 }}><Ms name="info" style={{ fontSize: 14, verticalAlign: "-2px", color: "var(--accent)" }} /> {d.note}</div>}
                  {d.preview()}
                </div>
              )}
            </div>
          );
        })}
      </div>

      <AccountingExport client={client} runs={runs.map((x) => ({ e: x.e, run: x }))} ym={period.ym} confirmed={confirmed} toast={ctx.toast} onAudit={(msg) => ctx.addAudit(client.id, msg)} />

      <div className="card card-pad" style={{ marginTop: 16, display: "flex", gap: 11, alignItems: "flex-start", background: "var(--bg-sunken)" }}>
        <Ms name="shield" style={{ fontSize: 19, color: "var(--ink-4)", flex: "none", marginTop: 1 }} />
        <div style={{ fontSize: 12, color: "var(--ink-3)", lineHeight: 1.6 }}>
          신고값은 확정된 계산 결과의 <b>읽기전용 파생</b>입니다 — 신고 생성이 급여 수치를 바꾸지 않습니다. 공단·국세청 <b>자동 제출은 하지 않으며</b>, 생성·CSV export 후 노무사가 직접 제출하고 "제출 표시"로 마킹합니다. 주민번호는 화면에서 마스킹되고 CSV에만 포함됩니다.
        </div>
      </div>
      {/* F1 수정신고 — 원신고를 덮어쓰지 않고 증감만 신고한다. 무엇이 왜 바뀌었는지가 남아야 방어가 된다. */}
      {amendOpen && (
        <Modal title="원천세 수정신고" onClose={() => setAmendOpen(false)}
          footer={<><button className="btn btn-default" onClick={() => setAmendOpen(false)}>닫기</button>
            <button className="btn btn-primary" disabled={!amendResult || !amendResult.changed.length || !amend.reason.trim()}
              onClick={() => { download(`원천세_수정신고_${client.short}_${period.ym}.csv`, ["구분", "원신고", "수정신고", "증감"],
                amendResult.changed.map((k) => [AMEND_LABEL[k] || k, amendResult.original[k], amendResult.revised[k], amendResult.delta[k]])
                  .concat([["수정 사유", "", "", amend.reason]])); setAmendOpen(false); }}>
              <Ms name="download" />수정신고서 CSV</button></>}>
          <p style={{ marginBottom: 12 }}>
            {client.halfYearWithholding ? <><b>반기({hw.from}~{hw.to})분</b> 신고건을 수정합니다. </> : <><b>{period.label}분</b> 신고건을 수정합니다. </>}
            원신고를 덮어쓰지 않고 <b>증감분</b>을 신고합니다 — 무엇이 왜 바뀌었는지가 남아야 가산세 다툼에서 방어가 됩니다.
          </p>
          <div className="form-2">
            <div className="field"><label>소득세 (원신고 {KRW(amendBase().incomeTax)})</label>
              <input className="input num" placeholder={String(amendBase().incomeTax)} value={amend.incomeTax}
                onChange={(ev) => setAmend({ ...amend, incomeTax: ev.target.value.replace(/[^\d]/g, "") })} /></div>
            <div className="field"><label>지방소득세 (원신고 {KRW(amendBase().localTax)})</label>
              <input className="input num" placeholder={String(amendBase().localTax)} value={amend.localTax}
                onChange={(ev) => setAmend({ ...amend, localTax: ev.target.value.replace(/[^\d]/g, "") })} /></div>
          </div>
          <div className="field" style={{ marginTop: 10 }}><label>수정 사유</label>
            <textarea className="input" style={{ height: 66, padding: 10, resize: "none", width: "100%" }}
              placeholder="예: 5/28 상여 누락분 반영" value={amend.reason} onChange={(ev) => setAmend({ ...amend, reason: ev.target.value })} /></div>
          {amendResult && (amendResult.changed.length > 0
            ? <div className="card card-pad" style={{ marginTop: 12, background: "var(--bg-sunken)" }}>
                {amendResult.changed.map((k) => (
                  <div key={k} style={{ display: "flex", justifyContent: "space-between", fontSize: 12.5, padding: "3px 0" }}>
                    <span className="muted">{AMEND_LABEL[k] || k}</span>
                    <span className="num">{KRW(amendResult.original[k])} → {KRW(amendResult.revised[k])}{" "}
                      <b style={{ color: amendResult.delta[k] > 0 ? "var(--danger)" : "var(--ok)" }}>
                        ({amendResult.delta[k] > 0 ? "+" : ""}{KRW(amendResult.delta[k])})</b></span>
                  </div>
                ))}
              </div>
            : <div className="muted" style={{ fontSize: 12, marginTop: 12 }}>변경된 금액이 없습니다 — 수정할 값을 입력하세요.</div>)}
        </Modal>
      )}
    </div>
  );
}

function maskRrn(seed) {
  const sd = String(seed).split("").reduce((s, ch) => s + ch.charCodeAt(0), 0);
  const yy = 70 + (sd % 30);
  return String(yy) + String((sd % 12) + 1).padStart(2, "0") + String((sd % 28) + 1).padStart(2, "0") + "-" + (((sd % 2) + 1)) + "******";
}

function DueChip({ title, due, today, done, muted }) {
  const dd = Math.round((new Date(due) - new Date(today)) / 86400000);
  const tone = done ? "ok" : dd < 0 ? "danger" : dd <= 3 ? "warn" : "accent";
  const TONE = {
    ok: { bg: "var(--ok-soft)", line: "var(--ok-line)", ink: "color-mix(in oklab, var(--ok) 80%, black)" },
    danger: { bg: "var(--danger-soft)", line: "var(--danger-line)", ink: "var(--danger)" },
    warn: { bg: "var(--warn-soft)", line: "var(--warn-line)", ink: "color-mix(in oklab, var(--warn) 60%, black)" },
    accent: { bg: "var(--accent-soft)", line: "var(--accent-line)", ink: "var(--accent-ink)" },
  }[tone];
  return (
    <div style={{ border: "1px solid " + TONE.line, background: muted ? "var(--surface)" : TONE.bg, borderRadius: 10, padding: "11px 13px", opacity: muted ? .6 : 1 }}>
      <div style={{ fontSize: 11.5, fontWeight: 650, color: "var(--ink-2)", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{title}</div>
      <div style={{ display: "flex", alignItems: "baseline", gap: 7, marginTop: 4 }}>
        <span className="num" style={{ fontSize: 14, fontWeight: 750, color: TONE.ink }}>{due.slice(5)}</span>
        <span className="num" style={{ fontSize: 11, fontWeight: 700, color: TONE.ink }}>{done ? "완료" : dd < 0 ? "마감 지남" : dd === 0 ? "오늘" : "D-" + dd}</span>
      </div>

    </div>
  );
}

function MiniKpi({ label, value, accent }) {
  return (
    <div style={{ background: accent ? "var(--accent)" : "var(--surface-2)", color: accent ? "#fff" : "var(--ink)", border: accent ? "none" : "1px solid var(--line)", borderRadius: 10, padding: "9px 14px", minWidth: 110 }}>
      <div style={{ fontSize: 11, fontWeight: 600, opacity: accent ? .85 : 1, color: accent ? "#fff" : "var(--ink-3)" }}>{label}</div>
      <div className="num" style={{ fontSize: 18, fontWeight: 750, marginTop: 1 }}>{value}</div>
    </div>
  );
}

function PreviewTable({ cols, rows, align, more }) {
  return (
    <table className="tbl tbl-cards" style={{ fontSize: 12.5 }}>
      <thead><tr>{cols.map((c, i) => <th key={i} className={align && align[i] ? "r" : ""}>{c}</th>)}</tr></thead>
      <tbody>
        {rows.map((r, i) => (
          <tr key={i}>{r.map((cell, j) => <td key={j} className={(align && align[j] ? "r num" : "") } data-label={cols[j]} style={j === 0 ? { fontWeight: 600 } : null}>{cell}</td>)}</tr>
        ))}
        {more > 0 && <tr><td colSpan={cols.length} className="muted" style={{ textAlign: "center", fontSize: 12, padding: "10px 0" }}>… 외 {more}명 (CSV에 전체 포함)</td></tr>}
      </tbody>
    </table>
  );
}

// 반기 합산용 월별 신고 — 확정 이력이 없는 달은 당월 값으로 근사한다(데모).
// ponytail: 귀속월별 확정 run이 영속되면 그 값으로 교체.
function halfMonthlyReports(client, ym, cur) {
  const w = window.BARUNSEM.halfYearWindow(ym);
  const out = [];
  const p = w.from.split("-").map(Number);
  for (let i = 0; i < 6; i++) {
    const mm = String(p[1] + i).padStart(2, "0");
    const k = p[0] + "-" + mm;
    if (k > ym) break;
    out.push({ ...cur, ym: k });
  }
  return out;
}

Object.assign(window, { Filings });
