/* ============================================================
   App — shell, routing, state, tweaks
   ============================================================ */
const { useState, useEffect, useRef } = React;
const ACCENTS = {
  royal:    { a: "#5A58E7", ink: "#4340C4", soft: "#E7E7FB" },
  sky:      { a: "#3490EB", ink: "#1F6FD0", soft: "#DEEBFE" },
  amethyst: { a: "#7D50CA", ink: "#5E37A0", soft: "#ECE1F8" },
  slate:    { a: "#5C6680", ink: "#434C63", soft: "#E4E8F1" },
};

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "accent": "royal",
  "displayFont": "Montserrat",
  "density": "regular",
  "showAvatarsInCal": true
}/*EDITMODE-END*/;

function applyTokens(t) {
  const root = document.documentElement;
  const ac = ACCENTS[t.accent] || ACCENTS.royal;
  root.style.setProperty("--accent", ac.a);
  root.style.setProperty("--accent-ink", ac.ink);
  root.style.setProperty("--accent-soft", ac.soft);
  root.style.setProperty("--t-vacation", ac.a);
  root.style.setProperty("--t-vacation-soft", ac.soft);
  root.style.setProperty("--font-display", `"${t.displayFont}", system-ui, sans-serif`);
  const dens = { compact: ["14px", "16px"], regular: ["20px", "22px"], comfy: ["26px", "28px"] }[t.density] || ["20px", "22px"];
  root.style.setProperty("--gap", dens[0]);
  root.style.setProperty("--pad-card", dens[1]);
}

/* ---- Toast ---- */
function Toast({ toast }) {
  if (!toast) return null;
  const tone = toast.tone || "default";
  const colors = {
    default: { bg: "var(--ink)", fg: "#fff" },
    approved: { bg: "var(--st-approved)", fg: "#fff" },
    declined: { bg: "var(--st-declined)", fg: "#fff" },
  }[tone];
  return (
    <div className="vp-pop" style={{
      position: "fixed", bottom: 26, left: "50%", transform: "translateX(-50%)", zIndex: 80,
      background: colors.bg, color: colors.fg, padding: "13px 20px", borderRadius: "var(--r-pill)",
      boxShadow: "var(--sh-pop)", fontSize: 14.5, fontWeight: 600, display: "flex", alignItems: "center", gap: 10,
    }}>
      <Icon name={toast.icon || "check"} size={18} stroke={2.2} /> {toast.msg}
    </div>
  );
}

/* ---- Account switcher ---- */
function AccountMenu({ user, onSwitch, onLogout }) {
  const [open, setOpen] = useState(false);
  const ref = useRef(null);
  useEffect(() => {
    const h = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener("mousedown", h); return () => document.removeEventListener("mousedown", h);
  }, []);
  return (
    <div ref={ref} style={{ position: "relative" }}>
      <button type="button" onClick={() => setOpen((o) => !o)} className="vp-focusable"
        style={{ display: "flex", alignItems: "center", gap: 10, background: open ? "var(--paper-2)" : "transparent", border: "1px solid var(--line)", padding: "5px 9px 5px 5px", borderRadius: "var(--r-pill)", cursor: "pointer" }}>
        <Avatar person={user} size={32} />
        <div style={{ textAlign: "left", lineHeight: 1.25 }}>
          <div style={{ fontWeight: 600, fontSize: 13.5, whiteSpace: "nowrap" }}>{user.first} {user.last}</div>
          <div style={{ fontSize: 11.5, color: "var(--ink-4)" }}>{user.role === "ceo" ? "CEO · Admin" : "Employee"}</div>
        </div>
        <Icon name="chevDown" size={16} style={{ color: "var(--ink-4)", transform: open ? "rotate(180deg)" : "none", transition: "transform .15s" }} />
      </button>
      {open && (
        <div className="vp-pop" style={{ position: "absolute", top: "calc(100% + 8px)", right: 0, width: 268, background: "var(--surface)", border: "1px solid var(--line)", borderRadius: "var(--r-md)", boxShadow: "var(--sh-lg)", padding: 7, zIndex: 30 }}>
          <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: "0.06em", textTransform: "uppercase", color: "var(--ink-4)", padding: "8px 10px 6px" }}>Switch account (demo)</div>
          <div style={{ maxHeight: 260, overflowY: "auto" }}>
            {VP_DATA.people.map((p) => (
              <button key={p.id} type="button" onClick={() => { onSwitch(p); setOpen(false); }}
                style={{ width: "100%", display: "flex", alignItems: "center", gap: 10, padding: "8px 10px", border: "none", background: p.id === user.id ? "var(--accent-soft)" : "transparent", borderRadius: "var(--r-sm)", cursor: "pointer", textAlign: "left" }}>
                <Avatar person={p} size={30} />
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontWeight: 600, fontSize: 13.5 }}>{p.first} {p.last}</div>
                  <div style={{ fontSize: 11.5, color: "var(--ink-3)" }}>{p.role === "ceo" ? "CEO · Admin" : p.team}</div>
                </div>
                {p.id === user.id && <Icon name="check" size={15} style={{ color: "var(--accent)" }} />}
              </button>
            ))}
          </div>
          <div style={{ borderTop: "1px solid var(--line)", marginTop: 6, paddingTop: 6 }}>
            <button type="button" onClick={onLogout} style={{ width: "100%", display: "flex", alignItems: "center", gap: 10, padding: "9px 10px", border: "none", background: "transparent", borderRadius: "var(--r-sm)", cursor: "pointer", color: "var(--ink-2)", fontWeight: 600, fontSize: 13.5 }}>
              <Icon name="logout" size={17} /> Sign out
            </button>
          </div>
        </div>
      )}
    </div>
  );
}

/* ---- Sidebar ---- */
function Sidebar({ user, view, onGoto, pendingCount, narrow }) {
  const items = [
    { id: "dashboard", label: "Home", icon: "home" },
    ...(user.role === "ceo" ? [{ id: "approvals", label: "Approvals", icon: "inbox", badge: pendingCount }] : []),
    { id: "calendar", label: "Team calendar", icon: "calendar" },
    ...(user.role === "ceo" ? [{ id: "team", label: "Team & admin", icon: "users" }] : []),
  ];
  return (
    <aside style={{
      width: narrow ? "100%" : 230, flex: "0 0 auto",
      padding: narrow ? "10px 12px" : "22px 16px",
      display: "flex", flexDirection: narrow ? "row" : "column",
      alignItems: narrow ? "center" : "stretch", gap: narrow ? 6 : 26,
      borderRight: narrow ? "none" : "1px solid var(--line)",
      borderBottom: narrow ? "1px solid var(--line)" : "none",
      background: "var(--surface-2)", overflowX: narrow ? "auto" : "visible",
      position: narrow ? "sticky" : "static", top: 0, zIndex: 20,
    }}>
      <div style={{ display: "flex", alignItems: "center", gap: 10, padding: narrow ? "0 6px 0 2px" : "0 8px", flex: "0 0 auto" }}>
        <MMark size={narrow ? 28 : 32} />
        {!narrow && <Wordmark size={17} />}
      </div>
      <nav style={{ display: "flex", flexDirection: narrow ? "row" : "column", gap: narrow ? 4 : 4, flex: narrow ? 1 : "none" }}>
        {items.map((it) => {
          const on = view === it.id;
          return (
            <button key={it.id} type="button" onClick={() => onGoto(it.id)} className="vp-focusable"
              title={it.label}
              style={{
                display: "flex", alignItems: "center", justifyContent: "center",
                gap: narrow ? 8 : 12, padding: narrow ? "9px 12px" : "10px 12px",
                minHeight: 44, borderRadius: "var(--r-sm)",
                border: "none", cursor: "pointer", fontFamily: "var(--font-body)", fontWeight: 600, fontSize: 14.5,
                background: on ? "var(--surface)" : "transparent", color: on ? "var(--ink)" : "var(--ink-3)",
                boxShadow: on ? "var(--sh-sm)" : "none", transition: "all 0.14s", whiteSpace: "nowrap",
              }}>
              <Icon name={it.icon} size={19} stroke={on ? 2 : 1.7} style={{ color: on ? "var(--accent)" : "var(--ink-4)" }} />
              {(!narrow || on) && <span style={{ flex: narrow ? "none" : 1, textAlign: "left" }}>{it.label}</span>}
              {it.badge > 0 && <span style={{ background: "var(--accent)", color: "#fff", fontSize: 11.5, fontWeight: 700, minWidth: 20, height: 20, borderRadius: 10, display: "grid", placeItems: "center", padding: "0 6px" }}>{it.badge}</span>}
            </button>
          );
        })}
      </nav>
    </aside>
  );
}

function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  useEffect(() => { applyTokens(t); }, [t]);

  const [user, setUser] = useState(null);
  const [view, setView] = useState("dashboard");
  const [requests, setRequests] = useState(() => VP_DATA.requests.map((r) => ({ ...r })));
  const [peopleVersion, setPeopleVersion] = useState(0);
  const [modal, setModal] = useState(null); // {mode:'new'|'view'|'person'|'editPerson', req|person}
  const [toast, setToast] = useState(null);
  const toastTimer = useRef(null);

  function flash(msg, opts = {}) {
    setToast({ msg, ...opts });
    clearTimeout(toastTimer.current);
    toastTimer.current = setTimeout(() => setToast(null), 2600);
  }

  function addRequest(req) {
    setRequests((rs) => [req, ...rs]);
    setModal(null);
    flash("Request submitted.");
  }
  function decide(id, status, by, reason) {
    setRequests((rs) => rs.map((r) => r.id === id ? { ...r, status, decidedBy: by, decidedOn: TODAY, reason: reason || r.reason } : r));
    setModal(null);
    flash(status === "approved" ? "Request approved." : "Request declined.", { tone: status, icon: status === "approved" ? "check" : "x" });
  }
  function goto(v) { setView(v); }

  function savePerson(p, isNew) {
    if (isNew) { VP_DATA.people.push(p); }
    else { const i = VP_DATA.people.findIndex((x) => x.id === p.id); if (i >= 0) VP_DATA.people[i] = p; }
    VP_DATA.byId[p.id] = p;
    VP_DATA.ceos = VP_DATA.people.filter((x) => x.role === "ceo");
    setPeopleVersion((v) => v + 1);
    setModal(null);
    flash(isNew ? `${p.first} ${p.last} added.` : "Changes saved.");
  }
  function archivePerson(p) {
    // keep historical records (byId) intact; remove from the active directory
    VP_DATA.people = VP_DATA.people.filter((x) => x.id !== p.id);
    VP_DATA.ceos = VP_DATA.people.filter((x) => x.role === "ceo");
    setPeopleVersion((v) => v + 1);
    setModal(null);
    flash(`${p.first} ${p.last}'s access was removed.`, { tone: "declined", icon: "x" });
  }

  const pendingCount = requests.filter((r) => r.status === "pending").length;
  const narrow = useIsNarrow(820);

  if (!user) return (<><LoginScreen onLogin={(p) => { setUser(p); setView(p.role === "ceo" ? "dashboard" : "dashboard"); }} /><TweaksHost t={t} setTweak={setTweak} /></>);

  return (
    <div style={{ display: "flex", flexDirection: narrow ? "column" : "row", height: "100%", overflow: narrow ? "auto" : "hidden" }}>
      <Sidebar user={user} view={view} onGoto={goto} pendingCount={pendingCount} narrow={narrow} />
      <div style={{ flex: 1, display: "flex", flexDirection: "column", minWidth: 0 }}>
        {/* top bar */}
        <header style={{ display: "flex", alignItems: "center", justifyContent: "flex-end", gap: 14, padding: narrow ? "11px 16px" : "13px 28px", borderBottom: "1px solid var(--line)", background: "var(--surface)" }}>
          <button type="button" className="vp-focusable" style={{ ...navBtn, position: "relative", border: "1px solid var(--line)" }} title="Notifications">
            <Icon name="bell" size={18} />
            {pendingCount > 0 && user.role === "ceo" && <span style={{ position: "absolute", top: 6, right: 6, width: 8, height: 8, borderRadius: "50%", background: "var(--accent)", border: "1.5px solid var(--surface)" }} />}
          </button>
          <AccountMenu user={user} onSwitch={(p) => { setUser(p); setView("dashboard"); }} onLogout={() => setUser(null)} />
        </header>

        {/* main */}
        <main style={{ flex: 1, overflowY: narrow ? "visible" : "auto", padding: narrow ? "20px 16px 48px" : "30px 36px 60px", maxWidth: 1180, width: "100%", margin: "0 auto" }}>
          {view === "dashboard" && <Dashboard user={user} requests={requests} onNewRequest={() => setModal({ mode: "new" })} onOpenRequest={(r) => setModal({ mode: "view", req: r })} onGoto={goto} />}
          {view === "approvals" && <ApprovalsInbox user={user} requests={requests} onOpenRequest={(r) => setModal({ mode: "view", req: r })} onDecide={decide} onNewRequest={() => setModal({ mode: "new" })} />}
          {view === "calendar" && <TeamCalendar user={user} requests={requests} onOpenRequest={(r) => setModal({ mode: "view", req: r })} onNewRequest={() => setModal({ mode: "new" })} />}
          {view === "team" && user.role === "ceo" && <TeamAdmin user={user} requests={requests} onAddPerson={() => setModal({ mode: "person" })} onEditPerson={(p) => setModal({ mode: "editPerson", person: p })} />}
        </main>
      </div>

      {/* Modals */}
      {modal?.mode === "new" && <Modal onClose={() => setModal(null)}><RequestForm user={user} requests={requests} onSubmit={addRequest} onClose={() => setModal(null)} /></Modal>}
      {modal?.mode === "view" && <Modal onClose={() => setModal(null)} width={540}><RequestDetail req={requests.find((r) => r.id === modal.req.id) || modal.req} user={user} requests={requests} onDecide={decide} onClose={() => setModal(null)} /></Modal>}
      {modal?.mode === "person" && <Modal onClose={() => setModal(null)} width={600}><PersonForm onSave={savePerson} onClose={() => setModal(null)} /></Modal>}
      {modal?.mode === "editPerson" && <Modal onClose={() => setModal(null)} width={600}><PersonForm person={modal.person} onSave={savePerson} onArchive={archivePerson} onClose={() => setModal(null)} /></Modal>}

      <Toast toast={toast} />
      <TweaksHost t={t} setTweak={setTweak} />
    </div>
  );
}

function TweaksHost({ t, setTweak }) {
  return (
    <TweaksPanel>
      <TweakSection label="Color" />
      <TweakColor label="Accent" value={t.accent === "royal" ? "#5A58E7" : t.accent === "sky" ? "#3490EB" : t.accent === "amethyst" ? "#7D50CA" : "#5C6680"}
        options={["#5A58E7", "#3490EB", "#7D50CA", "#5C6680"]}
        onChange={(hex) => { const m = { "#5A58E7": "royal", "#3490EB": "sky", "#7D50CA": "amethyst", "#5C6680": "slate" }; setTweak("accent", m[hex] || "royal"); }} />
      <TweakSection label="Typography" />
      <TweakSelect label="Display font" value={t.displayFont}
        options={["Montserrat", "Space Grotesk", "Arial"]}
        onChange={(v) => setTweak("displayFont", v)} />
      <TweakSection label="Layout" />
      <TweakRadio label="Density" value={t.density} options={["compact", "regular", "comfy"]} onChange={(v) => setTweak("density", v)} />
    </TweaksPanel>
  );
}

/* ---- Access gate ----
   Shared team password. Stored only as a SHA-256 hash below — never in clear text.
   This is an access barrier for an internal tool, NOT full authentication: the static
   files remain fetchable by someone technical. For stronger protection use Vercel
   Password Protection (Pro) or real auth.
   To change the password: run  printf '%s' 'NEW_PASSWORD' | shasum -a 256
   and paste the resulting hash into GATE_HASH. */
const GATE_HASH = "7bce0333ed2c4cd995357d26ac83fda9cd69bb05934ebef92a8f7bbd38186d60";
const GATE_KEY = "mesper_gate_v1";

async function sha256Hex(str) {
  const buf = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(str));
  return Array.from(new Uint8Array(buf)).map((b) => b.toString(16).padStart(2, "0")).join("");
}

function Gate({ children }) {
  const [ok, setOk] = useState(() => {
    try { return localStorage.getItem(GATE_KEY) === GATE_HASH; } catch (e) { return false; }
  });
  const [pw, setPw] = useState("");
  const [err, setErr] = useState(false);
  const [busy, setBusy] = useState(false);

  if (ok) return children;

  async function submit(e) {
    e.preventDefault();
    if (busy || !pw) return;
    setBusy(true); setErr(false);
    const h = await sha256Hex(pw);
    setBusy(false);
    if (h === GATE_HASH) {
      try { localStorage.setItem(GATE_KEY, GATE_HASH); } catch (e2) {}
      setOk(true);
    } else {
      setErr(true); setPw("");
    }
  }

  return (
    <div style={{ minHeight: "100%", display: "grid", placeItems: "center", padding: 24, background: "var(--paper)" }}>
      <form onSubmit={submit} style={{ width: "100%", maxWidth: 380, background: "var(--surface)", border: "1px solid var(--line)", borderRadius: "var(--r-xl)", boxShadow: "var(--sh-lg)", padding: "30px 28px" }}>
        <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 22 }}>
          <MMark size={32} />
          <Wordmark size={17} />
        </div>
        <h1 style={{ fontFamily: "var(--font-display)", fontSize: 24, fontWeight: 800, color: "var(--ink)", margin: "0 0 6px" }}>Restricted</h1>
        <p style={{ fontSize: 14, color: "var(--ink-3)", margin: "0 0 22px", lineHeight: 1.5 }}>Enter the team password to continue.</p>
        <label style={{ display: "block", fontSize: 13, fontWeight: 600, color: "var(--ink-2)", marginBottom: 7 }}>Team password</label>
        <input
          type="password"
          value={pw}
          autoFocus
          onChange={(e) => { setPw(e.target.value); setErr(false); }}
          style={{
            width: "100%", boxSizing: "border-box", padding: "11px 13px", fontSize: 14.5,
            fontFamily: "var(--font-body)", color: "var(--ink)", background: "var(--surface)",
            border: `1px solid ${err ? "var(--st-declined)" : "var(--line-2)"}`, borderRadius: "var(--r-md)", outline: "none",
          }}
        />
        {err && <div style={{ fontSize: 13, color: "var(--st-declined)", marginTop: 8 }}>That password didn't match. Try again.</div>}
        <button type="submit" disabled={busy || !pw}
          style={{
            width: "100%", marginTop: 18, padding: "12px 16px", fontSize: 15, fontWeight: 700,
            fontFamily: "var(--font-body)", color: "#fff", background: "var(--accent)",
            border: "none", borderRadius: "var(--r-md)", cursor: busy || !pw ? "default" : "pointer",
            opacity: busy || !pw ? 0.6 : 1,
          }}>
          {busy ? "Checking…" : "Continue"}
        </button>
      </form>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<Gate><App /></Gate>);
