// hrm-reports.jsx — BÁO CÁO NHÂN SỰ (4 nhóm theo tab), nạp SAU hrm.jsx (dùng useHrm/OtMeter của file đó).
// Khung kỳ + biểu đồ + xuất Excel/PDF: dùng CHUNG reports.jsx (y hệt báo cáo Hợp đồng) → thanh lọc kỳ
// (Tháng này · Quý trước · Q1–4 · Năm nay + khoảng ngày tuỳ ý) nằm trên cùng cho MỌI tab.
//
// Chỉ ĐỌC, tính client-side từ state. Nguyên tắc: KHÔNG bịa số — thiếu dữ liệu thì nói "chưa có",
// không suy diễn (vd đánh giá sau đào tạo chỉ tính NV có điểm cả trước lẫn sau khóa học).
//
// Nhóm "Tuyển dụng" CỐ Ý không có: user chốt bỏ (2026-07-16) — hệ thống cũng chưa có dữ liệu ứng viên/phễu.

/* ============ mô hình số liệu (tách khỏi view → dùng chung cho cả bảng hiển thị lẫn file Excel) ============ */
function hrmGroupCount(rows, keyOf){
  const m = {};
  rows.forEach(r => { const k = keyOf(r) || '—'; m[k] = (m[k] || 0) + 1; });
  return Object.keys(m).sort((a, b) => m[b] - m[a]).map(k => ({ label:k, value:m[k] }));
}
function hrmLastDayOf(ymKey){ return ymKey + '-' + String(new Date(+ymKey.slice(0, 4), +ymKey.slice(5), 0).getDate()).padStart(2, '0'); }

// --- 1. Tổng quan & cơ cấu ---
function hrmRepStructure(data, period, lang){
  const emps = data.employees || [];
  const head = emps.filter(e => hrmActiveAt(e, period.to));
  const tv = hrmTurnover(emps, period);
  const byDept = hrmGroupCount(head, e => e.dept);
  const byGender = Object.keys(HRM_GENDERS).map((g, i) => ({
    label:HRM_GENDERS[g][lang] || HRM_GENDERS[g].vi, value:head.filter(e => e.gender === g).length, color:REPORT_COLORS[i],
  })).filter(s => s.value);
  const byAge = HRM_AGE_BANDS.map(b => ({ label:b[lang] || b.vi,
    value:head.filter(e => { const a = hrmAge(e, period.to); return a != null && a >= b.min && a <= b.max; }).length }));
  const bySen = HRM_SENIORITY_BANDS.map(b => ({ label:b[lang] || b.vi,
    value:head.filter(e => { const y = hrmYearsOfService(e, period.to); return y >= b.min && y <= b.max; }).length }));
  const byEdu = hrmGroupCount(head, e => e.education || (lang === 'vi' ? 'Chưa cập nhật' : 'Not set'));
  const byStatus = Object.keys(HRM_STATUS).map((k, i) => ({ label:HRM_STATUS[k][lang] || HRM_STATUS[k].vi,
    value:head.filter(e => e.status === k).length, color:REPORT_COLORS[i] })).filter(s => s.value);
  // Xu hướng quân số: đếm người còn làm việc tại NGÀY CUỐI mỗi tháng trong kỳ.
  const months = monthsInPeriod(period), trend = {};
  months.forEach(m => { trend[m.key] = emps.filter(e => hrmActiveAt(e, hrmLastDayOf(m.key))).length; });
  const leaveByType = Object.keys(HRM_END_TYPES).map((k, i) => ({ label:HRM_END_TYPES[k][lang] || HRM_END_TYPES[k].vi,
    value:tv.byType[k] || 0, color:REPORT_COLORS[i] })).filter(s => s.value);
  return { emps, head, tv, byDept, byGender, byAge, bySen, byEdu, byStatus, months, trend, leaveByType };
}

// --- 2. Chấm công & thời gian làm việc ---
function hrmRepTime(data, st, period){
  // Gồm cả người đã nghỉ TRONG kỳ (công của họ vẫn thuộc kỳ này).
  const emps = (data.employees || []).filter(e => hrmActiveAt(e, period.to) || (e.endDate && e.endDate >= period.from && e.endDate <= period.to));
  const year = +period.to.slice(0, 4);
  const rows = emps.map(e => {
    const a = hrmAttendanceStats(e, period, data, st);
    const otRows = (data.requests || []).filter(r => r.kind === 'ot' && r.employeeId === e.id && r.status === 'approved'
      && (r.startDate || '') >= period.from && (r.startDate || '') <= period.to);
    const otH = Math.round(otRows.reduce((s, r) => s + (+r.hours || 0), 0) * 10) / 10;
    const nightH = Math.round(otRows.reduce((s, r) => s + hrmNightHours(r.startTime, r.endTime, st), 0) * 10) / 10;
    const quota = hrmLeaveQuota(e, year, st), used = hrmLeaveUsed(data.requests, e.id, year);
    return Object.assign({ e, otH, nightH, quota, used, left:Math.round((quota - used) * 10) / 10,
      otYear:hrmOtTotals(data.requests, e, year + '-12-31', st).year }, a);
  }).sort((x, y) => y.absent - x.absent || y.late - x.late);
  const T = (k) => Math.round(rows.reduce((s, r) => s + (r[k] || 0), 0) * 10) / 10;
  const totals = { standard:T('standard'), present:T('present'), leaveDays:T('leaveDays'), absent:T('absent'),
    late:T('late'), lateMin:T('lateMin'), early:T('early'), earlyMin:T('earlyMin'), otH:T('otH'), nightH:T('nightH') };
  totals.absentPct = totals.standard ? Math.round(totals.absent / totals.standard * 1000) / 10 : 0;
  const deptOt = {};
  rows.forEach(r => { const d = r.e.dept || '—'; deptOt[d] = (deptOt[d] || 0) + r.otH; });
  const otRank = Object.keys(deptOt).map(k => ({ label:k, value:Math.round(deptOt[k] * 10) / 10 }))
    .filter(x => x.value).sort((a, b) => b.value - a.value);
  return { rows, totals, otRank, year };
}

// --- 3. Lương thưởng & phúc lợi ---
function hrmRepPay(data, p, period){
  const months = monthsInPeriod(period), keys = months.map(m => m.key);
  // CHỈ kỳ lương đã CHỐT mới vào báo cáo chi phí (bản nháp còn sửa được → không phải số liệu tài chính).
  const finalized = new Set((data.payruns || []).filter(r => r.status === 'finalized').map(r => r.id));
  const slips = (data.payslips || []).filter(s => finalized.has(s.payrunId) && keys.indexOf(s.period) >= 0);
  let base = 0, allow = 0, ot = 0, gross = 0, net = 0, bhxh = 0, bhyt = 0, bhtn = 0, pit = 0, employerIns = 0;
  const rows = slips.map(s => {
    const d = s.detail || {}, i = d.insurance || {};
    base += +d.baseAmount || 0; allow += +d.allowTotal || 0; ot += +d.otTotal || 0; gross += +d.gross || 0;
    net += hrmPayslipNet(d);
    bhxh += +i.bhxh || 0; bhyt += +i.bhyt || 0; bhtn += +i.bhtn || 0; pit += +d.pit || 0;
    employerIns += Math.round((+d.siBase || 0) * HRM_EMPLOYER_INS_RATE);
    return { s, d, name:hrmEmpName(data.employees, s.employeeId) };
  }).sort((a, b) => (b.d.gross || 0) - (a.d.gross || 0));
  const byMonth = {};
  slips.forEach(s => { byMonth[s.period] = (byMonth[s.period] || 0) + (+(s.detail || {}).gross || 0); });
  // Doanh thu = tiền THỰC THU trong kỳ (lịch thanh toán HĐ/BG bán) — dùng lại incomeItems của module Tài chính.
  const revenue = (typeof incomeItems === 'function' ? incomeItems(p.contracts || [], p.quotes || []) : [])
    .filter(x => x.paid && inPeriod(x.paidDate || x.dueDate, period))
    .reduce((s, x) => s + (+x.amount || 0), 0);
  const cost = gross + employerIns;   // chi phí NS thực của DN = tổng thu nhập trả NV + phần DN đóng BH
  return { slips, rows, months, byMonth, base, allow, ot, gross, net, pit, employerIns, cost, revenue,
    ins:{ bhxh, bhyt, bhtn, total:bhxh + bhyt + bhtn },
    ratio:revenue ? Math.round(cost / revenue * 1000) / 10 : null };
}

// --- 4. Đào tạo & hiệu suất ---
function hrmRepTraining(data, period, lang){
  const trs = (data.trainings || []).filter(t => inPeriod(t.toDate || t.fromDate, period));
  const cost = trs.reduce((s, t) => s + (+t.cost || 0), 0);
  const hours = trs.reduce((s, t) => s + (+t.hours || 0), 0);
  const people = Object.keys(trs.reduce((m, t) => { m[t.employeeId] = 1; return m; }, {})).length;
  const revs = (data.reviews || []).filter(r => inPeriod(r.reviewDate, period));
  const byRating = Object.keys(HRM_REVIEW_RATINGS).map(k => ({ label:HRM_REVIEW_RATINGS[k][lang] || HRM_REVIEW_RATINGS[k].vi,
    value:revs.filter(r => r.rating === k).length, color:HRM_REVIEW_RATINGS[k].color })).filter(s => s.value);
  const scored = revs.filter(r => r.score != null);
  const avgScore = scored.length ? Math.round(scored.reduce((s, r) => s + (+r.score), 0) / scored.length * 10) / 10 : null;
  const deptM = {};
  scored.forEach(r => { const e = (data.employees || []).find(x => x.id === r.employeeId); const d = (e && e.dept) || '—';
    (deptM[d] = deptM[d] || []).push(+r.score); });
  const deptRank = Object.keys(deptM).map(k => ({ label:k,
    value:Math.round(deptM[k].reduce((s, v) => s + v, 0) / deptM[k].length * 10) / 10 })).sort((a, b) => b.value - a.value);
  return { trs, cost, hours, people, revs, byRating, avgScore, deptRank, scored,
    effect:hrmTrainingEffect(data.trainings, data.reviews, period),
    costPerHead:people ? Math.round(cost / people) : 0, hoursPerHead:people ? Math.round(hours / people * 10) / 10 : 0 };
}

/* ============ view: 1. Tổng quan & cơ cấu ============ */
function HrmRepStructure({ m }){
  const { lang } = useLang();
  const L = (vi, en) => (lang === 'vi' ? vi : en);
  const tv = m.tv;
  return (<>
    <div className="kpi-strip">
      <Kpi label={L('Nhân sự cuối kỳ', 'Headcount at period end')} value={tv.head1} icon="bi-people" />
      <Kpi label={L('Vào mới trong kỳ', 'New hires')} value={tv.hires.length} icon="bi-person-plus" />
      <Kpi label={L('Nghỉ việc trong kỳ', 'Leavers')} value={tv.leavers.length} icon="bi-person-dash" />
      <Kpi label={L('Tỷ lệ nghỉ việc', 'Turnover rate')} value={tv.turnoverPct + '%'} icon="bi-graph-down-arrow" />
    </div>
    <div className="dash-grid">
      <div className="card span2">
        <div className="card-head"><h3>{L('Quân số theo tháng', 'Headcount by month')}</h3>
          <span className="subtle" style={{ fontSize:'var(--fs-sm)' }}>{L('Số người còn làm việc tại ngày cuối mỗi tháng', 'Active at each month end')}</span></div>
        <div className="card-body"><ReportBars months={m.months} series={m.trend} /></div>
      </div>
      <div className="card">
        <div className="card-head"><h3>{L('Giới tính', 'Gender')}</h3></div>
        <div className="card-body"><ReportDonut slices={m.byGender} /></div>
      </div>
      <div className="card">
        <div className="card-head"><h3>{L('Theo phòng ban', 'By department')}</h3></div>
        <div className="card-body"><ReportRank rows={m.byDept} /></div>
      </div>
      <div className="card">
        <div className="card-head"><h3>{L('Độ tuổi', 'Age')}</h3></div>
        <div className="card-body"><ReportRank rows={m.byAge.filter(x => x.value)} /></div>
      </div>
      <div className="card">
        <div className="card-head"><h3>{L('Thâm niên', 'Seniority')}</h3></div>
        <div className="card-body"><ReportRank rows={m.bySen.filter(x => x.value)} /></div>
      </div>
      <div className="card">
        <div className="card-head"><h3>{L('Trình độ học vấn', 'Education')}</h3></div>
        <div className="card-body"><ReportRank rows={m.byEdu} /></div>
      </div>
      <div className="card">
        <div className="card-head"><h3>{L('Trạng thái làm việc', 'Employment status')}</h3></div>
        <div className="card-body"><ReportDonut slices={m.byStatus} /></div>
      </div>
      <div className="card">
        <div className="card-head"><h3>{L('Lý do nghỉ việc', 'Reason for leaving')}</h3></div>
        <div className="card-body">
          {m.leaveByType.length ? <ReportDonut slices={m.leaveByType} />
            : <EmptyState icon="bi-emoji-smile" text={L('Không có ai nghỉ việc trong kỳ', 'No leavers in this period')} />}
        </div>
      </div>
      <div className="card span3">
        <div className="card-head"><h3>{L('Chi tiết biến động trong kỳ', 'Movements in period')}</h3>
          <span className="subtle" style={{ fontSize:'var(--fs-sm)' }}>
            {L('Đầu kỳ ' + tv.head0 + ' → cuối kỳ ' + tv.head1 + ' người · giữ chân ' + tv.retentionPct + '%',
               'Start ' + tv.head0 + ' → end ' + tv.head1 + ' · retention ' + tv.retentionPct + '%')}</span></div>
        <div className="card-body" style={{ overflow:'auto' }}>
          {(tv.hires.length + tv.leavers.length) ? (
            <table className="tbl">
              <thead><tr><th>{L('Nhân viên', 'Employee')}</th><th>{L('Phòng ban', 'Department')}</th>
                <th>{L('Biến động', 'Movement')}</th><th>{L('Ngày', 'Date')}</th><th>{L('Phân loại', 'Type')}</th>
                <th>{L('Lý do', 'Reason')}</th></tr></thead>
              <tbody>
                {tv.hires.map(e => (
                  <tr key={'h' + e.id} style={{ cursor:'default' }}>
                    <td style={{ fontWeight:500 }}>{e.fullName}<div className="subtle mono" style={{ fontSize:'var(--fs-sm)' }}>{e.id}</div></td>
                    <td>{e.dept || '—'}</td>
                    <td><span className="badge badge-success"><i className="dot"></i>{L('Vào mới', 'Hired')}</span></td>
                    <td className="mono">{fmtDate(e.startDate)}</td><td className="subtle">—</td><td className="subtle">{e.position || '—'}</td>
                  </tr>
                ))}
                {tv.leavers.map(e => {
                  const et = HRM_END_TYPES[e.endType];
                  return (
                    <tr key={'l' + e.id} style={{ cursor:'default' }}>
                      <td style={{ fontWeight:500 }}>{e.fullName}<div className="subtle mono" style={{ fontSize:'var(--fs-sm)' }}>{e.id}</div></td>
                      <td>{e.dept || '—'}</td>
                      <td><span className="badge badge-error"><i className="dot"></i>{L('Nghỉ việc', 'Left')}</span></td>
                      <td className="mono">{fmtDate(e.endDate)}</td>
                      <td>{et ? <span className={'badge ' + et.cls}><i className="dot"></i>{et[lang] || et.vi}</span>
                        : <span className="subtle">{L('Chưa phân loại', 'Not set')}</span>}</td>
                      <td className="subtle">{e.endReason || '—'}</td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          ) : <EmptyState icon="bi-people" text={L('Không có nhân sự vào mới hay nghỉ việc trong kỳ', 'No movements in this period')} />}
        </div>
      </div>
    </div>
  </>);
}

/* ============ view: 2. Chấm công ============ */
function HrmRepTime({ m, st }){
  const { lang } = useLang();
  const L = (vi, en) => (lang === 'vi' ? vi : en);
  const t = m.totals;
  const hm = (min) => Math.floor(min / 60) + 'h' + String(Math.round(min % 60)).padStart(2, '0');
  return (<>
    <div className="kpi-strip">
      <Kpi label={L('Tỷ lệ vắng không phép', 'Absenteeism rate')} value={t.absentPct + '%'} icon="bi-calendar-x" />
      <Kpi label={L('Lượt đi trễ', 'Late arrivals')} value={t.late} icon="bi-clock" />
      <Kpi label={L('Lượt về sớm', 'Early leaves')} value={t.early} icon="bi-box-arrow-right" />
      <Kpi label={L('Tổng giờ làm thêm', 'Total OT hours')} value={t.otH + 'h'} icon="bi-moon-stars" />
    </div>
    <div className="dash-grid">
      <div className="card span2">
        <div className="card-head"><h3>{L('Giờ làm thêm theo phòng ban', 'OT hours by department')}</h3>
          <span className="subtle" style={{ fontSize:'var(--fs-sm)' }}>{L('Phòng nào đang quá tải', 'Spot overloaded teams')}</span></div>
        <div className="card-body">{m.otRank.length ? <ReportRank rows={m.otRank} />
          : <EmptyState icon="bi-moon-stars" text={L('Không có giờ làm thêm được duyệt trong kỳ', 'No approved OT in this period')} />}</div>
      </div>
      <div className="card">
        <div className="card-head"><h3>{L('Tổng hợp kỳ', 'Period totals')}</h3></div>
        <div className="card-body col g8" style={{ fontSize:'var(--fs-base)' }}>
          <div className="flex justify-between"><span className="subtle">{L('Ngày công chuẩn', 'Standard days')}</span><span className="mono" style={{ fontWeight:600 }}>{t.standard}</span></div>
          <div className="flex justify-between"><span className="subtle">{L('Ngày có mặt', 'Present days')}</span><span className="mono" style={{ fontWeight:600 }}>{t.present}</span></div>
          <div className="flex justify-between"><span className="subtle">{L('Ngày nghỉ có phép', 'Approved leave days')}</span><span className="mono" style={{ fontWeight:600 }}>{t.leaveDays}</span></div>
          <div className="flex justify-between"><span className="subtle">{L('Ngày vắng không phép', 'Unexcused absence')}</span><span className="mono" style={{ fontWeight:700, color:t.absent > 0 ? 'var(--warning)' : 'inherit' }}>{t.absent}</span></div>
          <div className="flex justify-between" style={{ paddingTop:8, borderTop:'1px solid var(--border-light)' }}>
            <span className="subtle">{L('Tổng phút đi trễ', 'Total late minutes')}</span><span className="mono" style={{ fontWeight:600 }}>{hm(t.lateMin)}</span></div>
          <div className="flex justify-between"><span className="subtle">{L('Tổng phút về sớm', 'Total early minutes')}</span><span className="mono" style={{ fontWeight:600 }}>{hm(t.earlyMin)}</span></div>
          <div className="flex justify-between"><span className="subtle">{L('Giờ làm thêm ban đêm', 'Night OT hours')}</span><span className="mono" style={{ fontWeight:600 }}>{t.nightH}h</span></div>
          <div className="subtle" style={{ fontSize:'var(--fs-sm)', marginTop:2, lineHeight:1.5 }}>
            {L('Giờ ca chuẩn đang áp: ' + (st.shiftStart || '08:00') + '–' + (st.shiftEnd || '17:30') + ' (đổi ở Thiết lập).',
               'Shift: ' + (st.shiftStart || '08:00') + '–' + (st.shiftEnd || '17:30') + ' (change in Settings).')}
            <br />{L('Chỉ tính từ ngày mỗi người bắt đầu có dữ liệu chấm công — quãng trước đó không đo được nên không tính là vắng.',
                     'Measured only from each person’s first attendance record.')}</div>
        </div>
      </div>
      <div className="card span3">
        <div className="card-head"><h3>{L('Chi tiết theo nhân viên', 'By employee')}</h3></div>
        <div className="card-body" style={{ overflow:'auto' }}>
          <table className="tbl">
            <thead><tr><th>{L('Nhân viên', 'Employee')}</th>
              <th className="num">{L('Công chuẩn', 'Standard')}</th><th className="num">{L('Có mặt', 'Present')}</th>
              <th className="num">{L('Phép', 'Leave')}</th><th className="num">{L('Vắng KP', 'Absent')}</th>
              <th className="num">{L('% vắng', '% absent')}</th><th className="num">{L('Trễ', 'Late')}</th>
              <th className="num">{L('Về sớm', 'Early')}</th><th className="num">{L('Giờ OT', 'OT')}</th>
              <th className="num">{L('Giờ đêm', 'Night')}</th><th className="num">{L('Phép còn', 'Leave left')}</th>
              <th>{L('OT năm vs trần', 'OT vs cap')}</th></tr></thead>
            <tbody>
              {m.rows.length === 0 && <tr><td colSpan="12"><EmptyState icon="bi-clock-history" text={L('Không có dữ liệu công trong kỳ', 'No attendance data')} /></td></tr>}
              {m.rows.map(r => (
                <tr key={r.e.id} style={{ cursor:'default' }}>
                  <td style={{ fontWeight:500 }}>{r.e.fullName}<div className="subtle" style={{ fontSize:'var(--fs-sm)' }}>{r.e.dept || '—'}</div></td>
                  <td className="num mono">{r.standard}</td><td className="num mono">{r.present}</td>
                  <td className="num mono">{r.leaveDays}</td>
                  <td className="num mono" style={{ fontWeight:600, color:r.absent > 0 ? 'var(--warning)' : 'inherit' }}>{r.absent}</td>
                  <td className="num mono">{r.absentPct}%</td>
                  <td className="num mono">{r.late ? r.late + ' (' + hm(r.lateMin) + ')' : '—'}</td>
                  <td className="num mono">{r.early ? r.early + ' (' + hm(r.earlyMin) + ')' : '—'}</td>
                  <td className="num mono" style={{ fontWeight:600 }}>{r.otH}</td>
                  <td className="num mono">{r.nightH}</td>
                  <td className="num mono" style={{ fontWeight:600, color:r.left <= 0 ? 'var(--error)' : 'inherit' }}>{r.left}</td>
                  <td style={{ minWidth:150 }}><OtMeter label="" used={r.otYear.used} cap={r.otYear.cap} level={r.otYear.level} /></td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </div>
    </div>
  </>);
}

/* ============ view: 3. Lương thưởng & phúc lợi ============ */
function HrmRepPay({ m }){
  const { lang } = useLang();
  const L = (vi, en) => (lang === 'vi' ? vi : en);
  if (!m.slips.length) return (
    <EmptyState icon="bi-cash-stack" text={L('Chưa có kỳ lương nào được CHỐT trong khoảng thời gian này. Vào Bảng lương để tạo và chốt kỳ lương — báo cáo sẽ tự có số liệu.',
      'No finalized payroll run in this period. Create and finalize a run in Payroll to populate this report.')} />
  );
  const mix = [
    { label:L('Lương theo công', 'Base pay'), value:m.base, color:REPORT_COLORS[0] },
    { label:L('Phụ cấp', 'Allowances'), value:m.allow, color:REPORT_COLORS[1] },
    { label:L('Làm thêm giờ', 'Overtime'), value:m.ot, color:REPORT_COLORS[2] },
  ].filter(s => s.value);
  return (<>
    <div className="kpi-strip">
      <Kpi label={L('Tổng thu nhập (gross)', 'Gross payroll')} value={vndShort(m.gross)} icon="bi-cash-stack" />
      <Kpi label={L('Thực lĩnh (net)', 'Net paid')} value={vndShort(m.net)} icon="bi-wallet2" />
      <Kpi label={L('Chi phí nhân sự', 'Total HR cost')} value={vndShort(m.cost)} icon="bi-building" />
      <Kpi label={L('Trên doanh thu', 'Of revenue')} value={m.ratio == null ? '—' : m.ratio + '%'} icon="bi-percent" />
    </div>
    <div className="dash-grid">
      <div className="card span2">
        <div className="card-head"><h3>{L('Quỹ lương theo tháng', 'Payroll by month')}</h3>
          <span className="subtle" style={{ fontSize:'var(--fs-sm)' }}>{L('Tổng thu nhập các kỳ đã chốt', 'Gross of finalized runs')}</span></div>
        <div className="card-body"><ReportBars months={m.months} series={m.byMonth} money={true} /></div>
      </div>
      <div className="card">
        <div className="card-head"><h3>{L('Cơ cấu thu nhập', 'Pay mix')}</h3></div>
        <div className="card-body"><ReportDonut slices={mix} money={true} /></div>
      </div>
      <div className="card">
        <div className="card-head"><h3>{L('Chi phí nhân sự trên doanh thu', 'HR cost vs revenue')}</h3></div>
        <div className="card-body col g8" style={{ fontSize:'var(--fs-base)' }}>
          <div className="flex justify-between"><span className="subtle">{L('Doanh thu thực thu', 'Revenue collected')}</span><span className="mono" style={{ fontWeight:600 }}>{vndShort(m.revenue)}</span></div>
          <div className="flex justify-between"><span className="subtle">{L('Tổng thu nhập trả NV', 'Gross paid')}</span><span className="mono" style={{ fontWeight:600 }}>{vndShort(m.gross)}</span></div>
          <div className="flex justify-between"><span className="subtle">{L('DN đóng bảo hiểm (21.5%)', 'Employer insurance')}</span><span className="mono" style={{ fontWeight:600 }}>{vndShort(m.employerIns)}</span></div>
          <div className="flex justify-between" style={{ paddingTop:8, borderTop:'1px solid var(--border-light)' }}>
            <span style={{ fontWeight:600 }}>{L('Tỷ trọng', 'Ratio')}</span>
            <span className="mono" style={{ fontWeight:700, color:'var(--primary)' }}>{m.ratio == null ? '—' : m.ratio + '%'}</span></div>
          {m.ratio == null && <div className="subtle" style={{ fontSize:'var(--fs-sm)' }}>{L('Chưa có khoản thu nào trong kỳ nên không tính được tỷ trọng.', 'No revenue collected in this period.')}</div>}
        </div>
      </div>
      <div className="card">
        <div className="card-head"><h3>{L('Bảo hiểm & thuế', 'Insurance & tax')}</h3></div>
        <div className="card-body col g8" style={{ fontSize:'var(--fs-base)' }}>
          <div className="flex justify-between"><span className="subtle">{L('BHXH (NV đóng 8%)', 'SI (employee 8%)')}</span><span className="mono" style={{ fontWeight:600 }}>{vndShort(m.ins.bhxh)}</span></div>
          <div className="flex justify-between"><span className="subtle">{L('BHYT (1.5%)', 'Health (1.5%)')}</span><span className="mono" style={{ fontWeight:600 }}>{vndShort(m.ins.bhyt)}</span></div>
          <div className="flex justify-between"><span className="subtle">{L('BHTN (1%)', 'Unemployment (1%)')}</span><span className="mono" style={{ fontWeight:600 }}>{vndShort(m.ins.bhtn)}</span></div>
          <div className="flex justify-between" style={{ paddingTop:8, borderTop:'1px solid var(--border-light)' }}>
            <span style={{ fontWeight:600 }}>{L('NV đóng — tổng', 'Employee total')}</span><span className="mono" style={{ fontWeight:700 }}>{vndShort(m.ins.total)}</span></div>
          <div className="flex justify-between"><span className="subtle">{L('DN đóng (21.5%)', 'Employer (21.5%)')}</span><span className="mono" style={{ fontWeight:600 }}>{vndShort(m.employerIns)}</span></div>
          <div className="flex justify-between" style={{ paddingTop:8, borderTop:'1px solid var(--border-light)' }}>
            <span style={{ fontWeight:600 }}>{L('Thuế TNCN', 'Personal income tax')}</span><span className="mono" style={{ fontWeight:700, color:'var(--primary)' }}>{vndShort(m.pit)}</span></div>
        </div>
      </div>
      <div className="card span3">
        <div className="card-head"><h3>{L('Chi tiết theo nhân viên', 'By employee')}</h3>
          <span className="subtle" style={{ fontSize:'var(--fs-sm)' }}>{m.slips.length} {L('phiếu lương', 'payslips')}</span></div>
        <div className="card-body" style={{ overflow:'auto' }}>
          <table className="tbl">
            <thead><tr><th>{L('Nhân viên', 'Employee')}</th><th>{L('Kỳ', 'Period')}</th>
              <th className="num">{L('Lương theo công', 'Base')}</th><th className="num">{L('Phụ cấp', 'Allow.')}</th>
              <th className="num">{L('Làm thêm', 'OT')}</th><th className="num">{L('Tổng thu nhập', 'Gross')}</th>
              <th className="num">{L('BH (NV)', 'Insurance')}</th><th className="num">{L('Thuế TNCN', 'PIT')}</th>
              <th className="num">{L('Thực lĩnh', 'Net')}</th></tr></thead>
            <tbody>{m.rows.map(({ s, d, name }) => (
              <tr key={s.id} style={{ cursor:'default' }}>
                <td style={{ fontWeight:500 }}>{name}<div className="subtle mono" style={{ fontSize:'var(--fs-sm)' }}>{s.employeeId}</div></td>
                <td className="mono">{s.period}</td>
                <td className="num mono">{vndShort(d.baseAmount)}</td><td className="num mono">{vndShort(d.allowTotal)}</td>
                <td className="num mono">{vndShort(d.otTotal)}</td>
                <td className="num mono" style={{ fontWeight:600 }}>{vndShort(d.gross)}</td>
                <td className="num mono">{vndShort((d.insurance || {}).total)}</td><td className="num mono">{vndShort(d.pit)}</td>
                <td className="num mono" style={{ fontWeight:700, color:'var(--primary)' }}>{vndShort(hrmPayslipNet(d))}</td>
              </tr>
            ))}</tbody>
          </table>
        </div>
      </div>
    </div>
  </>);
}

/* ============ view: 4. Đào tạo & hiệu suất ============ */
function HrmRepTraining({ m, data }){
  const { lang } = useLang();
  const L = (vi, en) => (lang === 'vi' ? vi : en);
  return (<>
    <div className="kpi-strip">
      <Kpi label={L('Lượt đào tạo', 'Trainings')} value={m.trs.length} icon="bi-mortarboard" />
      <Kpi label={L('Chi phí đào tạo', 'Training cost')} value={vndShort(m.cost)} icon="bi-cash-coin" />
      <Kpi label={L('Giờ học TB/người', 'Avg hours/head')} value={m.hoursPerHead + 'h'} icon="bi-hourglass-split" />
      <Kpi label={L('Điểm hiệu suất TB', 'Avg score')} value={m.avgScore == null ? '—' : m.avgScore} icon="bi-speedometer2" />
    </div>
    <div className="dash-grid">
      <div className="card">
        <div className="card-head"><h3>{L('Kết quả đánh giá', 'Review outcomes')}</h3></div>
        <div className="card-body">{m.byRating.length ? <ReportDonut slices={m.byRating} />
          : <EmptyState icon="bi-clipboard-check" text={L('Chưa có đánh giá nào chốt trong kỳ', 'No reviews in this period')} />}</div>
      </div>
      <div className="card span2">
        <div className="card-head"><h3>{L('Điểm hiệu suất trung bình theo phòng ban', 'Avg score by department')}</h3>
          <span className="subtle" style={{ fontSize:'var(--fs-sm)' }}>{L('Thang điểm 100', 'Out of 100')}</span></div>
        <div className="card-body">{m.deptRank.length ? <ReportRank rows={m.deptRank} max={100} />
          : <EmptyState icon="bi-speedometer2" text={L('Chưa có điểm đánh giá trong kỳ', 'No scores in this period')} />}</div>
      </div>
      <div className="card span3">
        <div className="card-head"><h3>{L('Các khóa đào tạo trong kỳ', 'Trainings in period')}</h3>
          <span className="subtle" style={{ fontSize:'var(--fs-sm)' }}>
            {m.people ? L(m.people + ' người · chi phí TB ' + vnd(m.costPerHead) + '/người', m.people + ' people · avg ' + vnd(m.costPerHead) + '/head') : ''}</span></div>
        <div className="card-body" style={{ overflow:'auto' }}>
          {m.trs.length ? (
            <table className="tbl">
              <thead><tr><th>{L('Khóa học', 'Course')}</th><th>{L('Nhân viên', 'Employee')}</th>
                <th>{L('Đơn vị đào tạo', 'Provider')}</th><th>{L('Thời gian', 'Dates')}</th>
                <th className="num">{L('Số giờ', 'Hours')}</th><th className="num">{L('Chi phí', 'Cost')}</th>
                <th>{L('Kết quả', 'Result')}</th></tr></thead>
              <tbody>{m.trs.map(t => (
                <tr key={t.id} style={{ cursor:'default' }}>
                  <td style={{ fontWeight:500 }}>{t.name}</td>
                  <td>{hrmEmpName(data.employees, t.employeeId)}</td>
                  <td className="subtle">{t.org || '—'}</td>
                  <td className="mono">{fmtDate(t.fromDate)}{t.toDate ? ' – ' + fmtDate(t.toDate) : ''}</td>
                  <td className="num mono">{t.hours != null ? t.hours : '—'}</td>
                  <td className="num mono">{t.cost != null ? vndShort(t.cost) : '—'}</td>
                  <td className="subtle">{t.result || '—'}</td>
                </tr>
              ))}</tbody>
            </table>
          ) : <EmptyState icon="bi-mortarboard" text={L('Không có khóa đào tạo nào kết thúc trong kỳ', 'No trainings completed in this period')} />}
        </div>
      </div>
      <div className="card span3">
        <div className="card-head"><h3>{L('Hiệu quả sau đào tạo', 'Post-training impact')}</h3>
          <span className="subtle" style={{ fontSize:'var(--fs-sm)' }}>
            {L('So điểm đánh giá trước và sau khóa học', 'Review score before vs after the course')}</span></div>
        <div className="card-body" style={{ overflow:'auto' }}>
          {m.effect.length ? (
            <table className="tbl">
              <thead><tr><th>{L('Khóa học', 'Course')}</th><th>{L('Nhân viên', 'Employee')}</th>
                <th className="num">{L('Điểm trước', 'Before')}</th><th className="num">{L('Điểm sau', 'After')}</th>
                <th className="num">{L('Thay đổi', 'Change')}</th></tr></thead>
              <tbody>{m.effect.map(x => (
                <tr key={x.training.id} style={{ cursor:'default' }}>
                  <td style={{ fontWeight:500 }}>{x.training.name}</td>
                  <td>{hrmEmpName(data.employees, x.training.employeeId)}</td>
                  <td className="num mono">{x.before}</td><td className="num mono">{x.after}</td>
                  <td className="num mono" style={{ fontWeight:700, color:x.delta > 0 ? 'var(--success)' : x.delta < 0 ? 'var(--error)' : 'inherit' }}>
                    {x.delta > 0 ? '+' : ''}{x.delta}</td>
                </tr>
              ))}</tbody>
            </table>
          ) : <EmptyState icon="bi-graph-up-arrow" text={L('Chưa đủ dữ liệu: cần nhân viên có đánh giá cả TRƯỚC và SAU khóa học mới so sánh được.',
            'Not enough data: needs a review both before and after the course.')} />}
        </div>
      </div>
    </div>
  </>);
}

/* ============ root ============ */
function HrmReports(){
  const { p, st, data } = useHrm();
  const { lang } = useLang();
  const toast = useToast();
  const L = (vi, en) => (lang === 'vi' ? vi : en);
  const period = useReportPeriod('thisyear');
  const [tab, setTab] = React.useState('structure');
  const pd = { from:period.from, to:period.to };

  const mStruct = React.useMemo(() => hrmRepStructure(data, pd, lang), [data, pd.from, pd.to, lang]);
  const mTime   = React.useMemo(() => hrmRepTime(data, st, pd), [data, st, pd.from, pd.to]);
  const mPay    = React.useMemo(() => hrmRepPay(data, p, pd), [data, p.contracts, p.quotes, pd.from, pd.to]);
  const mTrain  = React.useMemo(() => hrmRepTraining(data, pd, lang), [data, pd.from, pd.to, lang]);

  const TABS = [
    { id:'structure', icon:'bi-people', label:L('Tổng quan & cơ cấu', 'Overview & structure') },
    { id:'time', icon:'bi-clock-history', label:L('Chấm công & thời gian', 'Time & attendance') },
    { id:'pay', icon:'bi-cash-stack', label:L('Lương thưởng & phúc lợi', 'Compensation') },
    { id:'training', icon:'bi-mortarboard', label:L('Đào tạo & hiệu suất', 'Training & performance') },
  ];
  const SUBS = {
    structure:L('Quân số · cơ cấu tuổi/giới tính/học vấn/thâm niên · biến động vào–nghỉ trong kỳ.', 'Headcount, demographics and movements in this period.'),
    time:L('Vắng mặt · đi trễ/về sớm · làm thêm giờ · quỹ phép — theo kỳ đã chọn.', 'Absence, punctuality, overtime and leave balances.'),
    pay:L('Quỹ lương các kỳ đã chốt · cơ cấu chi phí trên doanh thu · bảo hiểm và thuế TNCN.', 'Finalized payroll, cost ratio, insurance and PIT.'),
    training:L('Chi phí và giờ đào tạo · kết quả đánh giá KPI/OKR · hiệu quả sau đào tạo.', 'Training cost/hours, KPI/OKR outcomes and post-training impact.'),
  };
  // Sổ QLLĐ điện tử (Điều 3 NĐ 145/2020, lưu ≥10 năm) — TOÀN BỘ nhân sự, KHÔNG theo kỳ báo cáo.
  const exportRegister = () => {
    const year = +pd.to.slice(0, 4);
    const H = ['Mã NV', 'Họ tên', 'Giới tính', 'Ngày sinh', 'Quốc tịch', 'Nơi cư trú', 'Số CCCD/hộ chiếu', 'Trình độ chuyên môn', 'Bậc kỹ năng nghề', 'Vị trí việc làm', 'Phòng ban', 'Loại HĐLĐ hiện tại', 'Ngày bắt đầu làm việc', 'Mã số BHXH', 'Ngày tham gia BHXH', 'Lương cơ bản', 'Số ngày nghỉ trong năm ' + year, 'Số giờ làm thêm năm ' + year, 'Trạng thái', 'Ngày chấm dứt HĐLĐ', 'Phân loại chấm dứt', 'Lý do chấm dứt'];
    const lines = data.employees.map(e => {
      const c = data.contracts.filter(x => x.employeeId === e.id && x.kind !== 'appendix' && x.status === 'active').pop();
      const leaveDays = (data.requests || []).filter(r => r.kind === 'leave' && r.employeeId === e.id && r.status === 'approved' && (r.startDate || '').slice(0, 4) === String(year)).reduce((s, r) => s + (+r.days || 0), 0);
      const otY = hrmOtTotals(data.requests, e, year + '-12-31', st).year.used;
      return [e.id, e.fullName, e.gender === 'male' ? 'Nam' : e.gender === 'female' ? 'Nữ' : '', e.birthDate, e.nationality, e.address, e.idNumber, e.education, e.skillLevel, e.position, e.dept, c ? (HRM_CONTRACT_KINDS[c.kind] || {}).vi : '', e.startDate, e.siCode, e.siJoinDate, (e.salary && e.salary.base) || 0, leaveDays, otY, (HRM_STATUS[e.status] || {}).vi, e.endDate || '', (HRM_END_TYPES[e.endType] || {}).vi || '', e.endReason || ''];
    });
    const csv = '﻿' + [H, ...lines].map(r => r.map(v => '"' + String(v == null ? '' : v).replace(/"/g, '""') + '"').join(',')).join('\r\n');
    const a = document.createElement('a');
    a.href = URL.createObjectURL(new Blob([csv], { type:'text/csv;charset=utf-8' }));
    a.download = 'so-quan-ly-lao-dong-' + todayVN() + '.csv'; a.click(); URL.revokeObjectURL(a.href);
    toast(L('Đã xuất sổ quản lý lao động (CSV)', 'Register exported (CSV)'), 'success');
  };
  // Excel: mỗi tab xuất đúng bảng của tab đó (không nhồi cả 4 nhóm vào 1 file cho rối).
  const excel = () => {
    const stamp = pd.from + '_' + pd.to;
    if (tab === 'structure') {
      const tv = mStruct.tv;
      exportReportXLSX([
        { name:L('Nhân sự', 'Headcount'), widths:[10, 22, 8, 12, 6, 14, 22, 20, 12, 8, 12, 12, 16],
          header:['Mã NV', 'Họ tên', 'Giới tính', 'Ngày sinh', 'Tuổi', 'Phòng ban', 'Vị trí', 'Trình độ', 'Ngày vào', 'Thâm niên (năm)', 'Trạng thái', 'Ngày nghỉ', 'Phân loại nghỉ'],
          rows:mStruct.head.map(e => [e.id, e.fullName, (HRM_GENDERS[e.gender] || {}).vi || '', e.birthDate || '', hrmAge(e, pd.to), e.dept || '', e.position || '', e.education || '', e.startDate, hrmYearsOfService(e, pd.to), (HRM_STATUS[e.status] || {}).vi || '', e.endDate || '', (HRM_END_TYPES[e.endType] || {}).vi || '']) },
        { name:L('Biến động', 'Movements'), widths:[28, 14],
          header:['Chỉ tiêu', 'Giá trị'],
          rows:[['Nhân sự đầu kỳ', tv.head0], ['Nhân sự cuối kỳ', tv.head1], ['Vào mới', tv.hires.length], ['Nghỉ việc', tv.leavers.length],
            ['Tỷ lệ nghỉ việc (%)', tv.turnoverPct], ['Tỷ lệ giữ chân (%)', tv.retentionPct]].concat(
            Object.keys(HRM_END_TYPES).map(k => ['Nghỉ — ' + HRM_END_TYPES[k].vi, tv.byType[k] || 0])) },
      ], 'bao-cao-nhan-su-co-cau-' + stamp, toast, lang);
    } else if (tab === 'time') {
      exportReportXLSX([{ name:L('Chấm công', 'Attendance'), widths:[10, 22, 14, 10, 10, 8, 8, 8, 8, 10, 10, 8, 8, 10],
        header:['Mã NV', 'Họ tên', 'Phòng ban', 'Công chuẩn', 'Có mặt', 'Phép', 'Vắng KP', '% vắng', 'Lượt trễ', 'Phút trễ', 'Lượt về sớm', 'Phút về sớm', 'Giờ OT', 'Giờ đêm', 'Phép còn'],
        rows:mTime.rows.map(r => [r.e.id, r.e.fullName, r.e.dept || '', r.standard, r.present, r.leaveDays, r.absent, r.absentPct, r.late, r.lateMin, r.early, r.earlyMin, r.otH, r.nightH, r.left]) }],
        'bao-cao-nhan-su-cham-cong-' + stamp, toast, lang);
    } else if (tab === 'pay') {
      exportReportXLSX([{ name:L('Lương', 'Payroll'), widths:[10, 22, 10, 14, 14, 14, 14, 12, 12, 12, 14],
        header:['Mã NV', 'Họ tên', 'Kỳ', 'Lương theo công', 'Phụ cấp', 'Làm thêm', 'Tổng thu nhập', 'BHXH', 'BHYT', 'BHTN', 'Thuế TNCN', 'Thực lĩnh'],
        rows:mPay.rows.map(({ s, d, name }) => { const i = d.insurance || {};
          return [s.employeeId, name, s.period, d.baseAmount || 0, d.allowTotal || 0, d.otTotal || 0, d.gross || 0, i.bhxh || 0, i.bhyt || 0, i.bhtn || 0, d.pit || 0, hrmPayslipNet(d)]; })
          .concat([[], ['', 'TỔNG', '', mPay.base, mPay.allow, mPay.ot, mPay.gross, mPay.ins.bhxh, mPay.ins.bhyt, mPay.ins.bhtn, mPay.pit, mPay.net],
            ['', 'DN đóng bảo hiểm (21.5%)', '', '', '', '', mPay.employerIns], ['', 'Chi phí nhân sự', '', '', '', '', mPay.cost],
            ['', 'Doanh thu thực thu', '', '', '', '', mPay.revenue], ['', 'Tỷ trọng CPNS/doanh thu (%)', '', '', '', '', mPay.ratio == null ? '' : mPay.ratio]]) }],
        'bao-cao-nhan-su-luong-' + stamp, toast, lang);
    } else {
      exportReportXLSX([
        { name:L('Đào tạo', 'Trainings'), widths:[28, 22, 20, 12, 12, 8, 14, 20],
          header:['Khóa học', 'Nhân viên', 'Đơn vị đào tạo', 'Từ ngày', 'Đến ngày', 'Số giờ', 'Chi phí', 'Kết quả'],
          rows:mTrain.trs.map(t => [t.name, hrmEmpName(data.employees, t.employeeId), t.org || '', t.fromDate || '', t.toDate || '', t.hours != null ? t.hours : '', t.cost != null ? t.cost : '', t.result || '']) },
        { name:L('Đánh giá', 'Reviews'), widths:[10, 22, 12, 12, 8, 8, 14, 24],
          header:['Mã NV', 'Họ tên', 'Kỳ đánh giá', 'Ngày chốt', 'Loại', 'Điểm', 'Kết quả', 'Ghi chú'],
          rows:mTrain.revs.map(r => [r.employeeId, hrmEmpName(data.employees, r.employeeId), r.period, r.reviewDate, (HRM_REVIEW_KINDS[r.kind] || {}).vi || '', r.score != null ? r.score : '', (HRM_REVIEW_RATINGS[r.rating] || {}).vi || '', r.note || '']) },
        { name:L('Sau đào tạo', 'Impact'), widths:[28, 22, 10, 10, 10],
          header:['Khóa học', 'Nhân viên', 'Điểm trước', 'Điểm sau', 'Thay đổi'],
          rows:mTrain.effect.map(x => [x.training.name, hrmEmpName(data.employees, x.training.employeeId), x.before, x.after, x.delta]) },
      ], 'bao-cao-nhan-su-dao-tao-' + stamp, toast, lang);
    }
  };

  return (
    <ReportShell title={L('Báo cáo nhân sự', 'HR reports')} subtitle={SUBS[tab]} period={period} onExcel={excel}
      extra={<button className="btn" onClick={exportRegister}><i className="bi bi-journal-text"></i>{L('Sổ QLLĐ', 'Register')}</button>}>
      <Tabs tabs={TABS} active={tab} onChange={setTab} />
      {tab === 'structure' && <HrmRepStructure m={mStruct} />}
      {tab === 'time' && <HrmRepTime m={mTime} st={st} />}
      {tab === 'pay' && <HrmRepPay m={mPay} />}
      {tab === 'training' && <HrmRepTraining m={mTrain} data={data} />}
    </ReportShell>
  );
}

Object.assign(window, { HrmReports, hrmRepStructure, hrmRepTime, hrmRepPay, hrmRepTraining });
