/* ============================================================
   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 menu ---- */
function AccountMenu({ user, 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={{ display: "flex", alignItems: "center", gap: 11, padding: "10px 10px 12px" }}>
            <Avatar person={user} size={38} />
            <div style={{ minWidth: 0 }}>
              <div style={{ fontWeight: 600, fontSize: 14, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{user.first} {user.last}</div>
              <div style={{ fontSize: 12, color: "var(--ink-3)", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{user.email}</div>
            </div>
          </div>
          <div style={{ borderTop: "1px solid var(--line)", 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>
  );
}

/* ---- Full-screen splash (loading / error) ---- */
function Splash({ children }) {
  return (
    <div style={{ minHeight: "100%", display: "grid", placeItems: "center", padding: "40px 24px" }}>
      <div className="vp-fade" style={{ textAlign: "center", maxWidth: 360 }}>
        <div style={{ display: "inline-flex", alignItems: "center", marginBottom: 22 }}>
          <BrandLockup height={30} sub="" />
        </div>
        {children}
      </div>
    </div>
  );
}

/* ---- Sidebar ---- */
function Sidebar({ user, view, onGoto, pendingCount, narrow, onHome }) {
  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" },
    { id: "activity", label: "Activity log", icon: "clock" },
    ...(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,
    }}>
      <button type="button" onClick={onHome} className="vp-focusable" title="All tools"
        style={{ display: "flex", alignItems: "center", gap: 6, padding: narrow ? "0 6px 0 2px" : "0 8px", flex: "0 0 auto", background: "transparent", border: "none", cursor: "pointer" }}>
        {narrow ? <BrandMark size={30} /> : <BrandLockup height={22} sub="Leave" />}
      </button>
      <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>
  );
}

/* ============================================================
   Leave tool — the original planner, now mounted under /leave.
   Auth + the directory live in Root; this receives the signed-in user.
   ============================================================ */
function LeaveApp({ user, onHome, onUserUpdate }) {
  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'|'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);
  }

  async function addRequest(req) {
    try {
      // A CEO is the approver — their entries (own or on behalf) are auto-approved.
      const ceoEntry = user.role === "ceo";
      const toInsert = ceoEntry ? { ...req, status: "approved", decidedBy: user.id } : req;
      const saved = await MesperDB.insertRequest(toInsert);
      setRequests((rs) => [saved, ...rs]);
      VP_DATA.requests = [saved, ...VP_DATA.requests];
      setModal(null);
      flash(ceoEntry ? "Logged and approved." : "Request submitted.", ceoEntry ? { tone: "approved", icon: "check" } : {});
    } catch (e) {
      flash((e && e.message) || "Could not submit your request.", { tone: "declined", icon: "x" });
    }
  }
  async function decide(id, status, by, reason) {
    try {
      const saved = await MesperDB.decideRequest(id, status, by, reason);
      setRequests((rs) => rs.map((r) => (r.id === id ? saved : r)));
      VP_DATA.requests = VP_DATA.requests.map((r) => (r.id === id ? saved : r));
      setModal(null);
      flash(status === "approved" ? "Request approved." : "Request declined.", { tone: status, icon: status === "approved" ? "check" : "x" });
    } catch (e) {
      flash((e && e.message) || "Could not save the decision.", { tone: "declined", icon: "x" });
    }
  }
  function goto(v) { setView(v); }

  async function savePerson(p) {
    try {
      const saved = await MesperDB.saveProfile(p);
      const i = VP_DATA.people.findIndex((x) => x.id === saved.id);
      if (i >= 0) VP_DATA.people[i] = saved; else VP_DATA.people.push(saved);
      VP_DATA.byId[saved.id] = saved;
      VP_DATA.ceos = VP_DATA.people.filter((x) => x.role === "ceo");
      if (user && saved.id === user.id && onUserUpdate) onUserUpdate(saved);
      setPeopleVersion((v) => v + 1);
      setModal(null);
      flash("Changes saved.");
    } catch (e) {
      flash((e && e.message) || "Could not save changes.", { tone: "declined", icon: "x" });
    }
  }
  async function archivePerson(p) {
    try {
      await MesperDB.removeProfile(p.id);
      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" });
    } catch (e) {
      flash((e && e.message) || "Could not remove access.", { tone: "declined", icon: "x" });
    }
  }

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

  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} onHome={onHome} />
      <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} onLogout={() => MesperDB.signOut()} />
        </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 === "activity" && <ActivityLog user={user} />}
          {view === "team" && user.role === "ceo" && <TeamAdmin user={user} requests={requests} 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 === "editPerson" && <Modal onClose={() => setModal(null)} width={600}><PersonForm person={modal.person} onSave={savePerson} onArchive={archivePerson} onClose={() => setModal(null)} /></Modal>}

      <Toast toast={toast} />
    </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>
  );
}

/* ---- Tiny path router (no build step, SPA via vercel rewrites) ---- */
function useRoute() {
  const [path, setPath] = useState(window.location.pathname);
  useEffect(() => {
    const onPop = () => setPath(window.location.pathname);
    window.addEventListener("popstate", onPop);
    return () => window.removeEventListener("popstate", onPop);
  }, []);
  const navigate = (to) => {
    if (to !== window.location.pathname) { window.history.pushState({}, "", to); setPath(to); }
    window.scrollTo(0, 0);
  };
  return [path, navigate];
}
const toolForPath = (p) => p.startsWith("/leave") ? "leave" : (p.startsWith("/approvals") || p.startsWith("/tasks")) ? "tasks" : "hub";

/* ---- Tasks placeholder until the task tracker ships ---- */
function TasksPlaceholder({ user, onHome }) {
  return (
    <Splash>
      <h2 style={{ fontSize: 22, marginBottom: 6 }}>Tasks</h2>
      <p style={{ color: "var(--ink-3)", fontSize: 14.5, marginBottom: 18, lineHeight: 1.5 }}>
        The board, list and timeline are on the way. Hang tight.
      </p>
      <Button variant="outline" icon="arrowLeft" onClick={onHome}>Back to tools</Button>
    </Splash>
  );
}

/* ============================================================
   Root — shared auth, tweaks and routing across all internal tools.
   The directory loads once; each tool mounts under its own path.
   ============================================================ */
function Root() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  useEffect(() => { applyTokens(t); }, [t]);

  const [phase, setPhase] = useState("loading"); // loading | anon | ready | error
  const [loadErr, setLoadErr] = useState("");
  const [user, setUser] = useState(null);
  const [path, navigate] = useRoute();
  const loadedFor = useRef(null);

  useEffect(() => {
    let active = true;

    async function bootstrap(session) {
      if (!session) { loadedFor.current = null; if (active) { setUser(null); setPhase("anon"); } return; }
      if (loadedFor.current === session.user.id) return;
      try {
        await MesperDB.loadDirectory();
        let me = VP_DATA.byId[session.user.id];
        if (!me) {
          await new Promise((r) => setTimeout(r, 900));
          await MesperDB.loadDirectory();
          me = VP_DATA.byId[session.user.id];
        }
        if (!active) return;
        if (!me) { setLoadErr("Your profile is still being set up. Reload in a moment."); setPhase("error"); return; }
        loadedFor.current = session.user.id;
        setUser(me);
        setPhase("ready");
      } catch (e) {
        if (!active) return;
        setLoadErr((e && e.message) || "Could not load your team data.");
        setPhase("error");
      }
    }

    MesperDB.getSession().then(({ data }) => bootstrap(data.session));
    const { data: sub } = MesperDB.onAuthStateChange((evt, session) => {
      if (evt === "SIGNED_OUT") { loadedFor.current = null; setUser(null); setPhase("anon"); }
      else if (session) bootstrap(session);
    });
    return () => { active = false; sub.subscription.unsubscribe(); };
  }, []);

  if (phase === "loading") {
    return (<><Splash><p style={{ color: "var(--ink-3)", fontSize: 14.5 }}>Loading…</p></Splash><TweaksHost t={t} setTweak={setTweak} /></>);
  }
  if (phase === "error") {
    return (<><Splash>
      <p style={{ color: "var(--st-declined)", fontSize: 14.5, marginBottom: 16, lineHeight: 1.5 }}>{loadErr}</p>
      <Button onClick={() => window.location.reload()}>Reload</Button>
    </Splash><TweaksHost t={t} setTweak={setTweak} /></>);
  }
  if (phase === "anon" || !user) return (<><LoginScreen /><TweaksHost t={t} setTweak={setTweak} /></>);

  const tool = toolForPath(path);
  return (
    <>
      {tool === "hub" && <Hub user={user} onOpen={navigate} onLogout={() => MesperDB.signOut()} />}
      {tool === "leave" && <LeaveApp user={user} onHome={() => navigate("/")} onUserUpdate={setUser} />}
      {tool === "tasks" && (typeof TasksApp === "function"
        ? <TasksApp user={user} onHome={() => navigate("/")} />
        : <TasksPlaceholder user={user} onHome={() => navigate("/")} />)}
      <TweaksHost t={t} setTweak={setTweak} />
    </>
  );
}

/* Access is gated by Supabase Auth: tools only render once a valid @mesper.de
   Google session exists, and Row-Level Security enforces the same rules
   server-side. One login is shared across every tool on this origin. */
ReactDOM.createRoot(document.getElementById("root")).render(<Root />);
