// AiiCafe Chấm Công — main app (SQLite-backed, cookie-session)
const { useState, useMemo, useEffect, useRef } = React;

window.EMPLOYEES = window.EMPLOYEES || [];

// ---------- Constants ----------
const TODAY = new Date(); TODAY.setHours(0, 0, 0, 0);

const fmtDate = (d) => `${String(d.getDate()).padStart(2,'0')}/${String(d.getMonth()+1).padStart(2,'0')}/${d.getFullYear()}`;
const fmtTime = (d) => `${String(d.getHours()).padStart(2,'0')}:${String(d.getMinutes()).padStart(2,'0')}`;
const fmtVND = (n) => n.toLocaleString('vi-VN') + ' ₫';

const SHIFT_START = { h: 8, m: 0 };
const SHIFT_END = { h: 17, m: 0 };

// ---------- Hours / payroll calc ----------
const minsBetween = (a, b) => (b.h * 60 + b.m) - (a.h * 60 + a.m);
const hoursBetween = (a, b) => minsBetween(a, b) / 60;

const calcRow = (r) => {
  const total = hoursBetween(r.checkIn, r.checkOut);
  const ot = Math.max(0, hoursBetween(SHIFT_END, r.checkOut));
  const lateMin = Math.max(0, minsBetween(SHIFT_START, r.checkIn));
  const earlyMin = Math.max(0, minsBetween(r.checkOut, SHIFT_END));
  return { total, ot, lateMin, earlyMin };
};

// Lương theo giờ thực tế. Áp dụng cho cả full-time và part-time.
//   regularH = giờ làm trong khung ca (tối đa SHIFT_END − SHIFT_START)
//   otH      = giờ làm ngoài khung ca (sau SHIFT_END), trả 1.5×
//   regularPay = regularH × hourly
//   otPay      = otH × hourly × 1.5
//   allowance  = phụ cấp tháng cố định, chỉ trả khi đã có ngày công duyệt
//   net        = regularPay + otPay + (allowance nếu days > 0)
const calcPayroll = (emp, rows) => {
  let totalH = 0, regularH = 0, ot = 0, days = 0, lateMin = 0, earlyMin = 0;
  rows.forEach((r) => {
    if (r.status !== 'approved') return;
    const c = calcRow(r);
    totalH += c.total;
    ot += c.ot;
    regularH += Math.max(0, c.total - c.ot);   // phần giờ trong khung ca
    lateMin += c.lateMin; earlyMin += c.earlyMin;
    days += 1;
  });
  if (days === 0) {
    return {
      totalH: 0, regularH: 0, ot: 0, days: 0, lateMin: 0, earlyMin: 0,
      regularPay: 0, otPay: 0, allowance: 0, net: 0, empty: true,
    };
  }
  const regularPay = Math.round(regularH * emp.hourly);
  const otPay      = Math.round(ot * emp.hourly * 1.5);
  const allowance  = emp.allowance || 0; // phụ cấp tháng — chỉ phát khi có làm
  const net        = regularPay + otPay + allowance;
  return { totalH, regularH, ot, days, lateMin, earlyMin, regularPay, otPay, allowance, net, empty: false };
};

// ---------- API client ----------
// All fetch calls include credentials:'include' so browser sends the HttpOnly cookie.
// If any response returns 401 after login, the global 401 handler is triggered.

// Callback ref — set by App to trigger logout on 401
let _on401 = null;

const rawFetch = (url, opts = {}) => {
  return fetch(url, { ...opts, credentials: 'include' });
};

// json helper: resolves on 2xx, rejects with parsed error body otherwise.
// If status === 401, also triggers global logout (except on /api/login itself).
const json = (r, skipAuthCheck) => {
  if (r.status === 401 && !skipAuthCheck) {
    // Trigger logout asynchronously — let current promise chain resolve first
    setTimeout(() => { if (_on401) _on401(); }, 0);
    return r.json().catch(() => ({})).then((e) => Promise.reject(e));
  }
  return r.ok ? r.json() : r.json().then((e) => Promise.reject(e));
};

const post = (url, body, skipAuthCheck) =>
  rawFetch(url, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(body || {}) })
    .then((r) => json(r, skipAuthCheck));

const patch = (url, body) =>
  rawFetch(url, { method: 'PATCH', headers: { 'content-type': 'application/json' }, body: JSON.stringify(body || {}) })
    .then(json);

const api = {
  me: () => rawFetch('/api/me').then((r) => {
    if (r.status === 401) return null; // not logged in
    return r.ok ? r.json() : r.json().then((e) => Promise.reject(e));
  }),
  login: (username, password) => post('/api/login', { username, password }, /* skipAuthCheck= */ true),
  logout: () => post('/api/logout', null, /* skipAuthCheck= */ true),
  changePassword: (current, next) => post('/api/auth/change-password', { current, next }),
  employees: () => rawFetch('/api/employees').then(json),
  accounts: () => rawFetch('/api/accounts').then(json),
  timesheets: (empId) => rawFetch('/api/timesheets' + (empId ? `?empId=${empId}` : '')).then(json),
  active: (empId) => rawFetch(`/api/timesheets/active/${empId}`).then(json),
  // Check-in/out go through ScanCheckin → postMultipart (multipart/form-data).
  // The old plain-JSON helpers were removed because they would always 400 on the
  // multipart-only endpoints.
  patchActive: (empId, note) => patch(`/api/timesheets/active/${empId}`, { note }),
  editTimesheet: (id, checkIn, checkOut, note) =>
    post(`/api/timesheets/${id}/edit`, { checkIn, checkOut, note }),
  manual: (empId, date, checkIn, checkOut, note) =>
    post('/api/timesheets/manual', { empId, date, checkIn, checkOut, note }),
  createEmp: (data) => post('/api/employees', data),
  patchEmp: (id, patch_) => patch(`/api/employees/${id}`, patch_),
  archiveEmp: (id) => post(`/api/employees/${id}/archive`),
  unarchiveEmp: (id) => post(`/api/employees/${id}/unarchive`),
  patchAcc: (username, patch_) => patch(`/api/accounts/${username}`, patch_),
};

// API rows ship date as "YYYY-MM-DD" — design code uses Date objects
const hydrateRow = (r) => ({ ...r, date: new Date(r.date + 'T00:00:00') });
const hydrateAcc = (a) => ({ ...a, createdAt: new Date(a.createdAt + 'T00:00:00') });
const dateToISO = (d) =>
  `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;

// ---------- Root ----------
function App() {
  const [authed, setAuthed] = useState(false);
  const [session, setSession] = useState(null);   // { role, username, empId, mustChangePassword }
  const [employees, setEmployees] = useState([]); // synced list
  const [accounts, setAccounts] = useState([]);
  const [rows, setRows] = useState([]);
  const [active, setActive] = useState(null);     // active check-in row for current user
  const [empTab, setEmpTab] = useState('home');
  const [mgrTab, setMgrTab] = useState('home');
  const [todayNote, setTodayNote] = useState('');
  const [detailRow, setDetailRow] = useState(null);
  const [exportOpen, setExportOpen] = useState(false);
  const [toast, setToast] = useState(null);
  const [booted, setBooted] = useState(false);
  const [changePwOpen, setChangePwOpen] = useState(false); // normal mode sheet
  const [scanState, setScanState] = useState(null); // { kind: 'checkin' | 'checkout' } | null
  const [qrOpen, setQrOpen] = useState(false);

  const showToast = (msg) => { setToast(msg); setTimeout(() => setToast(null), 1800); };

  // ---------- 401 global handler ----------
  const clearSession = () => {
    setSession(null); setAuthed(false);
    setEmployees([]); setAccounts([]); setRows([]); setActive(null);
    setChangePwOpen(false);
  };
  // Wire up global 401 callback
  _on401 = clearSession;

  // ---------- App boot: check session via /api/me ----------
  useEffect(() => {
    api.me().then((me) => {
      if (me && me.username) {
        setSession({ role: me.role, username: me.username, empId: me.empId, mustChangePassword: !!me.mustChangePassword });
        setAuthed(true);
      }
      setBooted(true);
    }).catch(() => {
      setBooted(true);
    });
  }, []);

  // ---------- Load data after auth ----------
  // /api/accounts is manager-only — don't call it as an employee (403 → uncaught reject).
  const refresh = async () => {
    if (!session) return;
    try {
      const emps = await api.employees();
      setEmployees(emps);
      window.EMPLOYEES = emps;

      if (session.role === 'manager') {
        const [accs, ts] = await Promise.all([api.accounts(), api.timesheets()]);
        setAccounts(accs.map(hydrateAcc));
        setRows(ts.map(hydrateRow));
      } else {
        const [ts, act] = await Promise.all([
          api.timesheets(session.empId),
          api.active(session.empId),
        ]);
        setRows(ts.map(hydrateRow));
        setActive(act);
        setTodayNote(act?.note || '');
      }
    } catch (e) {
      // The global 401 handler already triggers logout; swallow other errors so
      // the UI keeps whatever state it has rather than throwing uncaught.
      console.error('refresh failed', e);
    }
  };

  useEffect(() => { if (authed && session) refresh(); }, [authed, session?.username]);

  // ---------- Auth handlers ----------
  const onLogin = async (username, password) => {
    const acc = await api.login(username, password); // throws on failure
    setSession({ role: acc.role, username: acc.username, empId: acc.empId, mustChangePassword: !!acc.mustChangePassword });
    setEmpTab('home'); setMgrTab('home');
    setAuthed(true);
  };

  const onLogout = async () => {
    try { await api.logout(); } catch {}
    clearSession();
  };

  // ---------- Change password ----------
  const handleChangePassword = async (current, next) => {
    await api.changePassword(current, next); // throws with {error:...} on failure
    // On success: clear mustChangePassword flag, close any sheet
    setSession((s) => s ? { ...s, mustChangePassword: false } : s);
    setChangePwOpen(false);
    showToast('Đã đổi mật khẩu');
  };

  // ---------- Account / employee management ----------
  const createEmployee = async (data) => {
    try {
      await api.createEmp(data);
      await refresh();
      showToast('Đã tạo tài khoản ' + data.username);
    } catch (e) {
      showToast(e?.error || 'Lỗi tạo tài khoản');
    }
  };
  const updateAccount = async (username, p) => {
    try {
      await api.patchAcc(username, p);
      await refresh();
      if (p.password) showToast('Đã đặt lại mật khẩu');
      else if ('active' in p) showToast(p.active ? 'Đã mở khóa' : 'Đã tạm khóa tài khoản');
    } catch (e) { showToast(e?.error || 'Lỗi'); }
  };
  const updateEmployeeFields = async (empId, p) => {
    try { await api.patchEmp(empId, p); await refresh(); showToast('Đã cập nhật'); }
    catch (e) { showToast(e?.error || 'Lỗi'); }
  };
  const archiveEmployee = async (empId) => {
    try { await api.archiveEmp(empId); await refresh(); showToast('Đã cho nghỉ việc'); }
    catch (e) { showToast(e?.error || 'Lỗi'); }
  };
  const unarchiveEmployee = async (empId) => {
    try { await api.unarchiveEmp(empId); await refresh(); showToast('Đã phục hồi nhân viên'); }
    catch (e) { showToast(e?.error || 'Lỗi'); }
  };

  // ---------- Employee actions ----------
  const handleCheckIn = () => setScanState({ kind: 'checkin' });
  const handleCheckOut = () => setScanState({ kind: 'checkout' });

  const onScanCheckinDone = async (result) => {
    setScanState(null);
    setActive({ empId: session.empId, date: result.date, checkIn: result.checkIn, note: '' });
    setTodayNote('');
    showToast('Đã check-in lúc ' + `${String(result.checkIn.h).padStart(2,'0')}:${String(result.checkIn.m).padStart(2,'0')}`);
  };

  const onScanCheckoutDone = async () => {
    setScanState(null);
    setActive(null);
    setTodayNote('');
    await refresh();
    showToast('Đã chấm công xong');
  };

  // Persist note as user types (debounced)
  useEffect(() => {
    if (!active || !session?.empId) return;
    const t = setTimeout(() => { api.patchActive(session.empId, todayNote).catch(() => {}); }, 500);
    return () => clearTimeout(t);
  }, [todayNote, active?.date]);

  // ---------- Manager actions ----------
  const editTimesheet = async (id, checkIn, checkOut, note) => {
    await api.editTimesheet(id, checkIn, checkOut, note); setDetailRow(null); await refresh(); showToast('Đã chỉnh sửa');
  };
  const addManual = async (empId, date, checkIn, checkOut, note) => {
    await api.manual(empId, dateToISO(date), checkIn, checkOut, note);
    await refresh(); showToast('Đã thêm chấm công thủ công');
  };

  const me = employees.find((e) => e.id === session?.empId);

  // ---------- Render ----------
  if (!booted) return null;

  // Force change-password screen (full-screen, no way out)
  if (authed && session?.mustChangePassword) {
    return (
      <div className="device">
        <ChangePasswordScreen
          forceMode={true}
          onSubmit={handleChangePassword}
          onClose={null}
        />
        {toast && <div className="toast">{toast}</div>}
      </div>
    );
  }

  return (
    <div className="device">
      {!authed ? (
        <LoginScreen onLogin={onLogin} />
      ) : session.role === 'employee' ? (
        me ? (
          <EmployeeApp
            me={me}
            rows={rows.filter((r) => r.empId === session.empId)}
            empTab={empTab} setEmpTab={setEmpTab}
            todayState={active ? 'in' : 'out'}
            todayCheckIn={active?.checkIn}
            todayNote={todayNote} setTodayNote={setTodayNote}
            onCheckIn={handleCheckIn}
            onCheckOut={handleCheckOut}
            onSwitchRole={onLogout}
            onLogout={onLogout}
            onChangePassword={() => setChangePwOpen(true)}
          />
        ) : <div style={{ padding: 24 }} className="dim">Đang tải…</div>
      ) : (
        <ManagerApp
          rows={rows}
          employees={employees}
          accounts={accounts}
          mgrTab={mgrTab} setMgrTab={setMgrTab}
          onOpenDetail={setDetailRow}
          onAddManual={addManual}
          onSwitchRole={onLogout}
          onOpenExport={() => setExportOpen(true)}
          onLogout={onLogout}
          onCreateEmployee={createEmployee}
          onUpdateAccount={updateAccount}
          onUpdateEmployee={updateEmployeeFields}
          onArchiveEmployee={archiveEmployee}
          onUnarchiveEmployee={unarchiveEmployee}
          onChangePassword={() => setChangePwOpen(true)}
          onOpenQr={() => setQrOpen(true)}
        />
      )}

      {detailRow && (
        <DetailSheet
          row={detailRow}
          emp={employees.find((e) => e.id === detailRow.empId)}
          onClose={() => setDetailRow(null)}
          onEdit={editTimesheet}
        />
      )}

      {exportOpen && (
        <ExportSheet
          rows={rows}
          employees={employees}
          onClose={() => setExportOpen(false)}
          onDone={() => { setExportOpen(false); showToast('Đã xuất bảng lương'); }}
        />
      )}

      {changePwOpen && (
        <ChangePasswordScreen
          forceMode={false}
          onSubmit={handleChangePassword}
          onClose={() => setChangePwOpen(false)}
        />
      )}

      {scanState && (
        <ScanCheckin
          kind={scanState.kind}
          empId={session.empId}
          note={scanState.kind === 'checkout' ? todayNote : null}
          onCancel={() => setScanState(null)}
          onDone={scanState.kind === 'checkin' ? onScanCheckinDone : onScanCheckoutDone}
        />
      )}

      {qrOpen && <QrDisplayModal onClose={() => setQrOpen(false)} />}

      {toast && <div className="toast">{toast}</div>}
    </div>
  );
}

window.App = App;
window.fmtDate = fmtDate;
window.fmtTime = fmtTime;
window.fmtVND = fmtVND;
window.calcRow = calcRow;
window.calcPayroll = calcPayroll;
window.TODAY = TODAY;
window.SHIFT_START = SHIFT_START;
window.SHIFT_END = SHIFT_END;
