// Employee Self-Service portal
function ESS({ session, onLogout, embedded }) {
  const { useState, useMemo } = React;
  const ssESS = (x) => { const y = String(x).split(":"); return (+y[0] * 3600) + (+y[1] * 60) + (+y[2] || 0); };
  const atHm = (x) => String(x).slice(0, 5); // late/early compare on HH:MM, not the seconds
  const [apiEmp, setApiEmp] = useState(null);
  const [apiAtt, setApiAtt] = useState(null);
  const [apiDocs, setApiDocs] = useState(null);
  const [apiPayData, setApiPayData] = useState(null);
  const emp = apiEmp || { id: session.empId, name: session.name || "—", role: "", dept: "—", salary: 0, status: "Active", joined: "—" };
  const [tab, setTab] = useState("overview");
  const [openDay, setOpenDay] = useState(null); // day label of expanded attendance row
  const [myLeaves, setMyLeaves] = useState([]);

  React.useEffect(() => {
    API.hr.me().then(setApiEmp).catch(() => {});
    API.hr.myLeaves().then(setMyLeaves).catch(() => {});
    API.hr.myPay().then(setApiPayData).catch(() => {});
    API.hr.myDocs().then(setApiDocs).catch(() => {});
    API.hr.myTimeLogs(7).then((logs) => {
      const seen = {}; // several sessions can share a day: unique row keys, late on the first only
      setApiAtt(logs.map((t, i) => {
        const dayId = t.dateIso || t.day;
        const firstOfDay = !seen[dayId];
        seen[dayId] = true;
        const worked = t.out ? Math.max(0, ssESS(t.out) - ssESS(t.in) - t.breakSec) : 0;
        return {
          key: dayId + "#" + i, dateIso: t.dateIso,
          day: t.day, in: t.in, out: t.out || null, breakMin: t.breakMin, breakSec: t.breakSec,
          breakFrom: (t.breaks[0] || {}).from, breakTo: (t.breaks[0] || {}).to,
          workedSec: worked, hours: (worked / 3600).toFixed(1), // hours stays decimal for the week KPI
          late: firstOfDay && atHm(t.in) > "08:12", early: !!t.out && atHm(t.out) < "16:30"
        };
      }));
    }).catch(() => {});
  }, []);
  const [req, setReq] = useState({ type: "Annual", from: "", to: "", days: "", note: "" });
  const [errs, setErrs] = useState({});
  const [showReq, setShowReq] = useState(false);
  const [toasts, setToasts] = useState([]);

  React.useEffect(() => {
    const onToast = (e) => {
      const id = Date.now() + Math.random();
      setToasts((ts) => [...ts, { id, msg: e.detail }]);
      setTimeout(() => setToasts((ts) => ts.filter((x) => x.id !== id)), 3200);
    };
    window.addEventListener("erp:toast", onToast);
    return () => window.removeEventListener("erp:toast", onToast);
  }, []);

  // A neutral placeholder covers the moment before /employees/me/ answers
  // (or a missing contract) — there is no demo contract to fall back to.
  const placeholderContract = { type: "—", start: "—", end: null, hours: 0, notice: "—", probation: "—",
    probationPassed: false, leaveEntitlement: 21, sickEntitlement: 14, allowances: [], permit: null, daysLeft: null };
  const c = (apiEmp && apiEmp.contract) || placeholderContract;
  // HR writes the first contract by hand — until then the card says so instead
  // of showing dashes that read like a loading state.
  const noContract = !!apiEmp && !apiEmp.contract;
  const todayLabel = new Date().toLocaleDateString(uiLocale(), { weekday: "long", month: "long", day: "numeric", year: "numeric" });
  const attendance = apiAtt || [];
  const workedDays = attendance.filter((a) => !a.weekend);
  const totalHrs = workedDays.reduce((s, a) => s + parseFloat(a.hours || 0), 0);
  const lateCount = workedDays.filter((a) => a.late).length;
  const sickDays = myLeaves.filter((l) => l.type === "Sick" && l.status === "Approved").reduce((s, l) => s + l.days, 0);
  const usedDays = myLeaves.filter((l) => l.status === "Approved").reduce((s, l) => s + l.days, 0);
  // Annual leave accrues day by day over the employee's own 12-month cycle, so
  // the balance is measured against what is earned so far — not the full year.
  const accrual = leaveAccrual(emp.joinedIso, c.leaveEntitlement);
  const inCycle = (l) => !accrual.cycleStart || (l.fromIso && l.fromIso >= accrual.cycleStart && (!accrual.cycleEnd || l.fromIso <= accrual.cycleEnd));
  const annualUsed = myLeaves.filter((l) => l.type === "Annual" && l.status === "Approved" && inCycle(l)).reduce((s, l) => s + l.days, 0);
  const annualLeft = Math.max(0, accrual.accrued - annualUsed);
  const pendingCount = myLeaves.filter((l) => l.status === "Pending").length;

  // Month names come from Intl so they follow the active language.
  const periodName = (p) => {
    const x = String(p).split("-");
    const d = new Date(+x[0] || 2026, (+x[1] || 1) - 1, 1);
    return d.toLocaleDateString(uiLocale(), { month: "long", year: "numeric" });
  };
  const payslips = (() => {
    if (!apiPayData) return [];
    const p = apiPayData;
    const al = (p.allowances || []).reduce((s, a) => s + a.amt, 0);
    const b = p.adjustments.filter((a) => a.kind !== "Deduction").reduce((s, a) => s + a.amount, 0);
    const dd = p.adjustments.filter((a) => a.kind === "Deduction").reduce((s, a) => s + a.amount, 0);
    const ar = p.advances.filter((v) => v.status === "Active").reduce((s, v) => s + Math.min(v.monthly, v.amount - v.repaid), 0);
    const net = p.salary + al + b - dd - ar;
    const cur = API.fmt.period();
    const curRec = p.records.find((r) => r.period === cur);
    const rows = [{ period: periodName(cur), status: curRec ? curRec.status : "Pending", paid: curRec ? curRec.paidOn : null, net }];
    p.records.filter((r) => r.period !== cur).forEach((r) => rows.push({ period: periodName(r.period), status: r.status, paid: r.paidOn, net: p.salary + al }));
    return rows;
  })();
  const docsList = apiDocs || [];

  const isEarly = req.type === "Early leave";
  const between = (f, t) => Math.round((new Date(t) - new Date(f)) / 86400000) + 1;
  const reqDays = isEarly ? null : (req.from && req.to && new Date(req.to) >= new Date(req.from) ? between(req.from, req.to) : null);
  const submitReq = () => {
    const e = {};
    if (!req.from) e.from = isEarly ? T("ess.errChooseDate") : T("ess.errChooseStartDate");
    if (!isEarly) {
      if (!req.to) e.to = T("ess.errChooseEndDate");
      else if (req.from && new Date(req.to) < new Date(req.from)) e.to = T("ess.errEndBeforeStart");
    }
    setErrs(e);
    if (Object.keys(e).length) return;
    const payload = isEarly
      ? { type: req.type, fromIso: req.from, toIso: req.from, days: 0, note: req.note }
      : { type: req.type, fromIso: req.from, toIso: req.to, days: reqDays || 0, note: req.note };
    API.hr.myLeaveCreate(payload)
      .then((row) => {
        setMyLeaves((ls) => [row, ...ls]);
        setReq({ type: "Annual", from: "", to: "", days: "", note: "" });
        setShowReq(false);
        notify(T("ess.requestSent"));
      })
      .catch((ex) => notify(T("ess.errSendRequest", { message: ex.message })));
  };

  const tabs = [
    { id: "overview", label: T("inst.overview") },
    { id: "attendance", label: T("ess.attendance"), count: workedDays.length },
    { id: "requests", label: T("ess.myRequests"), count: pendingCount },
    { id: "payslips", label: T("ess.payslips") },
    { id: "docs", label: T("ess.documents"), count: docsList.length },
    { id: "profile", label: T("ess.profile") }
  ];

  const DAY_FULL = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
  // Weekday off the date itself — the row's label is the date, and an English
  // name parsed out of it could not be translated anyway.
  const dayName = (r) => (r.dateIso ? DAY_FULL[new Date(r.dateIso + "T00:00:00").getDay()] : r.day);
  const dayDate = (r) => dmy(r.dateIso) || "—";

  const body = (
        <div className={embedded ? "page" : "content ess-content"} data-screen-label="My Portal">
          <div className="ess-hello">
            <h2 className="display">{T("ess.hello", { name: emp.name.split(" ")[0] })}</h2>
            <p className="dim">{todayLabel}{[emp.role, emp.dept].filter((x) => x && x !== "—").map((x) => " · " + TV(x)).join("")}</p>
          </div>

          <KpiBar>
            <Stat label={T("ess.hoursThisWeek")} value={<HoursHM h={totalHrs} />} sub={lateCount ? T("ess.lateArrivalsN", { n: lateCount }) : T("ess.noLateArrivals")} />
            <Stat label={T("ess.leaveUsed")} value={fmtDays(annualUsed) + "/" + fmtDays(accrual.accrued)}
              sub={accrual.known ? T("ess.accruedOfTotal", { total: fmtDays(accrual.total) }) : T("ess.annualEntitlement")} />
            <Stat label={T("ess.sickDays")} value={sickDays + "/" + c.sickEntitlement} sub={T("ess.thisYear")} />
            <Stat label={T("ess.pendingRequests")} value={String(pendingCount)} sub={pendingCount ? T("ess.awaitingHrApproval") : T("ess.allSettled")} />
          </KpiBar>

          <Tabs active={tab} onChange={setTab} tabs={tabs} />

          {tab === "overview" ? (
            <div className="ep-overview">
              <ClockCard emp={emp} />
              {pendingCount > 0 ? (
                <div className="role-locked" style={{ marginBottom: 14 }}><Icon name="clock" size={14} />{T("ess.pendingBanner", { n: pendingCount })}</div>
              ) : null}
              <div className="grid-23 ess-grid">
                <div>
                  <SectionHead title={T("ess.thisWeek")} />
                  <section className="card" style={{ marginTop: 8 }}>
                    <DataTable tableId="ess-week" rowKey={(r) => r.key || r.day} rows={attendance.slice(0, 5)} cols={[
                    { k: "day", label: T("ess.day"), w: "110px", render: (r) => <b className="nw mono">{dayDate(r)}</b> },
                    { k: "in", label: T("ess.in"), w: "100px", render: (r) => <span className="mono"><TimeSec t={r.in} />{r.late ? <span className="ep-flag late">{T("ess.lateFlag")}</span> : null}</span> },
                    { k: "out", label: T("ess.out"), w: "100px", render: (r) => <span className="mono"><TimeSec t={r.out} /></span> },
                    { k: "hours", label: T("hr.hours"), align: "right", w: "90px", render: (r) => <span className="mono strong"><DurSec s={r.workedSec} /></span> }
                  ]} />
                  </section>
                </div>
                <div>
                  <SectionHead title={T("ess.myContract")} />
                  <section className="card pad" style={{ marginTop: 8 }}>
                  {noContract ? (
                    <div className="role-locked"><Icon name="alert" size={14} />{T("ess.noContractYet")}</div>
                  ) : (
                    <>
                      <KV k={T("hr.contractNo")} v={c.no != null ? "#" + c.no : "—"} mono />
                      <KV k={T("common.type")} v={TV(c.type)} />
                      <KV k={T("ess.start")} v={c.start} />
                      <KV k={T("ess.end")} v={c.end || T("hr.openEnded")} />
                      <KV k={T("hr.hoursPerWeek")} v={<HoursHM h={c.hours} />} mono />
                      <KV k={T("hr.salaryPerMonth")} v={money(emp.salary)} mono />
                      <KV k={T("hr.noticePeriod")} v={TV(c.notice)} />
                    </>
                  )}
                  </section>
                </div>
              </div>
            </div>
          ) : null}

          {tab === "attendance" ? (
            <section className="card">
              <DataTable tableId="ess-att" rowKey={(r) => r.key || r.day} rows={attendance}
                expanded={(r) => {
                  if (r.weekend || openDay !== r.day) return null;
                  return (
                    <div className="att-detail">
                      <span className="clock-pt"><b className="mono"><TimeSec t={r.in} /></b><small>{T("clock.checkedIn")}{r.late ? " · " + T("ess.lateFlag") : ""}</small></span>
                      <span className="clock-pt"><b className="mono"><DurSec s={r.breakSec} /></b><small>{T("ess.totalBreak")}</small></span>
                      <span className="clock-pt"><b className="mono"><TimeSec t={r.out} /></b><small>{T("clock.checkedOut")}{r.early ? " · " + T("ess.earlyFlag") : ""}</small></span>
                      <span className="clock-pt"><b className="mono"><DurSec s={r.workedSec} /></b><small>{T("ess.netWorked")}</small></span>
                    </div>
                  );
                }}
                onRow={(r) => { if (!r.weekend) setOpenDay(openDay === r.day ? null : r.day); }} cols={[
                { k: "day", label: T("ess.day"), w: "140px", render: (r) => r.weekend ? <span className="dim">{TV(dayName(r))}</span> : (
                    <span className="att-day"><Icon name="chevR" size={13} style={{ transform: openDay === r.day ? "rotate(90deg)" : "none", color: "var(--ink3)" }} /><b>{TV(dayName(r))}</b></span>
                  ) },
                { k: "date", label: T("common.date"), w: "110px", render: (r) => <span className={r.weekend ? "dim mono" : "mono"}>{dayDate(r)}</span> },
                { k: "in", label: T("ess.checkIn"), w: "120px", render: (r) => r.weekend ? <span className="dim">{T("ess.weekend")}</span> : <span className="mono"><TimeSec t={r.in} />{r.late ? <span className="ep-flag late">{T("ess.lateFlag")}</span> : null}</span> },
                { k: "out", label: T("ess.checkOut"), w: "120px", render: (r) => r.weekend ? <span className="dim">—</span> : <span className="mono"><TimeSec t={r.out} />{r.early ? <span className="ep-flag early">{T("ess.earlyFlag")}</span> : null}</span> },
                { k: "break", label: T("clock.break"), w: "100px", render: (r) => r.weekend || !r.breakSec ? <span className="dim">—</span> : <span className="mono"><DurSec s={r.breakSec} /></span> },
                { k: "hours", label: T("hr.hours"), align: "right", w: "100px", render: (r) => r.weekend ? <span className="dim">—</span> : <span className="mono strong"><DurSec s={r.workedSec} /></span> }
              ]} />
            </section>
          ) : null}

          {tab === "requests" ? (
            <>
              <div className="toolbar page-toolbar">
                <span className="dim" style={{ fontSize: 13 }}>{pendingCount ? T("ess.requestsAwaiting", { n: pendingCount }) : T("ess.leaveRequestsHint")}</span>
                <Btn variant="primary" icon="plus" onClick={() => { setReq({ type: "Annual", from: "", to: "", days: "", note: "" }); setErrs({}); setShowReq(true); }}>{T("ess.newRequest")}</Btn>
              </div>
              <section className="card">
                <DataTable rowKey={(r) => r.id} rows={myLeaves} empty={T("ess.noRequests")} cols={[
                  { k: "type", label: T("common.type"), render: (r) => <b>{TV(r.type)}</b> },
                  { k: "from", label: T("ess.from"), w: "96px", render: (r) => <span className="dim mono">{r.from}</span> },
                  { k: "to", label: T("ess.to"), w: "96px", render: (r) => <span className="dim mono">{r.type === "Early leave" ? "—" : r.to}</span> },
                  { k: "days", label: T("ess.days"), align: "right", w: "64px", render: (r) => r.days ? <span className="mono strong">{r.days}</span> : <span className="dim">—</span> },
                  { k: "status", label: T("common.status"), w: "112px", render: (r) => <Badge tone={r.status === "Pending" ? "amber" : r.status === "Declined" ? "red" : undefined}>{r.status}</Badge> }
                ]} />
              </section>

              <Modal open={showReq} onClose={() => setShowReq(false)} width={460}
                title={T("ess.newLeaveRequest")} sub={T("ess.sentToHr")}
                footer={
                  <div className="btnrow">
                    <Btn onClick={() => setShowReq(false)}>{T("common.cancel")}</Btn>
                    <Btn variant="primary" icon="send" onClick={submitReq}>{T("ess.submitRequest")}</Btn>
                  </div>
                }>
                <Field label={T("common.type")}>
                  <SearchSelect options={["Annual", "Sick", "Unpaid", "Early leave"]} value={req.type} onChange={(v) => setReq({ ...req, type: v, from: "", to: "" })} />
                </Field>
                {isEarly ? (
                  <Field label={T("common.date")} error={errs.from}><Input type="date" value={req.from} onChange={(e) => setReq({ ...req, from: e.target.value })} /></Field>
                ) : (
                  <div>
                    <div className="formgrid">
                      <Field label={T("ess.from")} error={errs.from}><Input type="date" value={req.from} onChange={(e) => setReq({ ...req, from: e.target.value })} /></Field>
                      <Field label={T("ess.to")} error={errs.to}><Input type="date" value={req.to} onChange={(e) => setReq({ ...req, to: e.target.value })} /></Field>
                    </div>
                    {reqDays ? <p className="formhint" style={{ marginTop: -4 }}>{TP("ess.daysRequested", reqDays)}</p> : null}
                    {/* Over the accrued balance is allowed — HR decides. The employee just sees it. */}
                    {req.type === "Annual" && reqDays && reqDays > annualLeft ? (
                      <p className="formhint warn">{T("ess.overAccrued", { over: fmtDays(reqDays - annualLeft), left: fmtDays(annualLeft) })}</p>
                    ) : null}
                  </div>
                )}
                <Field label={T("common.noteOptional")}><Input placeholder={T("ess.notePlaceholder")} value={req.note} onChange={(e) => setReq({ ...req, note: e.target.value })} /></Field>
              </Modal>
            </>
          ) : null}

          {tab === "payslips" ? (
            <><div className="toolbar page-toolbar">
              <span className="dim" style={{ fontSize: 13 }}>{T("ess.payslipsHint")}</span>
              <Btn variant="primary" icon="download" onClick={() => notify(T("ess.downloadingPayslip", { period: (payslips.find((p) => p.status === "Paid") || payslips[0]).period }))}>{T("ess.downloadPayslip")}</Btn>
            </div>
            <section className="card">
              <DataTable rowKey={(r) => r.period} rows={payslips} cols={[
                { k: "period", label: T("hr.period"), render: (r) => <b>{r.period}</b> },
                { k: "paid", label: T("ess.paidOn"), w: "110px", render: (r) => r.paid ? <span className="dim mono">{r.paid}</span> : <span className="dim">—</span> },
                { k: "net", label: T("ess.netPay"), align: "right", w: "130px", render: (r) => <span className="mono strong">{money(r.net)}</span> },
                { k: "status", label: T("common.status"), w: "120px", render: (r) => <Badge tone={r.status === "Paid" ? "green" : r.status === "Processing" ? "blue" : "amber"}>{r.status}</Badge> }
              ]} />
            </section></>
          ) : null}

          {tab === "docs" ? (
            <div className="ep-docs">
              {docsList.map((d, i) => (
                <div className="ep-doc" key={i}>
                  <span className="ep-doc-ic"><Icon name="file" size={18} /></span>
                  <span className="ep-doc-main"><b>{d.name}</b><small>{TV(d.type)} · {d.size} · {d.date}</small></span>
                  <button type="button" className="ep-doc-btn" onClick={() => notify(T("ess.downloadingFile", { name: d.name }))} title={T("common.download")}>
                    <Icon name="download" size={16} />
                  </button>
                </div>
              ))}
            </div>
          ) : null}

          {tab === "profile" ? (
            <div className="grid-23 ess-grid">
              <section className="card pad">
                <SectionHead title={T("ess.myDetails")} />
                <div className="udetail" style={{ paddingBottom: 12 }}>
                  <Avatar name={emp.name} size={52} />
                  <div className="udetail-id">
                    <b>{emp.name}</b>
                    <span className="dim">{TV(emp.role)}</span>
                    <span className="udetail-tags"><Badge tone={emp.status === "On leave" ? "amber" : undefined}>{emp.status}</Badge></span>
                  </div>
                </div>
                <KV k={T("ess.employeeNo")} v={emp.no != null ? "#" + emp.no : "—"} mono />
                <KV k={T("common.email")} v={session.email} mono />
                <KV k={T("hr.department")} v={TV(emp.dept)} />
                <KV k={T("hr.joined")} v={emp.joined} />
              </section>
              <section className="card pad">
                <SectionHead title={T("ess.entitlements")} />
                <KV k={T("hr.annualLeave")} v={T("hr.daysPerYear", { n: fmtDays(c.leaveEntitlement) })} />
                <KV k={T("ess.accruedBalance")} v={T("ess.usedOfDays", { used: fmtDays(annualUsed), total: fmtDays(accrual.accrued) })} />
                {accrual.known && accrual.cycleStart ? <KV k={T("ess.accrualCycle")} v={T("ess.cycleRange", { from: dmy(accrual.cycleStart), to: dmy(accrual.cycleEnd) })} mono /> : null}
                <KV k={T("hr.sickLeave")} v={T("ess.usedOfDays", { used: sickDays, total: c.sickEntitlement })} />
                <KV k={T("hr.probation")} v={TV(c.probation) + (c.probationPassed ? " · " + T("hr.passed") : " · " + T("hr.inProgress"))} />
                {c.allowances.map((a, i) => <KV key={i} k={TV(a.name)} v={T("hr.perMonthAmount", { amount: money(a.amt) })} mono />)}
                {c.permit ? <KV k={T("hr.workPermit")} v={T("hr.permitExpires", { no: c.permit.no, exp: c.permit.exp })} mono /> : null}
                <p className="formhint" style={{ marginTop: 12 }}>{T("ess.contactHrHint")}</p>
              </section>
            </div>
          ) : null}
        </div>
  );
  if (embedded) return body;
  return (
    <div className="shell ess-shell" data-theme="light">
      <main className="main">
        <header className="topbar">
          <div className="topbar-left">
            <div className="logo ess-logo">
              <span className="logo-mark"></span>
              <span className="logo-word display">{T("app.brand")}</span>
              <span className="logo-tag">{T("ess.selfService")}</span>
            </div>
          </div>
          <div className="topbar-right">
            <LangPop />
            <span className="ess-user">
              <Avatar name={emp.name} size={30} />
              <span className="side-user"><b>{emp.name}</b><small>{TV(emp.role)}</small></span>
            </span>
            <Btn className="ess-logout" icon="logout" onClick={onLogout}>{T("app.signOut")}</Btn>
          </div>
        </header>
        {body}
      </main>

      <div className="toasts">
        {toasts.map((x) => (
          <div key={x.id} className="toast"><Icon name="check" size={14} stroke={2.4} />{x.msg}</div>
        ))}
      </div>
    </div>
  );
}

// ESS embedded inside the ERP shell as the "My Portal" nav tab.
// /employees/me/ is the only source of truth — there is no offline mode.
function MyPortal() {
  return <ESS embedded session={window.__session || {}} />;
}
Object.assign(window, { ESS, MyPortal });
