/* global React, window */
const { useState: useLeaveState, useEffect: useLeaveEffect } = React;

// ประเภทการลาอ่านจากระบบ (window.LEAVE_TYPES — ราชการ 11 ประเภท หรือชุดที่ HR ตั้งเอง)
const LEAVE_CHIP_BY_COLOR = { coral: 'c-coral', yellow: 'c-amber', mint: 'c-green', magenta: 'c-violet', indigo: 'c-blue', blue: 'c-blue' };
function leaveTypeMeta(code) {
  const t = (window.LEAVE_TYPES || []).find((x) => x.code === code);
  if (t) return { label: t.name, chip: LEAVE_CHIP_BY_COLOR[t.color] || 'c-gray', count_mode: t.count_mode || 'workday', form: t.form || 'generic' };
  return { label: { sick: 'ลาป่วย', personal: 'ลากิจ', vacation: 'ลาพักผ่อน', other: 'อื่นๆ' }[code] || code, chip: 'c-gray', count_mode: 'workday' };
}
const LEAVE_STATUS = {
  pending:  { label: 'รออนุมัติ', chip: 'c-amber' },
  approved: { label: 'อนุมัติ',    chip: 'c-green' },
  rejected: { label: 'ปฏิเสธ',     chip: 'c-gray' },
};
function leaveDays(a, b) {
  try { const d1 = new Date(a), d2 = new Date(b); const n = Math.round((d2 - d1) / 86400000) + 1; return n > 0 ? n : 1; } catch (e) { return 1; }
}
// ปีงบประมาณ (พ.ศ.) ของวันที่ — 1 ต.ค. ขึ้นปีใหม่
function fiscalYearOf(iso) { const d = new Date((iso || new Date().toISOString().slice(0, 10)) + 'T00:00:00'); return d.getFullYear() + 543 + (d.getMonth() >= 9 ? 1 : 0); }
const openPrint = (id) => window.open(`/api/leaves/${id}/print`, '_blank', 'noopener');

// Circular face avatar (clickable → profile).
function LvAvatar({ id, size = 44 }) {
  const [err, setErr] = useLeaveState(false);
  const src = (err || !id) ? '/img/avatar-person.svg' : `/faces/${id}.jpg`;
  return <img src={src} alt="" onError={() => setErr(true)}
    onClick={() => id && window.openProfile && window.openProfile(id)} title={id ? 'ดูโปรไฟล์' : ''}
    style={{ width: size, height: size, borderRadius: '50%', objectFit: 'cover', flex: 'none',
      cursor: id ? 'pointer' : 'default', border: '1px solid var(--line)' }} />;
}

// ระบบลา — request + approve + ยอดคงเหลือ (ปีงบประมาณ) + ทะเบียนคุมวันลา
function LeavePage({ role }) {
  const [tab, setTab] = useLeaveState('requests');   // requests | register
  const [list, setList] = useLeaveState([]);
  const [statusFilter, setStatusFilter] = useLeaveState('all');
  const [requesting, setRequesting] = useLeaveState(false);
  const [balEmp, setBalEmp] = useLeaveState((window.CURRENT_USER && window.CURRENT_USER.employee_id) || ((window.EMPLOYEES || [])[0] || {}).id || '');
  const [fy, setFy] = useLeaveState(fiscalYearOf());
  const [bal, setBal] = useLeaveState(null);
  const canApprove = role === 'admin' || role === 'hr' || role === 'manager';
  const isStaff = canApprove;

  const load = async () => {
    const q = statusFilter === 'all' ? '' : `?status=${statusFilter}`;
    const r = await fetch('/api/leaves' + q, { credentials: 'include' });
    if (r.ok) setList(await r.json());
  };
  useLeaveEffect(() => { load(); }, [statusFilter]);

  const loadBalance = async () => {
    if (!balEmp) { setBal(null); return; }
    const r = await fetch(`/api/leaves/balance?employee_id=${encodeURIComponent(balEmp)}&fy=${fy}`, { credentials: 'include' });
    if (r.ok) setBal(await r.json()); else setBal(null);
  };
  useLeaveEffect(() => { loadBalance(); }, [balEmp, fy]);

  const approve = async (id) => {
    await fetch(`/api/leaves/${id}/approve`, { method: 'PUT', credentials: 'include' });
    window.appToast && window.appToast('อนุมัติแล้ว', { tone: 'success' }); load(); loadBalance();
  };
  const reject = async (id) => {
    const reason = await window.appPrompt({ title: 'เหตุผลที่ไม่อนุมัติ', placeholder: 'ระบุเหตุผล (ไม่บังคับ)' });
    if (reason === null) return;
    await fetch(`/api/leaves/${id}/reject`, { method: 'PUT', credentials: 'include',
      headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ reason }) });
    window.appToast && window.appToast('ปฏิเสธแล้ว', { tone: 'info' }); load(); loadBalance();
  };

  const fmtRange = (a, b) => a === b ? a : `${a} – ${b}`;
  const countBy = (s) => list.filter((l) => l.status === s).length;
  const stats = [['รออนุมัติ', countBy('pending'), 'var(--yellow-ink)'], ['อนุมัติแล้ว', countBy('approved'), 'var(--mint)'], ['ปฏิเสธ', countBy('rejected'), 'var(--ink-4)']];
  const fyOptions = [fiscalYearOf() + 1, fiscalYearOf(), fiscalYearOf() - 1, fiscalYearOf() - 2];

  return (
    <div data-screen-label="Leave">
      <div style={{ marginBottom: 16, display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between', flexWrap: 'wrap', gap: 10 }}>
        <div>
          <h1 style={{ fontFamily: 'var(--font-display)', fontSize: 22, fontWeight: 700, margin: 0 }}>การลา / Leave</h1>
          <div style={{ fontSize: 12.5, color: 'var(--ink-4)', marginTop: 4 }}>ยื่นใบลา · อนุมัติ · สิทธิ์วันลาตามระเบียบราชการ (นับวันทำการ · ปีงบประมาณ)</div>
        </div>
        {isStaff && (
          <div className="gv-seg">
            <button className={tab === 'requests' ? 'on' : ''} onClick={() => setTab('requests')}>คำขอลา</button>
            <button className={tab === 'register' ? 'on' : ''} onClick={() => setTab('register')}>ทะเบียนคุมวันลา</button>
          </div>
        )}
      </div>

      {tab === 'register' && isStaff ? <LeaveRegister role={role}/> : (<>
      {/* stats */}
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3,1fr)', gap: 14, marginBottom: 16 }}>
        {stats.map(([k, v, c]) => (
          <div key={k} className="gv-card" style={{ padding: '16px 20px', boxShadow: 'var(--shadow-sm)', display: 'flex', alignItems: 'baseline', gap: 12 }}>
            <div className="tnum" style={{ fontFamily: 'var(--font-display)', fontSize: 34, fontWeight: 700, lineHeight: 1, color: c }}>{v}</div>
            <div style={{ fontSize: 13, color: 'var(--ink-4)', fontWeight: 500 }}>{k}</div>
          </div>
        ))}
      </div>

      {canApprove && <CorrectionsPanel/>}

      {/* leave requests */}
      <div className="gv-card" style={{ marginBottom: 16 }}>
        <div className="gv-card-h" style={{ flexWrap: 'wrap', gap: 10 }}>
          <b>คำขอลา</b>
          <div style={{ display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap' }}>
            <div className="gv-seg">
              {['all', 'pending', 'approved', 'rejected'].map((s) => (
                <button key={s} className={statusFilter === s ? 'on' : ''} onClick={() => setStatusFilter(s)}>{s === 'all' ? 'ทั้งหมด' : LEAVE_STATUS[s].label}</button>
              ))}
            </div>
            <button className="gv-btn ok sm" onClick={() => setRequesting(true)}>＋ ยื่นใบลา</button>
          </div>
        </div>
        <div className="gv-card-b" style={{ paddingTop: 6 }}>
          {list.length === 0 ? <div className="gv-empty">— ไม่มีใบลา —</div>
            : list.map((l) => {
              const t = leaveTypeMeta(l.leave_type);
              const st = LEAVE_STATUS[l.status] || LEAVE_STATUS.pending;
              const n = Number(l.days) || leaveDays(l.start_date, l.end_date);
              return (
                <div key={l.id} className="gv-row">
                  <LvAvatar id={l.employee_id} />
                  <div style={{ minWidth: 140 }}>
                    <div style={{ fontSize: 14.5, fontWeight: 600 }}>{l.first_name} {l.last_name}</div>
                    <div style={{ fontSize: 11.5, color: 'var(--ink-4)', marginTop: 2 }}>{l.employee_id}{l.department_name ? ` · ${l.department_name}` : ''}</div>
                  </div>
                  <div style={{ minWidth: 170 }}>
                    <span className={`gv-chip ${t.chip}`}>{t.label}</span>
                    <div className="tnum" style={{ fontSize: 11.5, color: 'var(--ink-4)', marginTop: 4 }}>{fmtRange(l.start_date, l.end_date)} · {n} {t.count_mode === 'calendar' ? 'วัน' : 'วันทำการ'}</div>
                  </div>
                  <div style={{ flex: 1, fontSize: 13, color: 'var(--ink-4)', minWidth: 0 }}>{l.reason || '—'}</div>
                  <button className="gv-btn no sm" title="พิมพ์ใบลา" onClick={() => openPrint(l.id)} style={{ flexShrink: 0 }}>🖨 ใบลา</button>
                  {canApprove && l.status === 'pending' ? (
                    <div style={{ display: 'flex', gap: 6, flexShrink: 0 }}>
                      <button className="gv-btn no sm" onClick={() => reject(l.id)}>ปฏิเสธ</button>
                      <button className="gv-btn ok sm" onClick={() => approve(l.id)}>อนุมัติ</button>
                    </div>
                  ) : (
                    <span className={`gv-chip ${st.chip}`} style={{ flexShrink: 0 }}>{st.label}</span>
                  )}
                </div>
              );
            })}
        </div>
      </div>

      {/* leave balance */}
      <div className="gv-card">
        <div className="gv-card-h" style={{ flexWrap: 'wrap', gap: 10 }}>
          <b>สิทธิ์วันลา {bal ? <span style={{ fontWeight: 400, color: 'var(--ink-4)', fontSize: 12.5 }}>· {bal.emp_class_name} · ปีงบฯ {bal.fy} ({bal.from} – {bal.to})</span> : null}</b>
          <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
            <select className="gv-select" value={fy} onChange={(e) => setFy(Number(e.target.value))} style={{ width: 'auto' }}>
              {fyOptions.map((y) => <option key={y} value={y}>ปีงบประมาณ {y}</option>)}
            </select>
            {isStaff && (
              <select className="gv-select" value={balEmp} onChange={(e) => setBalEmp(e.target.value)} style={{ maxWidth: 260, width: 'auto' }}>
                <option value="">— เลือกพนักงาน —</option>
                {(window.EMPLOYEES || []).map((e) => <option key={e.id} value={e.id}>{e.first_name} {e.last_name} ({e.id})</option>)}
              </select>
            )}
          </div>
        </div>
        <div className="gv-card-b">
          {!bal ? <div className="gv-empty">เลือกพนักงานเพื่อดูสิทธิ์วันลา</div>
            : <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(190px, 1fr))', gap: 12 }}>
                {bal.balance.filter((b) => b.entitled || b.used).map((b) => {
                  const t = leaveTypeMeta(b.code);
                  const unit = b.count_mode === 'calendar' ? 'วัน' : 'วันทำการ';
                  const pct = b.quota ? Math.min(100, Math.round((b.used / b.quota) * 100)) : 0;
                  const barColor = b.color === 'coral' ? 'var(--coral)' : b.color === 'yellow' ? 'var(--yellow)' : b.color === 'mint' ? 'var(--mint)' : b.color === 'magenta' ? 'var(--magenta)' : 'var(--primary)';
                  return (
                    <div key={b.code} style={{ padding: 14, background: 'var(--surface-2)', borderRadius: 'var(--r-md)' }} title={b.note || ''}>
                      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8, gap: 6 }}>
                        <span className={`gv-chip ${t.chip}`} style={{ whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', maxWidth: '100%' }}>{b.name}</span>
                        {b.pending > 0 && <span style={{ fontSize: 10.5, color: 'var(--yellow-ink)', whiteSpace: 'nowrap' }}>รอ {b.pending}</span>}
                      </div>
                      {!b.unlimited ? (
                        <>
                          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>
                            <span className="tnum" style={{ fontFamily: 'var(--font-display)', fontSize: 26, fontWeight: 700 }}>{b.remaining}</span>
                            <span style={{ fontSize: 11.5, color: 'var(--ink-4)' }}>/ {b.quota} {unit}</span>
                          </div>
                          <div className="gv-bar" style={{ marginTop: 8 }}><i style={{ width: `${pct}%`, background: barColor }} /></div>
                          <div style={{ fontSize: 11, color: 'var(--ink-4)', marginTop: 5 }}>ใช้ไป {b.used}{b.carry ? ` · สะสมยกมา ${b.carry} + ปีนี้ ${b.base}` : ''}{b.reason ? ` · ${b.reason}` : ''}</div>
                        </>
                      ) : <div style={{ fontSize: 12.5, color: 'var(--ink-4)', marginTop: 6 }}>ใช้ไป {b.used} {unit} · ตามที่ได้รับอนุมัติ</div>}
                    </div>
                  );
                })}
                {isStaff && bal.balance.some((b) => b.code === 'vacation') && <VacationCarryEditor empId={balEmp} fy={fy} onSaved={loadBalance}/>}
              </div>}
        </div>
      </div>
      </>)}

      {requesting && <LeaveModal onClose={() => setRequesting(false)} onSaved={() => { setRequesting(false); load(); loadBalance(); }} isStaff={role === 'admin' || role === 'hr'}/>}
    </div>
  );
}

// HR ตั้ง "วันลาพักผ่อนสะสมยกมา" ของปีงบฯ นี้ (ประวัติปีก่อนไม่อยู่ในระบบ จึงต้องกรอกครั้งแรก)
function VacationCarryEditor({ empId, fy, onSaved }) {
  const emp = (window.EMPLOYEES || []).find((e) => String(e.id) === String(empId)) || {};
  const [v, setV] = useLeaveState(Number(emp.vacation_carry_fy) === Number(fy) ? (emp.vacation_carry ?? 0) : 0);
  const [busy, setBusy] = useLeaveState(false);
  useLeaveEffect(() => { setV(Number(emp.vacation_carry_fy) === Number(fy) ? (emp.vacation_carry ?? 0) : 0); }, [empId, fy]);
  const save = async () => {
    setBusy(true);
    const fd = new FormData(); fd.append('vacation_carry', String(Number(v) || 0)); fd.append('vacation_carry_fy', String(fy));
    const r = await fetch(`/api/employees/${encodeURIComponent(empId)}`, { method: 'PUT', credentials: 'include', body: fd });
    setBusy(false);
    if (r.ok) { window.appToast('บันทึกวันสะสมแล้ว', { tone: 'success' }); try { await window.refreshData(); } catch (_) {} onSaved(); }
    else window.appToast('บันทึกไม่สำเร็จ', { tone: 'error' });
  };
  return (
    <div style={{ padding: 14, background: 'var(--surface-2)', borderRadius: 'var(--r-md)', border: '1px dashed var(--line-2)' }}>
      <div style={{ fontSize: 12, color: 'var(--ink-4)', fontWeight: 600 }}>วันลาพักผ่อนสะสมยกมา (ปีงบฯ {fy})</div>
      <div style={{ display: 'flex', gap: 6, marginTop: 8 }}>
        <input className="gv-input" type="number" min="0" max="30" value={v} onChange={(e) => setV(e.target.value)} style={{ width: 80 }}/>
        <button className="gv-btn no sm" onClick={save} disabled={busy}>บันทึก</button>
      </div>
      <div style={{ fontSize: 10.5, color: 'var(--ink-5)', marginTop: 6 }}>ระบบจำกัดรวมไม่เกิน 20 (อายุราชการ ≥10 ปี: 30) วันทำการ</div>
    </div>
  );
}

// ยื่นใบลา — เลือกประเภท → ระบบนับวันทำการ/เช็คสิทธิ์ให้ทันที (GET /api/leaves/preview)
function LeaveModal({ onClose, onSaved, isStaff }) {
  const types = (window.LEAVE_TYPES || []);
  const [empId, setEmpId] = useLeaveState((window.CURRENT_USER && window.CURRENT_USER.employee_id) || '');
  const [type, setType] = useLeaveState((types[0] || {}).code || 'sick');
  const [start, setStart] = useLeaveState(window.TODAY || new Date().toISOString().slice(0, 10));
  const [end, setEnd] = useLeaveState(window.TODAY || new Date().toISOString().slice(0, 10));
  const [reason, setReason] = useLeaveState('');
  const [contact, setContact] = useLeaveState('');
  const [hasDoc, setHasDoc] = useLeaveState(false);
  const [force, setForce] = useLeaveState(false);
  const [preview, setPreview] = useLeaveState(null);
  const [busy, setBusy] = useLeaveState(false);
  const [err, setErr] = useLeaveState('');
  const tmeta = types.find((t) => t.code === type) || {};

  useLeaveEffect(() => {
    if (!empId || !type || !start || !end || end < start) { setPreview(null); return; }
    const ctl = { stop: false };
    const q = new URLSearchParams({ employee_id: empId, leave_type: type, start_date: start, end_date: end });
    fetch('/api/leaves/preview?' + q, { credentials: 'include' }).then((r) => r.ok ? r.json() : null).then((d) => { if (!ctl.stop) setPreview(d); }).catch(() => {});
    return () => { ctl.stop = true; };
  }, [empId, type, start, end]);

  const save = async () => {
    if (!empId) { setErr('กรุณาเลือกพนักงาน'); return; }
    if (end < start) { setErr('วันสิ้นสุดต้องไม่ก่อนวันเริ่ม'); return; }
    setBusy(true); setErr('');
    try {
      const r = await fetch('/api/leaves', { method: 'POST', credentials: 'include',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ employee_id: empId, leave_type: type, start_date: start, end_date: end, reason: reason.trim(), contact: contact.trim(), has_doc: hasDoc, force }) });
      const d = await r.json().catch(() => ({}));
      if (!r.ok) throw new Error(d.error || `error ${r.status}`);
      window.appToast && window.appToast(`ยื่นใบลาแล้ว (${d.days} วัน)`, { tone: 'success' });
      onSaved();
    } catch (e) { setErr(e.message); } finally { setBusy(false); }
  };
  const hasErr = preview && preview.errors && preview.errors.length > 0;
  const unit = (tmeta.count_mode || 'workday') === 'calendar' ? 'วัน' : 'วันทำการ';

  return (
    <div onClick={onClose} style={{ position: 'fixed', inset: 0, zIndex: 200, background: 'rgba(15,23,42,.45)', display: 'grid', placeItems: 'center', padding: 20 }}>
      <div className="gv-card" onClick={(e) => e.stopPropagation()} style={{ width: 'min(560px,100%)', maxHeight: '92vh', overflow: 'auto' }}>
        <div className="gv-card-h"><b>ยื่นใบลา</b></div>
        <div className="gv-card-b" style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
          {isStaff || !(window.CURRENT_USER && window.CURRENT_USER.employee_id) ? (
            <div className="gv-field">
              <label>พนักงาน *</label>
              <select className="gv-select" value={empId} onChange={(e) => setEmpId(e.target.value)}>
                <option value="">— เลือกพนักงาน —</option>
                {(window.EMPLOYEES || []).map((e) => <option key={e.id} value={e.id}>{e.first_name} {e.last_name} ({e.id})</option>)}
              </select>
            </div>
          ) : null}
          <div className="gv-field">
            <label>ประเภทการลา</label>
            <select className="gv-select" value={type} onChange={(e) => setType(e.target.value)}>
              {types.map((t) => <option key={t.code} value={t.code}>{t.name}</option>)}
            </select>
            {tmeta.rules_json && (() => { try { const n = JSON.parse(tmeta.rules_json).note; return n ? <div style={{ fontSize: 11.5, color: 'var(--ink-5)', marginTop: 4 }}>{n}</div> : null; } catch (_) { return null; } })()}
          </div>
          <div style={{ display: 'flex', gap: 12 }}>
            <div className="gv-field" style={{ flex: 1, marginBottom: 0 }}>
              <label>วันเริ่ม</label>
              <input className="gv-input" type="date" value={start} onChange={(e) => { setStart(e.target.value); if (end < e.target.value) setEnd(e.target.value); }}/>
            </div>
            <div className="gv-field" style={{ flex: 1, marginBottom: 0 }}>
              <label>วันสิ้นสุด</label>
              <input className="gv-input" type="date" value={end} min={start} onChange={(e) => setEnd(e.target.value)}/>
            </div>
          </div>
          {preview && (
            <div style={{ padding: '10px 14px', borderRadius: 10, background: hasErr ? 'var(--coral-soft)' : 'var(--mint-soft)', fontSize: 12.5 }}>
              <div style={{ fontWeight: 700, color: hasErr ? 'var(--coral-ink)' : 'var(--mint-ink)' }}>นับได้ {preview.days} {unit}{hasErr ? ' · ยื่นไม่ได้' : ' · ยื่นได้'}</div>
              {(preview.errors || []).map((m, i) => <div key={'e' + i} style={{ color: 'var(--coral-ink)', marginTop: 3 }}>✗ {m}</div>)}
              {(preview.warnings || []).map((m, i) => <div key={'w' + i} style={{ color: 'var(--yellow-ink)', marginTop: 3 }}>⚠ {m}</div>)}
            </div>
          )}
          <div className="gv-field">
            <label>เหตุผล</label>
            <input className="gv-input" value={reason} onChange={(e) => setReason(e.target.value)} placeholder="(ไม่บังคับ — จะพิมพ์ลงในใบลา)"/>
          </div>
          <div className="gv-field">
            <label>ระหว่างลาติดต่อได้ที่</label>
            <input className="gv-input" value={contact} onChange={(e) => setContact(e.target.value)} placeholder="เบอร์โทร / ที่อยู่ (ไม่บังคับ)"/>
          </div>
          <div style={{ display: 'flex', gap: 16, flexWrap: 'wrap', fontSize: 13 }}>
            <label style={{ display: 'flex', alignItems: 'center', gap: 6 }}><input type="checkbox" checked={hasDoc} onChange={(e) => setHasDoc(e.target.checked)}/> แนบใบรับรองแพทย์/เอกสารประกอบ</label>
            {isStaff && hasErr && <label style={{ display: 'flex', alignItems: 'center', gap: 6, color: 'var(--coral-ink)' }}><input type="checkbox" checked={force} onChange={(e) => setForce(e.target.checked)}/> HR ยืนยันบันทึกแม้เกินเงื่อนไข</label>}
          </div>
          {err && <div style={{ padding: '10px 14px', borderRadius: 10, background: 'var(--coral-soft)', color: 'var(--coral-ink)', fontSize: 12.5, fontWeight: 600 }}>{err}</div>}
          <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8, marginTop: 4 }}>
            <button className="gv-btn no" onClick={onClose} disabled={busy}>ยกเลิก</button>
            <button className="gv-btn ok" onClick={save} disabled={busy || (hasErr && !force)}>{busy ? 'กำลังส่ง…' : 'ยื่นใบลา'}</button>
          </div>
        </div>
      </div>
    </div>
  );
}

// ทะเบียนคุมวันลา + สรุปการมาปฏิบัติราชการ ต่อปีงบประมาณ (แยกรอบประเมิน) + ส่งออก Excel
function LeaveRegister({ role }) {
  const [fy, setFy] = useLeaveState(fiscalYearOf());
  const [dept, setDept] = useLeaveState('');
  const [data, setData] = useLeaveState(null);
  const [q, setQ] = useLeaveState('');
  const [onlyFlag, setOnlyFlag] = useLeaveState(false);
  const load = async () => {
    setData(null);
    const p = new URLSearchParams({ fy }); if (dept) p.set('department_id', dept);
    const r = await fetch('/api/leaves/register?' + p, { credentials: 'include' });
    if (r.ok) setData(await r.json());
  };
  useLeaveEffect(() => { load(); }, [fy, dept]);
  const fyOptions = [fiscalYearOf() + 1, fiscalYearOf(), fiscalYearOf() - 1, fiscalYearOf() - 2];
  const mainTypes = data ? data.types.filter((t) => ['sick', 'personal', 'vacation', 'maternity'].includes(t.code)) : [];
  const otherCodes = data ? data.types.filter((t) => !['sick', 'personal', 'vacation', 'maternity'].includes(t.code)).map((t) => t.code) : [];
  const qq = q.trim().toLowerCase();
  const rows = data ? data.rows.filter((r) => (!onlyFlag || r.flags.length) && (!qq || [r.employee_id, r.name, r.department_name, r.position].some((v) => String(v || '').toLowerCase().includes(qq)))) : [];
  const flagged = data ? data.rows.filter((r) => r.flags.length).length : 0;
  const dl = () => { const p = new URLSearchParams({ fy, format: 'xlsx' }); if (dept) p.set('department_id', dept); window.open('/api/leaves/register?' + p, '_blank'); };

  return (
    <div className="gv-card">
      <div className="gv-card-h" style={{ flexWrap: 'wrap', gap: 10 }}>
        <div>
          <b>ทะเบียนคุมวันลา / สรุปการมาปฏิบัติราชการ</b>
          {data && <div style={{ fontSize: 11.5, color: 'var(--ink-4)', marginTop: 2 }}>ปีงบฯ {data.fy} ({data.from} – {data.to}) · {data.rounds[0].label} · {data.rounds[1].label} · เกณฑ์เตือน: ป่วย+กิจ &gt; {data.thresholds.sick_personal_max} วันทำการ, สาย &gt; {data.thresholds.late_max} ครั้ง/รอบ</div>}
        </div>
        <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', alignItems: 'center' }}>
          <input className="gv-input" value={q} onChange={(e) => setQ(e.target.value)} placeholder="ค้นชื่อ / กอง" style={{ width: 160 }}/>
          <select className="gv-select" value={fy} onChange={(e) => setFy(Number(e.target.value))} style={{ width: 'auto' }}>
            {fyOptions.map((y) => <option key={y} value={y}>ปีงบประมาณ {y}</option>)}
          </select>
          {role !== 'manager' && (
            <select className="gv-select" value={dept} onChange={(e) => setDept(e.target.value)} style={{ width: 'auto', maxWidth: 220 }}>
              <option value="">ทุกกอง</option>
              {(window.DEPARTMENTS || []).map((d) => <option key={d.id} value={d.id}>{d.name}</option>)}
            </select>
          )}
          <label style={{ fontSize: 12.5, display: 'flex', alignItems: 'center', gap: 4 }}><input type="checkbox" checked={onlyFlag} onChange={(e) => setOnlyFlag(e.target.checked)}/> เฉพาะที่มีธงเตือน ({flagged})</label>
          <button className="gv-btn no sm" onClick={dl}>⬇ Excel</button>
        </div>
      </div>
      <div className="gv-card-b" style={{ overflowX: 'auto', paddingTop: 6 }}>
        {!data ? <div className="gv-empty">กำลังโหลด…</div> : (
          <table className="gv-tbl" style={{ fontSize: 12.5, minWidth: 980 }}>
            <thead>
              <tr>
                <th style={{ textAlign: 'left' }}>พนักงาน</th>
                {mainTypes.map((t) => <th key={t.code} title={t.count_mode === 'calendar' ? 'วัน' : 'วันทำการ'}>{t.name.replace('ลา', '')}</th>)}
                <th>อื่นๆ</th>
                <th title="ลาป่วย + ลากิจ (วันทำการ)">ป่วย+กิจ</th>
                <th title="สิทธิ์ลาพักผ่อนคงเหลือ">พักผ่อนเหลือ</th>
                <th>มา</th><th>สาย</th><th>ขาด</th>
                <th title="รอบ 1: สาย/ขาด">รอบ 1</th><th title="รอบ 2: สาย/ขาด">รอบ 2</th>
                <th style={{ textAlign: 'left' }}>หมายเหตุ</th>
              </tr>
            </thead>
            <tbody>
              {rows.map((r) => (
                <tr key={r.employee_id} style={r.flags.length ? { background: 'var(--coral-soft)' } : undefined}>
                  <td style={{ whiteSpace: 'nowrap' }}>
                    <div style={{ fontWeight: 600 }}>{r.name}</div>
                    <div style={{ fontSize: 11, color: 'var(--ink-5)' }}>{r.employee_id} · {r.department_name}{r.emp_type ? ` · ${r.emp_type}` : ''}</div>
                  </td>
                  {mainTypes.map((t) => <td key={t.code} className="tnum" style={{ textAlign: 'center' }}>{r.leave[t.code] || '–'}</td>)}
                  <td className="tnum" style={{ textAlign: 'center' }}>{otherCodes.reduce((a, c) => a + (r.leave[c] || 0), 0) || '–'}</td>
                  <td className="tnum" style={{ textAlign: 'center', fontWeight: 700, color: r.sick_personal > data.thresholds.sick_personal_max ? 'var(--coral-ink)' : undefined }}>{r.sick_personal}</td>
                  <td className="tnum" style={{ textAlign: 'center' }}>{r.vacation_remaining ?? '–'}{r.vacation_quota != null ? <span style={{ color: 'var(--ink-5)' }}>/{r.vacation_quota}</span> : ''}</td>
                  <td className="tnum" style={{ textAlign: 'center' }}>{r.present}</td>
                  <td className="tnum" style={{ textAlign: 'center', color: r.late ? 'var(--yellow-ink)' : undefined }}>{r.late || '–'}</td>
                  <td className="tnum" style={{ textAlign: 'center', color: r.absent ? 'var(--coral-ink)' : undefined }}>{r.absent || '–'}</td>
                  <td className="tnum" style={{ textAlign: 'center', whiteSpace: 'nowrap' }}>{r.rounds[0].late}/{r.rounds[0].absent}</td>
                  <td className="tnum" style={{ textAlign: 'center', whiteSpace: 'nowrap' }}>{r.rounds[1].late}/{r.rounds[1].absent}</td>
                  <td style={{ fontSize: 11.5, color: 'var(--coral-ink)' }}>{r.flags.join(' · ')}</td>
                </tr>
              ))}
              {rows.length === 0 && <tr><td colSpan={12}><div className="gv-empty">— ไม่มีรายการ —</div></td></tr>}
            </tbody>
          </table>
        )}
      </div>
    </div>
  );
}

// HR/admin approval of "ลืมสแกน" time-correction requests.
function CorrectionsPanel() {
  const [list, setList] = useLeaveState([]);
  const load = async () => {
    const r = await fetch('/api/corrections?status=pending', { credentials: 'include' });
    if (r.ok) setList(await r.json());
  };
  useLeaveEffect(() => { load(); }, []);
  const act = async (id, action) => {
    if (action === 'reject') {
      const reason = await window.appPrompt({ title: 'เหตุผลที่ปฏิเสธ', placeholder: '(ไม่บังคับ)' });
      if (reason === null) return;
      await fetch(`/api/corrections/${id}/reject`, { method: 'PUT', credentials: 'include', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ reason }) });
      window.appToast && window.appToast('ปฏิเสธแล้ว', { tone: 'info' });
    } else {
      const r = await fetch(`/api/corrections/${id}/approve`, { method: 'PUT', credentials: 'include' });
      const d = await r.json().catch(() => ({}));
      if (!r.ok) { window.appToast && window.appToast(d.error || 'อนุมัติไม่สำเร็จ', { tone: 'error' }); return; }
      window.appToast && window.appToast('อนุมัติ + ลงเวลาให้แล้ว', { tone: 'success' });
    }
    load();
  };
  if (list.length === 0) return null;
  return (
    <div className="gv-card" style={{ marginBottom: 16 }}>
      <div className="gv-card-h">
        <b>⏱️ ขอแก้เวลา (ลืมสแกน)</b>
        <span className="gv-chip c-amber">{list.length} รออนุมัติ</span>
      </div>
      <div className="gv-card-b" style={{ paddingTop: 6 }}>
        {list.map((c) => (
          <div key={c.id} className="gv-row">
            <LvAvatar id={c.employee_id} size={40} />
            <div style={{ minWidth: 140 }}>
              <div style={{ fontSize: 14, fontWeight: 600 }}>{`${c.first_name || ''} ${c.last_name || ''}`.trim() || c.employee_id}</div>
              <div style={{ fontSize: 11.5, color: 'var(--ink-4)', marginTop: 2 }}>{c.employee_id}{c.department_name ? ` · ${c.department_name}` : ''}</div>
            </div>
            <div style={{ minWidth: 140 }}>
              <div style={{ fontSize: 13.5 }}><span style={{ color: c.punch_type === 'out' ? 'var(--primary)' : 'var(--mint-ink)', fontWeight: 600 }}>{c.punch_type === 'out' ? 'ออกงาน' : 'เข้างาน'}</span> {c.proposed_time}</div>
              <div style={{ fontSize: 11.5, color: 'var(--ink-4)', marginTop: 2 }}>{c.date}</div>
            </div>
            <div style={{ flex: 1, fontSize: 13, color: 'var(--ink-4)', minWidth: 0 }}>{c.reason || '—'}</div>
            <div style={{ display: 'flex', gap: 6, flexShrink: 0 }}>
              <button className="gv-btn no sm" onClick={() => act(c.id, 'reject')}>ปฏิเสธ</button>
              <button className="gv-btn ok sm" onClick={() => act(c.id, 'approve')}>อนุมัติ</button>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

window.LeavePage = LeavePage;
