function usePayFmt() {
  const { lang } = useMizo();
  const loc = lang === 'fr' ? 'fr-CA' : 'en-CA';
  const tf = new Intl.DateTimeFormat(loc, { hour: 'numeric', minute: '2-digit' });
  const df = new Intl.DateTimeFormat(loc, { weekday: 'short', month: 'short', day: 'numeric' });
  const hf = new Intl.NumberFormat(loc, { minimumFractionDigits: 2, maximumFractionDigits: 2 });
  return {
    time: (m) => (m == null ? '—' : tf.format(new Date(2026, 9, 28, Math.floor(m / 60), Math.round(m % 60)))),
    date: (d) => df.format(new Date(d + 'T12:00')),
    hrs: (x) => hf.format(x) + ' h',
    toInput: (m) => (m == null ? '' : String(Math.floor(m / 60)).padStart(2, '0') + ':' + String(Math.round(m % 60)).padStart(2, '0')),
    fromInput: (s) => { if (!s) return null; const [a, b] = s.split(':').map(Number); return a * 60 + b; },
  };
}

function Payroll({ focus, tab: ctlTab, setTab: ctlSet }) {
  const { L } = useMizo();
  const [tabS, setTabS] = React.useState((focus && focus.tab) || 'run');
  React.useEffect(() => { if (focus && focus.tab) setTabS(focus.tab); }, [focus]);
  const tab = ctlTab || tabS, setTab = ctlSet || setTabS;
  return (
    <div className="page">
      <PageHead title={L('Payroll & time clock', 'Paie et pointage')} sub={L('Hours, pay and labour cost for the current pay period.', 'Heures, paie et coût de main-d’œuvre pour la période en cours.')} />
      {ctlTab ? null : <div className="tabs-row"><Seg value={tab} onChange={setTab} options={[['run', L('Pay run', 'Paie')], ['clock', L('Time clock', 'Pointage')], ['sheets', L('Timesheets', 'Feuilles de temps')], ['team', L('Team', 'Équipe')]]} /></div>}
      {tab === 'run' ? <PayRun setTab={setTab} /> : tab === 'clock' ? <TimeClock /> : tab === 'sheets' ? <Timesheets /> : <Team />}
    </div>
  );
}

function PayRun({ setTab }) {
  const { L, lang, fmt, P, employees, punches, now, payStatus, setPayStatus, toast } = useMizo();
  const pf = usePayFmt();
  const [sel, setSel] = React.useState(null);
  const [confirm, setConfirm] = React.useState(false);
  const rows = P.calc(employees, punches, now);
  const T = rows.reduce((a, r) => ({ gross: a.gross + r.gross, er: a.er + r.erT + r.vac, cost: a.cost + r.cost, net: a.net + r.net, ded: a.ded + r.dedT, ot: a.ot + r.ot, hours: a.hours + r.hours, so: a.so + r.grossSoFar }), { gross: 0, er: 0, cost: 0, net: 0, ded: 0, ot: 0, hours: 0, so: 0 });
  const labourPct = (T.cost / P.periodSales) * 100;
  const missing = punches.filter((p) => p.flag === 'missing').length;
  const pending = punches.filter((p) => p.status === 'pending' && !p.open).length;
  const blocked = missing > 0;
  const step = payStatus === 'exported' ? 3 : payStatus === 'approved' ? 2 : pending || missing ? 0 : 1;
  const sr = rows.find((r) => r.e.id === sel);

  const exportCsv = () => {
    const head = ['Employee', 'Role', 'Type', 'Rate', 'Regular hours', 'Overtime hours', 'Gross', 'Tips', 'Deductions (est.)', 'Net (est.)', 'Employer cost (est.)'];
    const lines = rows.map((r) => [r.e.name, r.e.role.en, r.e.type, r.e.rate || r.e.salary, r.reg.toFixed(2), r.ot.toFixed(2), r.gross.toFixed(2), r.tips.toFixed(2), r.dedT.toFixed(2), r.net.toFixed(2), (r.erT + r.vac).toFixed(2)]);
    const csv = [head, ...lines].map((l) => l.map((c) => '"' + String(c).replace(/"/g, '""') + '"').join(',')).join('\n');
    const a = document.createElement('a'); a.href = URL.createObjectURL(new Blob([csv], { type: 'text/csv' })); a.download = 'souvlaki-village-payroll-2026-10-19.csv'; a.click();
    setPayStatus('exported'); toast(L('Payroll file downloaded', 'Fichier de paie téléchargé'));
  };
  const steps = [L('Approve hours', 'Approuver les heures'), L('Review pay', 'Vérifier la paie'), L('Approve payroll', 'Approuver la paie'), L('Export to provider', 'Exporter')];

  return (
    <>
      <section className="panel payhead">
        <div className="payhead-top">
          <div>
            <div className="card-kicker">{L('Pay period', 'Période de paie')} · {L('every 2 weeks', 'aux 2 semaines')}</div>
            <h2>{pf.date(P.days[0])} – {pf.date(P.days[13])}</h2>
            <p className="muted">{L('Pay date', 'Date de paie')} {pf.date(P.payDate)} · {L('Period ends Sunday — figures after today are projected from the schedule.', 'La période se termine dimanche — les chiffres après aujourd’hui sont projetés selon l’horaire.')}</p>
          </div>
          <div className="payhead-actions">
            {step === 0 ? <button className="btn btn-primary" onClick={() => setTab('sheets')}><Icon n="clock-countdown" />{blocked ? L(`Fix ${missing} missing punch`, `Corriger ${missing} pointage manquant`) : L(`Approve ${pending} shifts`, `Approuver ${pending} quarts`)}</button> : null}
            {step === 1 ? <button className="btn btn-primary" onClick={() => setConfirm(true)}><Icon n="seal-check" />{L('Approve payroll', 'Approuver la paie')}</button> : null}
            {step === 2 ? <button className="btn btn-primary" onClick={exportCsv}><Icon n="download-simple" />{L('Export payroll file', 'Exporter le fichier')}</button> : null}
            {step === 3 ? <><span className="pill pill-pos"><Icon n="check" />&nbsp;{L('Exported', 'Exporté')}</span><button className="btn btn-secondary" onClick={exportCsv}><Icon n="download-simple" />{L('Download again', 'Télécharger à nouveau')}</button></> : null}
          </div>
        </div>
        <ol className="wiz-steps paysteps">{steps.map((s, i) => { const done = i < step || (i === 0 && step >= 1) || (i === 1 && step >= 2); return <li key={i} className={done ? 'done' : i === Math.max(step, 0) ? 'on' : ''}><span>{done ? <Icon n="check" /> : i + 1}</span>{s}</li>; })}</ol>
      </section>

      <section className="stats">
        <Stat label={L('Gross pay (projected)', 'Salaire brut (projeté)')} value={fmt.money0(T.gross)}>{fmt.money0(T.so)} {L('earned so far', 'gagné à ce jour')}</Stat>
        <Stat label={L('Employer costs (est.)', 'Charges patronales (est.)')} value={fmt.money0(T.er)}>{L('Contributions + 4% vacation', 'Cotisations + 4 % vacances')}</Stat>
        <Stat label={L('Total labour cost', 'Coût total main-d’œuvre')} value={fmt.money0(T.cost)} tone="acc">{fmt.num(Math.round(T.hours))} {L('hours', 'heures')}</Stat>
        <Stat label={L('Labour % of sales', 'Main-d’œuvre / ventes')} value={fmt.pct(labourPct)} tone={labourPct > 32 ? 'warn' : ''}>{L('Sales', 'Ventes')} {fmt.money0(P.periodSales)} {L('(incl. forecast)', '(avec prévision)')}</Stat>
        <Stat label={L('Overtime', 'Heures supp.')} value={pf.hrs(T.ot)} tone={T.ot ? 'warn' : ''}>{L('Over 40 h/week at 1.5×', 'Au-delà de 40 h/sem. à 1,5×')}</Stat>
      </section>

      <div className="estimate-note"><span className="tag tag-outline">{L('Estimate', 'Estimation')}</span>{L('Deductions use standard Québec rates for a quick preview. Your payroll provider calculates the final amounts.', 'Les retenues utilisent des taux standards du Québec pour un aperçu. Votre fournisseur de paie calcule les montants finaux.')}</div>

      <div className="panel panel-flush">
        <div className="table-scroll">
          <table className="table mtable">
            <thead><tr><th>{L('Employee', 'Employé')}</th><th className="num">{L('Hours', 'Heures')}</th><th className="num hide-sm">{L('Overtime', 'Supp.')}</th><th className="num hide-sm hide-md">{L('Rate', 'Taux')}</th><th className="num">{L('Gross', 'Brut')}</th><th className="num hide-sm hide-md">{L('Deductions', 'Retenues')}</th><th className="num">{L('Net (est.)', 'Net (est.)')}</th></tr></thead>
            <tbody>
              {rows.map((r) => (
                <tr key={r.e.id} className="clickable" onClick={() => setSel(r.e.id)}>
                  <td><div className="emp-cell"><span className="emp-av">{r.e.name.split(' ').map((w) => w[0]).join('').slice(0, 2)}</span><div><div className="cell-main">{r.e.name}{r.flags ? <span className="pill pill-neg" style={{ marginLeft: '.5rem' }}>{L('Missing punch', 'Pointage manquant')}</span> : null}</div><div className="cell-sub">{r.e.role[lang]}</div></div></div></td>
                  <td className="num"><span className="tnum">{pf.hrs(r.hours)}</span><div className="cell-sub tnum">{pf.hrs(r.hoursSoFar)} {L('so far', 'à ce jour')}</div></td>
                  <td className={'num tnum hide-sm' + (r.ot ? ' txt-warn' : ' muted')}>{r.ot ? pf.hrs(r.ot) : '—'}</td>
                  <td className="num tnum hide-sm hide-md muted">{r.e.type === 'salary' ? L('Salary', 'Salaire') : fmt.money(r.e.rate) + '/h'}</td>
                  <td className="num tnum">{fmt.money(r.gross)}</td>
                  <td className="num tnum hide-sm hide-md muted">−{fmt.money(r.dedT)}</td>
                  <td className="num tnum"><b>{fmt.money(r.net)}</b></td>
                </tr>
              ))}
            </tbody>
            <tfoot><tr><td><b>{L('Total', 'Total')}</b> · {rows.length} {L('employees', 'employés')}</td><td className="num tnum"><b>{pf.hrs(T.hours)}</b></td><td className="num tnum hide-sm">{pf.hrs(T.ot)}</td><td className="hide-sm hide-md"></td><td className="num tnum"><b>{fmt.money(T.gross)}</b></td><td className="num tnum hide-sm hide-md">−{fmt.money(T.ded)}</td><td className="num tnum"><b>{fmt.money(T.net)}</b></td></tr></tfoot>
          </table>
        </div>
      </div>

      <Drawer open={!!sr} onClose={() => setSel(null)}>{sr ? <PayDetail r={sr} onClose={() => setSel(null)} /> : null}</Drawer>
      <Modal open={confirm} onClose={() => setConfirm(false)} title={L('Approve this payroll?', 'Approuver cette paie?')} actions={<><button className="btn btn-secondary" onClick={() => setConfirm(false)}>{L('Not yet', 'Pas encore')}</button><button className="btn btn-primary" onClick={() => { setPayStatus('approved'); setConfirm(false); toast(L('Payroll approved', 'Paie approuvée')); }}>{L(`Approve ${fmt.money0(T.gross)}`, `Approuver ${fmt.money0(T.gross)}`)}</button></>}>
        <p>{L(`You're approving gross pay of ${fmt.money(T.gross)} for ${rows.length} employees. Hours after today are projected from the schedule and will be updated from the time clock before export.`, `Vous approuvez un salaire brut de ${fmt.money(T.gross)} pour ${rows.length} employés. Les heures après aujourd’hui sont projetées et seront mises à jour avant l’export.`)}</p>
      </Modal>
    </>
  );
}

function PayDetail({ r, onClose }) {
  const { L, lang, fmt, P, punches, now, go } = useMizo();
  const pf = usePayFmt();
  const nm = (n) => (typeof n === 'string' ? n : n[lang]);
  const shifts = punches.filter((p) => p.emp === r.e.id);
  return (
    <div className="dr">
      <div className="dr-head"><div><div className="card-kicker">{r.e.role[lang]}</div><h2>{r.e.name}</h2></div><button className="btn btn-icon btn-secondary" onClick={onClose} aria-label="Close"><Icon n="x" s="1.1rem" /></button></div>
      <div className="dr-sec"><div className="pay-sum"><Stat label={L('Hours', 'Heures')} value={pf.hrs(r.hours)} /><Stat label={L('Gross', 'Brut')} value={fmt.money0(r.gross)} /><Stat label={L('Net (est.)', 'Net (est.)')} value={fmt.money0(r.net)} tone="acc" /></div></div>
      <div className="dr-sec">
        <div className="dr-label">{L('Earnings', 'Gains')}</div>
        {r.e.type === 'salary' ? <div className="ln"><span>{L('Salary, 2 weeks', 'Salaire, 2 semaines')}</span><b>{fmt.money(r.e.salary)}</b></div> : <>
          <div className="ln"><span>{L('Regular', 'Régulier')} · {pf.hrs(r.reg)} × {fmt.money(r.e.rate)}</span><b>{fmt.money(r.reg * r.e.rate)}</b></div>
          {r.ot ? <div className="ln txt-warn"><span>{L('Overtime', 'Supp.')} · {pf.hrs(r.ot)} × {fmt.money(r.e.rate * 1.5)}</span><b>{fmt.money(r.ot * r.e.rate * 1.5)}</b></div> : null}
        </>}
        {r.tips ? <div className="ln muted"><span>{L('Declared tips (taxable, not paid by you)', 'Pourboires déclarés (imposables)')}</span><span>{fmt.money(r.tips)}</span></div> : null}
        <div className="ln tot"><span>{L('Gross pay', 'Salaire brut')}</span><b>{fmt.money(r.gross)}</b></div>
      </div>
      <div className="dr-sec">
        <div className="dr-row"><div className="dr-label">{L('Deductions', 'Retenues')}</div><span className="tag tag-outline">{L('Estimate', 'Estimation')}</span></div>
        {r.ded.map((d) => <div key={d.k} className="ln"><span>{nm(d.n)} <span className="muted">{fmt.num(d.r)} %</span></span><span className="tnum">−{fmt.money(d.v)}</span></div>)}
        <div className="ln tot"><span>{L('Net pay', 'Salaire net')}</span><b>{fmt.money(r.net)}</b></div>
      </div>
      <div className="dr-sec">
        <div className="dr-label">{L('Your cost as employer', 'Votre coût d’employeur')}</div>
        {r.er.map((d) => <div key={d.k} className="ln"><span>{nm(d.n)} <span className="muted">{fmt.num(d.r)} %</span></span><span className="tnum">{fmt.money(d.v)}</span></div>)}
        <div className="ln"><span>{L('Vacation accrual', 'Indemnité de vacances')} <span className="muted">4 %</span></span><span className="tnum">{fmt.money(r.vac)}</span></div>
        <div className="ln tot"><span>{L('Total cost to you', 'Coût total')}</span><b>{fmt.money(r.cost)}</b></div>
      </div>
      <div className="dr-sec">
        <div className="dr-row"><div className="dr-label">{L('Shifts this period', 'Quarts de la période')}</div><button className="btn btn-ghost" onClick={() => go('payroll', { tab: 'sheets' })}>{L('Timesheets', 'Feuilles de temps')}</button></div>
        {shifts.map((p) => (
          <div key={p.id} className="ln">
            <span>{pf.date(p.date)} <span className="muted">· {pf.time(p.in)} – {p.open ? L('now', 'maint.') : pf.time(p.out)}</span></span>
            <span className={p.flag === 'missing' ? 'txt-neg' : 'tnum'}>{p.flag === 'missing' ? L('Missing out', 'Sortie manquante') : pf.hrs(P.worked(p, now))}</span>
          </div>
        ))}
      </div>
    </div>
  );
}

function Timesheets() {
  const { L, lang, fmt, P, employees, punches, setPunches, now, toast } = useMizo();
  const pf = usePayFmt();
  const [who, setWho] = React.useState('all');
  const [week, setWeek] = React.useState(1);
  const [onlyIssues, setOnlyIssues] = React.useState(false);
  const [edit, setEdit] = React.useState(null);
  const wDays = P.days.slice(week * 7, week * 7 + 7);
  const emp = (id) => employees.find((e) => e.id === id);
  const rows = punches.filter((p) => wDays.includes(p.date) && (who === 'all' || p.emp === who) && (!onlyIssues || p.flag)).sort((a, b) => (a.date === b.date ? a.in - b.in : a.date < b.date ? 1 : -1));
  const pendingIds = punches.filter((p) => wDays.includes(p.date) && p.status === 'pending' && !p.open && p.flag !== 'missing').map((p) => p.id);
  const missing = punches.filter((p) => p.flag === 'missing');
  const flagPill = (p) => p.flag === 'missing' ? <Pill tone="neg">{L('Missing punch-out', 'Sortie manquante')}</Pill> : p.flag === 'early' ? <Pill tone="warn">{L('Clocked in early', 'Entrée hâtive')}</Pill> : p.flag === 'late' ? <Pill tone="warn">{L('Late', 'En retard')}</Pill> : null;
  const total = rows.reduce((s, p) => s + P.worked(p, now), 0);

  return (
    <>
      {missing.length ? (
        <div className="suggest warnbox">
          <Icon n="warning" s="1.3rem" />
          <div>{L(<><b>{emp(missing[0].emp).name}</b> forgot to punch out on {pf.date(missing[0].date)}. Fix it before running payroll.</>, <><b>{emp(missing[0].emp).name}</b> n’a pas pointé sa sortie le {pf.date(missing[0].date)}. Corrigez avant la paie.</>)}</div>
          <button className="btn btn-primary" onClick={() => { setWeek(P.days.indexOf(missing[0].date) < 7 ? 0 : 1); setEdit(missing[0]); }}>{L('Fix now', 'Corriger')}</button>
        </div>
      ) : null}
      <div className="panel panel-flush">
        <div className="toolbar">
          <Seg size="sm" value={week} onChange={setWeek} options={[[0, L('Oct 19–25', '19–25 oct.')], [1, L('Oct 26–Nov 1', '26 oct.–1 nov.')]]} />
          <select className="input" style={{ maxWidth: 240 }} value={who} onChange={(e) => setWho(e.target.value)}><option value="all">{L('Everyone', 'Tout le monde')}</option>{employees.map((e) => <option key={e.id} value={e.id}>{e.name}</option>)}</select>
          <label className="check"><input type="checkbox" checked={onlyIssues} onChange={(e) => setOnlyIssues(e.target.checked)} />{L('Only issues', 'Problèmes seulement')}</label>
          <span style={{ flex: 1 }}></span>
          <button className="btn btn-primary" disabled={!pendingIds.length} onClick={() => { setPunches(punches.map((p) => (pendingIds.includes(p.id) ? { ...p, status: 'approved' } : p))); toast(L(`${pendingIds.length} shifts approved`, `${pendingIds.length} quarts approuvés`)); }}><Icon n="checks" />{L(`Approve ${pendingIds.length} shifts`, `Approuver ${pendingIds.length} quarts`)}</button>
        </div>
        <div className="table-scroll">
          <table className="table mtable">
            <thead><tr><th>{L('Day', 'Jour')}</th><th>{L('Employee', 'Employé')}</th><th>{L('In', 'Entrée')}</th><th>{L('Out', 'Sortie')}</th><th className="num hide-sm">{L('Break', 'Pause')}</th><th className="num">{L('Hours', 'Heures')}</th><th>{L('Status', 'État')}</th><th></th></tr></thead>
            <tbody>
              {rows.map((p) => (
                <tr key={p.id}>
                  <td className="tnum">{pf.date(p.date)}</td>
                  <td><div className="cell-main">{emp(p.emp).name}</div>{p.log.length ? <div className="cell-sub">{L('Edited', 'Modifié')} · {p.log[p.log.length - 1]}</div> : null}</td>
                  <td className="tnum">{pf.time(p.in)}</td>
                  <td className="tnum">{p.open ? <span className="live"><i></i>{p.onBreak != null ? L('On break', 'En pause') : L('Working', 'Au travail')}</span> : p.out == null ? <span className="txt-neg">—</span> : pf.time(p.out)}</td>
                  <td className="num tnum hide-sm muted">{p.brk ? p.brk + ' min' : '—'}</td>
                  <td className="num tnum">{pf.hrs(P.worked(p, now))}</td>
                  <td>{flagPill(p) || (p.open ? <Pill tone="neutral">{L('In progress', 'En cours')}</Pill> : p.status === 'approved' ? <Pill tone="pos">{L('Approved', 'Approuvé')}</Pill> : <Pill tone="neutral">{L('To approve', 'À approuver')}</Pill>)}</td>
                  <td className="num">{!p.open ? <button className="btn btn-ghost" onClick={() => setEdit(p)}>{L('Edit', 'Modifier')}</button> : null}</td>
                </tr>
              ))}
            </tbody>
            <tfoot><tr><td colSpan="5" className="muted">{rows.length} {L('shifts', 'quarts')}</td><td className="num tnum"><b>{pf.hrs(total)}</b></td><td colSpan="2"></td></tr></tfoot>
          </table>
          {!rows.length ? <div className="empty">{L('No shifts match.', 'Aucun quart trouvé.')}</div> : null}
        </div>
      </div>
      <EditPunch p={edit} onClose={() => setEdit(null)} />
    </>
  );
}

function EditPunch({ p, onClose }) {
  const { L, employees, punches, setPunches, toast } = useMizo();
  const pf = usePayFmt();
  const [f, setF] = React.useState({});
  React.useEffect(() => { if (p) setF({ in: pf.toInput(p.in), out: pf.toInput(p.out != null ? p.out : (employees.find((e) => e.id === p.emp).sched[new Date(p.date + 'T12:00').getDay() === 0 ? 6 : new Date(p.date + 'T12:00').getDay() - 1] || [0, 0])[1]), brk: p.brk, reason: '' }); }, [p]);
  if (!p) return null;
  const inM = pf.fromInput(f.in), outM = pf.fromInput(f.out);
  const err = outM != null && inM != null && outM <= inM ? L('Out time must be after in time.', 'La sortie doit suivre l’entrée.') : null;
  const ok = !err && inM != null && outM != null && f.reason && f.reason.trim().length > 2;
  const save = () => {
    setPunches(punches.map((x) => (x.id === p.id ? { ...x, in: inM, out: outM, brk: Number(f.brk) || 0, flag: null, status: 'pending', log: [...x.log, f.reason.trim()] } : x)));
    toast(L('Shift updated · change logged', 'Quart modifié · changement noté')); onClose();
  };
  return (
    <Modal open={!!p} onClose={onClose} title={L('Edit shift', 'Modifier le quart')} actions={<><button className="btn btn-secondary" onClick={onClose}>{L('Cancel', 'Annuler')}</button><button className="btn btn-primary" disabled={!ok} onClick={save}>{L('Save shift', 'Enregistrer')}</button></>}>
      <p className="muted">{employees.find((e) => e.id === p.emp).name} · {pf.date(p.date)}{p.flag === 'missing' ? L(' — the out time below is from the schedule. Check it with the employee.', ' — l’heure de sortie vient de l’horaire. Vérifiez avec l’employé.') : ''}</p>
      <div className="form-grid">
        <div className="field"><label>{L('Clock in', 'Entrée')}</label><input className="input" type="time" value={f.in || ''} onChange={(e) => setF({ ...f, in: e.target.value })} /></div>
        <div className="field"><label>{L('Clock out', 'Sortie')}</label><input className="input" type="time" value={f.out || ''} onChange={(e) => setF({ ...f, out: e.target.value })} /></div>
        <div className="field"><label>{L('Unpaid break (minutes)', 'Pause non payée (minutes)')}</label><input className="input" type="number" min="0" step="5" value={f.brk} onChange={(e) => setF({ ...f, brk: e.target.value })} /></div>
        <div className="field"><label>{L('Reason for change (required)', 'Raison (obligatoire)')}</label><input className="input" placeholder={L('e.g. Forgot to punch out', 'ex. Oubli de pointer')} value={f.reason || ''} onChange={(e) => setF({ ...f, reason: e.target.value })} /></div>
      </div>
      {err ? <div className="txt-neg sm">{err}</div> : null}
      <div className="muted sm"><Icon n="clock-counter-clockwise" /> {L('Every change is saved with your name and the reason.', 'Chaque changement est conservé avec votre nom et la raison.')}</div>
    </Modal>
  );
}
Object.assign(window, { Payroll, usePayFmt });
