// «Зарплаты» — доходы + штрафы (Требование 3) и модуль-подготовщик взысканий (Модуль 5).

const FINE_STATUS = {
  prepared:              { label: "Подготовлено",            cls: "muted" },
  explanation_requested: { label: "Затребованы объяснения",  cls: "tg" },
  approved:              { label: "Утверждено",              cls: "err" },
  removed:               { label: "Снято",                   cls: "ok" },
  dismissal_recommended: { label: "Рекомендация к увольнению", cls: "err" },
};
const FINE_KIND = { fine: "Штраф", reprimand: "Выговор", dismissal: "Увольнение" };
const ESCALATION_PREVIEW = { 1: "−25%", 2: "−35%", 3: "−50%" };

function money(n, cur) {
  return Math.round(n || 0).toLocaleString("ru-RU") + " " + (cur || "₽");
}

// prevPeriod — прошлый месяц в формате MM.YYYY (дефолт для импорта из 1С).
function prevPeriod() {
  const d = new Date();
  d.setMonth(d.getMonth() - 1);
  return String(d.getMonth() + 1).padStart(2, "0") + "." + d.getFullYear();
}

function SalariesPage() {
  const [data, setData] = React.useState(null);
  const [fines, setFines] = React.useState([]);
  const [monitor, setMonitor] = React.useState({ rows: [] });
  const [createOpen, setCreateOpen] = React.useState(false);
  const [fineOpen, setFineOpen] = React.useState(null);
  const [profileFor, setProfileFor] = React.useState(null);
  const [period, setPeriod] = React.useState(""); // "" = текущий срез; MM.YYYY = данные 1С
  const [importOpen, setImportOpen] = React.useState(false);

  const load = React.useCallback(() => {
    if (!window.API) return;
    Promise.all([
      window.API.payroll(period).catch(() => ({ rows: [] })),
      window.API.listFines().catch(() => []),
      window.API.monitoring().catch(() => ({ rows: [] })),
    ]).then(([pay, fn, mon]) => {
      setData(pay); setFines(Array.isArray(fn) ? fn : []); setMonitor(mon || { rows: [] });
    });
  }, [period]);
  React.useEffect(() => { load(); }, [load]);

  const canManage = window.ME && (window.ME.role === "admin" || window.ME.role === "controller");

  // Выгрузка утверждённых удержаний за период в файл для ЗУП (идемпотентно).
  const exportDeductions = async (force) => {
    let p = period;
    if (!p) {
      const finePeriods = [...new Set(fines.filter(f => f.status === "approved").map(f => f.period))];
      p = window.prompt("За какой период выгрузить удержания? (ММ.ГГГГ)", finePeriods[0] || prevPeriod());
      if (!p) return;
    }
    try {
      const blob = await window.API.payrollDeductionsExport(p.trim(), force);
      downloadBlob(blob, `Удержания_${p.trim()}.xlsx`);
      load();
    } catch (e) {
      if (e && e.alreadyExported) {
        if (window.confirm(String(e.message) + "\n\nВыгрузить повторно?")) return exportDeductions(true);
        return;
      }
      alert(String((e && e.message) || e));
    }
  };

  if (!data) {
    return (
      <div className="page page-fade">
        <div className="page-head"><div><h1 className="page-title">Зарплаты</h1><p className="page-subtitle">Доходы, удержания и взыскания</p></div></div>
        <PageLoading label="Загружаем зарплаты…" />
      </div>
    );
  }

  const rows = (data.rows || []).slice().sort((a, b) => b.net - a.net);
  const cur = (rows[0] && rows[0].currency) || "₽";
  const totalGross = rows.reduce((s, r) => s + r.gross, 0);
  const totalDeduct = rows.reduce((s, r) => s + r.deduction, 0);
  const totalNet = rows.reduce((s, r) => s + r.net, 0);
  const activeFines = fines.filter(f => f.status !== "removed");
  const monByPerson = {};
  (monitor.rows || []).forEach(m => { monByPerson[m.person] = m; });

  const periods = data.periods || [];
  const from1C = data.source === "1c";
  const sourceLabel = period
    ? (from1C ? `данные из 1С за ${period} (read-only)` : `за ${period} данных нет — импортируйте из 1С`)
    : "текущий срез (ручной/демо — до импорта из 1С)";

  return (
    <div className="page page-fade">
      <div className="page-head">
        <div>
          <h1 className="page-title">Зарплаты и удержания</h1>
          <p className="page-subtitle">
            Доходы и штрафы · вычет не более {data.maxDeductionPct}% в месяц (п.13.16) · {sourceLabel}
            {from1C && <span className="pill tg" style={{ marginLeft: 8, fontSize: 10.5 }}>1С</span>}
          </p>
        </div>
        <div style={{ display: "flex", gap: 8, flexWrap: "wrap", alignItems: "center" }}>
          <select className="fld" style={{ width: 190 }} value={period} onChange={e => setPeriod(e.target.value)} title="Период ФОТ">
            <option value="">Текущий срез</option>
            {periods.map(p => <option key={p} value={p}>{p} · из 1С</option>)}
            {period && !periods.includes(period) && <option value={period}>{period}</option>}
          </select>
          {canManage && <button className="btn btn-outline" onClick={() => setImportOpen(true)}><I.Upload size={14} /> Импорт из 1С</button>}
          {canManage && <button className="btn btn-outline" onClick={() => exportDeductions(false)} title="Excel утверждённых удержаний за период — для загрузки в ЗУП"><I.Download size={14} /> Удержания → 1С</button>}
          <button className="btn btn-primary" onClick={() => setCreateOpen(true)}><I.Shield size={14} /> Подготовить взыскание</button>
        </div>
      </div>

      <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(180px, 1fr))", gap: 12, marginBottom: 16 }}>
        <StatCard label="Фонд (начислено)" value={money(totalGross, cur)} icon={<I.Coins size={16} />} />
        <StatCard label="Удержания (штрафы)" value={money(totalDeduct, cur)} color={totalDeduct ? "var(--err)" : "var(--ok)"} icon={<I.Alert size={16} />} />
        <StatCard label="К выплате" value={money(totalNet, cur)} icon={<I.Dollar size={16} />} />
        <StatCard label="Активные взыскания" value={activeFines.length} color={activeFines.length ? "var(--err)" : "var(--ok)"} icon={<I.Shield size={16} />} />
      </div>

      <Card title="Расчёт по сотрудникам" subtitle="Оклад + надбавки − удержания по утверждённым штрафам" bodyStyle={{ padding: 0 }}>
        <div className="eff-row pay-row eff-head">
          <div>Сотрудник</div><div>Начислено</div><div>Удержание</div><div>К выплате</div><div></div>
        </div>
        {rows.map(r => (
          <div key={r.person} className="eff-row pay-row">
            <div style={{ minWidth: 0 }}>
              <div style={{ fontWeight: 500, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", cursor: "pointer", color: "var(--accent-strong)" }} title="Движения по сотруднику" onClick={() => setProfileFor(r.person)}>{r.person}</div>
              <div style={{ fontSize: 11.5, color: "var(--ink-400)" }}>{r.dept || "—"}</div>
            </div>
            <div style={{ fontSize: 13 }}>
              {money(r.gross, r.currency)}
              <div style={{ fontSize: 11, color: "var(--ink-400)" }}>оклад {money(r.base, r.currency)}{r.accruals ? ` + ${money(r.accruals, r.currency)}` : ""}</div>
              {(r.tariffRate > 0 || r.hoursWorked > 0) && (
                <div style={{ fontSize: 11, color: "var(--ink-400)" }}>
                  {r.tariffRate > 0 ? `ставка ${money(r.tariffRate, r.currency)}/ч` : ""}{r.tariffRate > 0 && r.hoursWorked > 0 ? " · " : ""}{r.hoursWorked > 0 ? `${r.hoursWorked} ч` : ""}
                </div>
              )}
            </div>
            <div style={{ fontSize: 13, color: r.deduction ? "var(--err)" : "var(--ink-300)" }}>
              {r.deduction ? `− ${money(r.deduction, r.currency)}` : "—"}
              {r.deductionPct > 0 && <div style={{ fontSize: 11, color: "var(--ink-400)" }}>{r.deductionPct}%</div>}
            </div>
            <div style={{ fontSize: 13, fontWeight: 600 }}>{money(r.net, r.currency)}</div>
            <div style={{ textAlign: "right" }}>
              <button className="btn btn-ghost" style={{ padding: "4px 8px", fontSize: 11.5 }} onClick={() => setCreateOpen(r.person)}>Взыскание</button>
            </div>
          </div>
        ))}
        {rows.length === 0 && (
          <div style={{ padding: 22, color: "var(--ink-400)", fontSize: 13, textAlign: "center" }}>
            {period
              ? <>За период {period} данных ФОТ нет.{canManage ? " Нажмите «Импорт из 1С» и загрузите выгрузку за этот период." : ""}</>
              : "Нет данных по зарплате."}
          </div>
        )}
      </Card>

      <div style={{ height: 16 }} />

      <Card title="Дисциплинарные взыскания" subtitle="ИИ готовит документы и контролирует сроки — решение принимает руководитель (п.13.5)" bodyStyle={{ padding: 0 }}>
        {fines.length === 0 && <div style={{ padding: 18, color: "var(--ink-400)", fontSize: 13 }}>Взысканий нет.</div>}
        {fines.map(f => {
          const st = FINE_STATUS[f.status] || { label: f.status, cls: "muted" };
          return (
            <div key={f.id} className="setting-row" style={{ cursor: "pointer" }} onClick={() => setFineOpen(f.id)}>
              <div className="desc" style={{ minWidth: 0 }}>
                <div className="t" style={{ display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }}>
                  {f.person}
                  <span className="pill muted" style={{ fontSize: 10 }}>{FINE_KIND[f.kind] || f.kind}</span>
                  {f.percent > 0 && <span style={{ color: "var(--err)", fontSize: 12 }}>−{f.percent}%</span>}
                </div>
                <div className="s">период {f.period} · просрочек: {f.overdueCount}{f.basis ? ` · ${f.basis}` : ""}{f.files && f.files.length ? ` · 📎 ${f.files.length}` : ""}</div>
              </div>
              {f.exportedAt && <span className="pill muted" style={{ fontSize: 10, marginRight: 6 }} title={`Выгружено в 1С: ${f.exportedAt}`}>→ 1С</span>}
              <span className={`pill ${st.cls}`}>{st.label}</span>
            </div>
          );
        })}
      </Card>

      {createOpen && <CreateFineModal person={typeof createOpen === "string" ? createOpen : ""} people={rows.map(r => r.person)} monByPerson={monByPerson} onClose={() => setCreateOpen(false)} onCreated={() => { setCreateOpen(false); load(); }} />}
      {fineOpen != null && <FineModal fine={fines.find(f => f.id === fineOpen)} onClose={() => setFineOpen(null)} onChanged={load} />}
      {profileFor && <PersonProfileModal person={profileFor} onClose={() => setProfileFor(null)} />}
      {importOpen && <Payroll1CImportModal defaultPeriod={period || prevPeriod()} onClose={() => setImportOpen(false)} onApplied={(p) => { setImportOpen(false); setPeriod(p); }} />}
    </div>
  );
}

// Payroll1CImportModal — импорт ФОТ из выгрузки 1С:ЗУП (Excel/CSV): период + файл →
// превью-диф (новые / изменённые / несопоставленные / без изменений) → применить.
function Payroll1CImportModal({ defaultPeriod, onClose, onApplied }) {
  const [period, setPeriod] = React.useState(defaultPeriod);
  const [file, setFile] = React.useState(null);
  const [preview, setPreview] = React.useState(null);
  const [sel, setSel] = React.useState({}); // ключ строки превью → выбрана ли к применению
  const [busy, setBusy] = React.useState("");
  const [err, setErr] = React.useState("");
  const [result, setResult] = React.useState(null);
  const fileRef = React.useRef(null);
  const validP = /^(0[1-9]|1[0-2])\.\d{4}$/.test((period || "").trim());

  const doPreview = async () => {
    if (!file) { setErr("Выберите файл выгрузки из 1С (.xlsx или .csv)"); return; }
    if (!validP) { setErr("Период — в формате ММ.ГГГГ, напр. 06.2026"); return; }
    setBusy("preview"); setErr(""); setResult(null);
    try {
      const p = await window.API.payrollImportPreview(file, period.trim());
      setPreview(p);
      const s = {};
      (p.new || []).forEach((_, i) => { s["n" + i] = true; });
      (p.changed || []).forEach((_, i) => { s["c" + i] = true; });
      setSel(s);
    } catch (e) { setErr(String((e && e.message) || e)); }
    finally { setBusy(""); }
  };

  const apply = async () => {
    const rows = [];
    (preview.new || []).forEach((r, i) => { if (sel["n" + i]) rows.push({ record: r.record, person: r.person }); });
    (preview.changed || []).forEach((r, i) => { if (sel["c" + i]) rows.push({ record: r.record, person: r.person }); });
    if (!rows.length) { setErr("Не выбрано ни одной строки"); return; }
    setBusy("apply"); setErr("");
    try {
      const res = await window.API.payrollImportApply(period.trim(), rows);
      setResult(res);
      if (!res.errors || !res.errors.length) setTimeout(() => onApplied(period.trim()), 900);
    } catch (e) { setErr(String((e && e.message) || e)); }
    finally { setBusy(""); }
  };

  const rowLine = (r, key, extra) => (
    <div key={key} className="setting-row" style={{ padding: "7px 0", gap: 10 }}>
      <CheckBox on={!!sel[key]} onChange={(v) => setSel(s => ({ ...s, [key]: v }))} />
      <div className="desc" style={{ minWidth: 0, flex: 1 }}>
        <div className="t" style={{ display: "flex", gap: 8, flexWrap: "wrap", alignItems: "center" }}>
          {r.person}
          <span style={{ color: "var(--ink-400)", fontWeight: 400, fontSize: 11.5 }}>({r.matchedBy}{r.record.externalId ? ` · таб. ${r.record.externalId}` : ""})</span>
        </div>
        <div className="s">
          оклад {money(r.record.base)} {r.record.accruals ? `+ надбавки ${money(r.record.accruals)}` : ""} · начислено {money(r.record.gross)} · к выплате по 1С {money(r.record.net)}
        </div>
        {extra}
      </div>
    </div>
  );

  return (
    <div className="tg-overlay open" style={{ background: "rgba(13,22,32,.5)", display: "grid", placeItems: "center", zIndex: 270 }} onClick={onClose}>
      <div className="card" style={{ width: 680, maxWidth: "95vw", maxHeight: "92vh", display: "flex", flexDirection: "column", boxShadow: "var(--sh-3)" }} onClick={e => e.stopPropagation()}>
        <div className="card-head">
          <div style={{ flex: 1 }}>
            <h3 className="card-title">Импорт ФОТ из 1С</h3>
            <p className="card-sub">Выгрузка ЗУП (Excel/CSV) → превью → применить. 1С — источник истины, данные будут read-only.</p>
          </div>
          <button className="icon-btn" style={{ color: "var(--ink-400)" }} onClick={onClose}><I.X size={18} /></button>
        </div>

        <div className="card-body" style={{ overflowY: "auto", display: "flex", flexDirection: "column", gap: 12 }}>
          <div style={{ display: "grid", gridTemplateColumns: "140px 1fr auto", gap: 10, alignItems: "end" }}>
            <div>
              <label className="fld-label">Период (ММ.ГГГГ)</label>
              <input className="fld" style={{ width: "100%" }} placeholder="06.2026" value={period} onChange={e => setPeriod(e.target.value)} />
            </div>
            <div>
              <label className="fld-label">Файл выгрузки из 1С</label>
              <button className="btn btn-outline" style={{ width: "100%", justifyContent: "flex-start", overflow: "hidden" }} onClick={() => fileRef.current && fileRef.current.click()}>
                <I.Paperclip size={13} /> {file ? file.name : "Выбрать файл (.xlsx / .csv)…"}
              </button>
              <input ref={fileRef} type="file" accept=".xlsx,.csv" style={{ display: "none" }} onChange={e => { setFile(e.target.files && e.target.files[0]); setPreview(null); }} />
            </div>
            <button className="btn btn-accent" onClick={doPreview} disabled={busy === "preview"}>{busy === "preview" ? "Читаю…" : "Показать превью"}</button>
          </div>
          <div style={{ fontSize: 11.5, color: "var(--ink-400)" }}>
            Колонки: Таб. номер · ФИО · Период · Оклад · Надбавки · Начислено · К выплате · Ставка · Часы · Даты аванса/расчёта
            (порядок и формулировки не критичны). <a href={window.API.payrollTemplateUrl()} style={{ color: "var(--accent-strong)" }}>Скачать шаблон-образец</a> ·
            обработка для 1С — в <code style={{ fontSize: 11 }}>docs/1c/</code>.
          </div>

          {preview && (
            <>
              <div style={{ display: "flex", gap: 8, flexWrap: "wrap", fontSize: 12 }}>
                <span className="pill ok">новых: {(preview.new || []).length}</span>
                <span className="pill warn">изменённых: {(preview.changed || []).length}</span>
                <span className="pill err">несопоставленных: {(preview.unmatched || []).length}</span>
                <span className="pill muted">без изменений: {preview.unchanged || 0}</span>
              </div>

              {(preview.new || []).length > 0 && (
                <div>
                  <div style={{ fontSize: 11, textTransform: "uppercase", letterSpacing: ".05em", color: "var(--ink-400)", marginBottom: 4 }}>Новые записи</div>
                  {(preview.new || []).map((r, i) => rowLine(r, "n" + i))}
                </div>
              )}
              {(preview.changed || []).length > 0 && (
                <div>
                  <div style={{ fontSize: 11, textTransform: "uppercase", letterSpacing: ".05em", color: "var(--ink-400)", marginBottom: 4 }}>Изменённые (файл заменит данные CRM)</div>
                  {(preview.changed || []).map((r, i) => rowLine(r, "c" + i,
                    <div className="s" style={{ color: "var(--warn)" }}>изменилось: {(r.fields || []).join(", ")}</div>))}
                </div>
              )}
              {(preview.unmatched || []).length > 0 && (
                <div>
                  <div style={{ fontSize: 11, textTransform: "uppercase", letterSpacing: ".05em", color: "var(--err)", marginBottom: 4 }}>Несопоставленные (не будут записаны)</div>
                  {(preview.unmatched || []).map((r, i) => (
                    <div key={i} style={{ fontSize: 12.5, padding: "4px 0", color: "var(--ink-600)" }}>
                      {r.record.fullName}{r.record.externalId ? ` (таб. ${r.record.externalId})` : ""} — <span style={{ color: "var(--err)" }}>{r.reason}</span>
                    </div>
                  ))}
                  <div style={{ fontSize: 11.5, color: "var(--ink-400)", marginTop: 4 }}>Добавьте сотрудника в CRM или проставьте таб. номер 1С в его карточке — и повторите превью.</div>
                </div>
              )}
            </>
          )}

          {result && (
            <div style={{ fontSize: 12.5, color: result.errors && result.errors.length ? "var(--warn)" : "var(--ok)" }}>
              ✓ Записано строк: {result.applied}{result.externalIdSet ? ` · проставлено таб. номеров: ${result.externalIdSet}` : ""}
              {result.errors && result.errors.length > 0 && <div style={{ color: "var(--err)" }}>{result.errors.join("; ")}</div>}
            </div>
          )}
          {err && <div style={{ color: "var(--err)", fontSize: 12.5 }}>{err}</div>}
        </div>

        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", gap: 8, padding: "12px 20px", borderTop: "1px solid var(--ink-100)" }}>
          <span style={{ fontSize: 12, color: "var(--ink-400)" }}>{preview ? `В файле строк: ${preview.total}` : ""}</span>
          <div style={{ display: "flex", gap: 8 }}>
            <button className="btn btn-ghost" onClick={onClose}>Закрыть</button>
            <button className="btn btn-accent" onClick={apply} disabled={!preview || busy === "apply" || ((preview && (preview.new || []).length === 0 && (preview.changed || []).length === 0))}>
              <I.Check size={14} /> {busy === "apply" ? "Запись…" : "Применить выбранное"}
            </button>
          </div>
        </div>
      </div>
    </div>
  );
}

// CreateFineModal — подготовить взыскание по факту просрочки.
function CreateFineModal({ person, people, monByPerson, onClose, onCreated }) {
  const [who, setWho] = React.useState(person || (people[0] || ""));
  const suggested = (monByPerson[who] && monByPerson[who].notDone) || 1;
  const [count, setCount] = React.useState(String(suggested));
  const [basis, setBasis] = React.useState("");
  const [busy, setBusy] = React.useState(false);
  const [err, setErr] = React.useState("");
  React.useEffect(() => { setCount(String((monByPerson[who] && monByPerson[who].notDone) || 1)); }, [who]);

  const n = parseInt(count, 10) || 0;
  const preview = n >= 4 ? "рекомендация к увольнению" : (ESCALATION_PREVIEW[n] || "—");
  const options = (people || []).map(p => ({ value: p, label: p }));

  const submit = async () => {
    if (!who || n < 1) { setErr("Укажите сотрудника и число просрочек ≥ 1"); return; }
    setBusy(true); setErr("");
    try { await window.API.createFine({ person: who, overdueCount: n, basis: basis.trim() }); onCreated(); }
    catch (e) { setErr(String((e && e.message) || e)); setBusy(false); }
  };

  return (
    <div className="tg-overlay open" style={{ background: "rgba(13,22,32,.5)", display: "grid", placeItems: "center", zIndex: 260 }} onClick={onClose}>
      <div className="card" style={{ width: 480, maxWidth: "92vw", boxShadow: "var(--sh-3)" }} onClick={e => e.stopPropagation()}>
        <div className="card-head">
          <div style={{ flex: 1 }}><h3 className="card-title">Подготовить взыскание</h3><p className="card-sub">По факту просрочки. Решение — за руководителем.</p></div>
          <button className="icon-btn" style={{ color: "var(--ink-400)" }} onClick={onClose}><I.X size={18} /></button>
        </div>
        <div className="card-body" style={{ display: "flex", flexDirection: "column", gap: 12 }}>
          <div>
            <label className="fld-label">Сотрудник</label>
            <Combobox options={options} value={who} onSelect={setWho} placeholder="Выберите сотрудника…" />
          </div>
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}>
            <div>
              <label className="fld-label">Число просрочек</label>
              <input className="fld" type="number" min="1" style={{ width: "100%" }} value={count} onChange={e => setCount(e.target.value)} />
            </div>
            <div>
              <label className="fld-label">Размер</label>
              <div className="fld" style={{ width: "100%", display: "flex", alignItems: "center", color: n >= 4 ? "var(--err)" : "var(--ink-700)", fontWeight: 600 }}>{preview}</div>
            </div>
          </div>
          <div style={{ fontSize: 11.5, color: "var(--ink-400)" }}>Эскалация: 1 → −25%, 2 → −35%, 3 → −50%, 4 → рекомендация к увольнению. Предложено по числу невыполненных за период.</div>
          <div>
            <label className="fld-label">Основание</label>
            <textarea className="fld" rows={2} style={{ width: "100%", resize: "vertical" }} placeholder="Суть нарушения (фиксируется в журнале)" value={basis} onChange={e => setBasis(e.target.value)} />
          </div>
          {err && <div style={{ color: "var(--err)", fontSize: 12.5 }}>{err}</div>}
        </div>
        <div style={{ display: "flex", justifyContent: "flex-end", gap: 8, padding: "0 20px 18px" }}>
          <button className="btn btn-ghost" onClick={onClose}>Отмена</button>
          <button className="btn btn-accent" onClick={submit} disabled={busy || !who}><I.Shield size={14} /> Подготовить</button>
        </div>
      </div>
    </div>
  );
}

// FineModal — процедура по Правилам: затребование объяснений → решение/снятие.
function FineModal({ fine, onClose, onChanged }) {
  const [expl, setExpl] = React.useState((fine && fine.explanationText) || "");
  const [busy, setBusy] = React.useState(false);
  const [err, setErr] = React.useState("");
  const fileRef = React.useRef(null);
  if (!fine) return null;
  const st = FINE_STATUS[fine.status] || { label: fine.status, cls: "muted" };

  const act = async (fn, ok) => {
    setBusy(true); setErr("");
    try { await fn(); onChanged(); ok && ok(); }
    catch (e) { setErr(String((e && e.message) || e)); setBusy(false); }
  };
  const saveExpl = () => act(() => window.API.fineExplanation(fine.id, expl));
  const request = () => act(() => window.API.fineRequestExplanation(fine.id), onClose);
  const approve = () => { if (window.confirm("Утвердить взыскание? Это решение руководителя (п.13.5).")) act(() => window.API.approveFine(fine.id), onClose); };
  const remove = () => {
    const reason = window.prompt("Основание для снятия взыскания:");
    if (reason == null) return;
    act(() => window.API.removeFine(fine.id, reason), onClose);
  };
  const upload = async (e) => {
    const files = e.target.files; e.target.value = "";
    if (!files || !files.length) return;
    await act(() => window.API.uploadFineFiles(fine.id, files));
  };

  return (
    <div className="tg-overlay open" style={{ background: "rgba(13,22,32,.5)", display: "grid", placeItems: "center", zIndex: 270 }} onClick={onClose}>
      <div className="card" style={{ width: 560, maxWidth: "94vw", maxHeight: "90vh", display: "flex", flexDirection: "column", boxShadow: "var(--sh-3)" }} onClick={e => e.stopPropagation()}>
        <div className="card-head">
          <div style={{ flex: 1 }}>
            <h3 className="card-title">Взыскание · {fine.person}</h3>
            <p className="card-sub">{FINE_KIND[fine.kind] || fine.kind} · период {fine.period} · просрочек {fine.overdueCount}</p>
          </div>
          <span className={`pill ${st.cls}`} style={{ marginRight: 8 }}>{st.label}</span>
          <button className="icon-btn" style={{ color: "var(--ink-400)" }} onClick={onClose}><I.X size={18} /></button>
        </div>
        <div className="card-body" style={{ overflowY: "auto", display: "flex", flexDirection: "column", gap: 12 }}>
          {fine.kind === "dismissal"
            ? <div style={{ fontSize: 12.5, color: "var(--err)", background: "#fbecec", borderRadius: "var(--r-md)", padding: "8px 11px" }}>4-я просрочка — сформирована <b>рекомендация к увольнению</b>. Решение принимает Руководитель Центра (п.13.5); система лишь сигнализирует.</div>
            : <div style={{ display: "flex", gap: 16, fontSize: 13 }}>
                <div><span style={{ color: "var(--ink-400)" }}>Размер: </span><b style={{ color: "var(--err)" }}>−{fine.percent}%</b></div>
                {fine.amount > 0 && <div><span style={{ color: "var(--ink-400)" }}>Сумма: </span><b>{money(fine.amount)}</b></div>}
              </div>}
          {fine.basis && <div style={{ fontSize: 12.5 }}><span style={{ color: "var(--ink-400)" }}>Основание: </span>{fine.basis}</div>}

          <div style={{ borderTop: "1px solid var(--ink-100)", paddingTop: 10 }}>
            <div style={{ fontSize: 11, textTransform: "uppercase", letterSpacing: ".05em", color: "var(--ink-400)", marginBottom: 6 }}>Объяснительная работника</div>
            {fine.requestedAt && <div style={{ fontSize: 11.5, color: "var(--ink-400)", marginBottom: 6 }}>Объяснения затребованы: {fine.requestedAt} (отказ дать объяснения не препятствует — п.13.6)</div>}
            <textarea className="fld" rows={3} style={{ width: "100%", resize: "vertical" }} placeholder="Текст объяснительной (со слов работника или из документа)" value={expl} onChange={e => setExpl(e.target.value)} disabled={fine.status === "approved" || fine.status === "removed"} />
            <div style={{ display: "flex", gap: 8, marginTop: 8, alignItems: "center", flexWrap: "wrap" }}>
              {fine.status !== "approved" && fine.status !== "removed" &&
                <button className="btn btn-outline" style={{ padding: "5px 10px", fontSize: 12 }} onClick={saveExpl} disabled={busy}><I.Check size={13} /> Сохранить текст</button>}
              <button className="btn btn-outline" style={{ padding: "5px 10px", fontSize: 12 }} onClick={() => fileRef.current && fileRef.current.click()} disabled={busy}><I.Paperclip size={13} /> Прикрепить файл</button>
              <input ref={fileRef} type="file" multiple style={{ display: "none" }} onChange={upload} />
            </div>
            {fine.files && fine.files.length > 0 && (
              <div style={{ display: "flex", flexDirection: "column", gap: 4, marginTop: 8 }}>
                {fine.files.map(ff => (
                  <a key={ff.id} href={window.API.fineFileUrl(fine.id, ff.id)} target="_blank" style={{ fontSize: 12.5, color: "var(--accent-strong)", display: "inline-flex", alignItems: "center", gap: 5 }}>
                    <I.File size={12} /> {ff.name}
                  </a>
                ))}
              </div>
            )}
          </div>

          {fine.decidedBy && <div style={{ fontSize: 12, color: "var(--ink-400)" }}>Решение: {fine.decidedBy} · {fine.decidedAt}</div>}
          {fine.removedReason && <div style={{ fontSize: 12, color: "var(--ok)" }}>Снято: {fine.removedReason}</div>}
          {err && <div style={{ color: "var(--err)", fontSize: 12.5 }}>{err}</div>}
        </div>
        <div style={{ display: "flex", justifyContent: "flex-end", gap: 8, padding: "12px 20px", borderTop: "1px solid var(--ink-100)" }}>
          {fine.status === "prepared" && fine.kind !== "dismissal" &&
            <button className="btn btn-accent" onClick={request} disabled={busy}><I.Send size={13} /> Затребовать объяснения</button>}
          {fine.status === "explanation_requested" &&
            <button className="btn btn-accent" onClick={approve} disabled={busy}><I.Check size={14} /> Утвердить взыскание</button>}
          {fine.status !== "removed" &&
            <button className="btn btn-danger" onClick={remove} disabled={busy}><I.X size={13} /> Снять</button>}
        </div>
      </div>
    </div>
  );
}

window.SalariesPage = SalariesPage;
