// ============ TaskQuest — App root (Supabase-backed) ============
const { useState, useEffect, useRef } = React;
const { ROLES: RA, PROJECTS: PA, CONSULTANTS: CA, COLUMNS: COLA } = window.TQ;
const BE = window.TQ_BACKEND;

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "tileLayout": "comfortable",
  "dmLayout": "cards",
  "accent": "#5E6AD2"
}/*EDITMODE-END*/;

let _nid = 500, _bid = 50;
function genId(p) { _nid += 1; return p + _nid + Math.random().toString(36).slice(2, 5); }

function BootScreen({ label }) {  return (
    <div style={{ position: "fixed", inset: 0, display: "grid", placeItems: "center", background: "var(--bg, #0a0a0d)" }}>
      <div style={{ display: "flex", flexDirection: "column", alignItems: "center", gap: 16 }}>
        <div className="brand-mark" style={{ width: 44, height: 44 }}><Ic.Layers size={22} sw={2.2} /></div>
        <div style={{ color: "var(--muted)", fontSize: 14, fontWeight: 500 }}>{label || "Loading your workspace…"}</div>
      </div>
    </div>
  );
}

// Manager visibility: team members on the current project with no active tasks.
function UnassignedBanner({ project, tasks }) {
  const { ROLES, CONSULTANTS, projectsOf } = window.TQ;
  const inScope = CONSULTANTS.filter(c => {
    const pr = projectsOf(ROLES[c]);
    return project === "all" ? pr.length > 0 : pr.indexOf(project) >= 0;
  });
  const hasActive = id => tasks.some(t => t.assignee === id && t.status !== "done" && (project === "all" || t.project === project));
  const unassigned = inScope.filter(c => !hasActive(c));
  if (inScope.length === 0 || unassigned.length === 0) return null;
  return (
    <div className="unassigned-banner">
      <div className="ub-ic"><Ic.Users size={16} /></div>
      <div className="ub-body">
        <div className="ub-title">{unassigned.length} team member{unassigned.length > 1 ? "s have" : " has"} no active tasks</div>
        <div className="ub-people">
          {unassigned.map(c => (
            <span className="ub-chip" key={c}><Avatar who={c} size="xs" />{ROLES[c].short}</span>
          ))}
        </div>
      </div>
    </div>
  );
}

// Board employee filter bar (PM/DM) — compact searchable dropdown
function BoardFilterBar({ project, filterAssignee, setFilterAssignee }) {
  const { CONSULTANTS, projectsOf, ROLES } = window.TQ;
  const pool = CONSULTANTS.filter(c => {
    const pr = projectsOf(ROLES[c]);
    return project === "all" ? pr.length > 0 : pr.indexOf(project) >= 0;
  });
  if (pool.length < 2) return null;
  const ALL = "__all__";
  return (
    <div className="board-filter-bar">
      <span className="board-filter-label"><Ic.Users size={14} />Member</span>
      <div style={{ minWidth: 200, maxWidth: 280 }}>
        <SearchableSelect
          options={[ALL, ...pool]}
          value={filterAssignee || ALL}
          onChange={v => setFilterAssignee(v === ALL ? "" : v)}
          placeholder="All members"
          emptyText="No matching members"
          getLabel={c => c === ALL ? "All members" : (ROLES[c] && ROLES[c].name) || c}
          renderOption={c => c === ALL
            ? <span style={{ fontWeight: 600, color: "var(--text-2)" }}>All members</span>
            : (<span className="ssel-person"><Avatar who={c} size="sm" /><span className="ssel-person-name">{ROLES[c].name}</span><span className="ssel-person-role">{ROLES[c].label}</span></span>)
          }
        />
      </div>
    </div>
  );
}

function App({ authUser, onLogout, theme, setTheme }) {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [role, setRoleRaw] = useState(authUser);
  const [project, setProject] = useState(RA[authUser] && RA[authUser].type === "dm" ? "all" : "alpha");
  const [tasks, setTasks] = useState([]);
  const [bonus, setBonus] = useState([]);
  const [notifs, setNotifs] = useState([]);
  const [deliverables, setDeliverables] = useState([]);
  const [delivId, setDelivId] = useState(null);
  const [createDelivOpen, setCreateDelivOpen] = useState(false);
  const [createCtx, setCreateCtx] = useState(null); // {deliverable, project} when breaking a deliverable down
  const [dmTab, setDmTab] = useState(() => { try { return localStorage.getItem("tq_dm_tab") || "backlog"; } catch(e) { return "backlog"; } });
  const [filterAssignee, setFilterAssignee] = useState("");
  const [booted, setBooted] = useState(false);
  const [bootErr, setBootErr] = useState("");
  const [detailId, setDetailId] = useState(null);
  const [moveModal, setMoveModal] = useState(null);   // { kind, taskId }
  const [createOpen, setCreateOpen] = useState(false);
  const [bonusModal, setBonusModal] = useState(null); // { to, project }
  const [notifOpen, setNotifOpen] = useState(false);
  const [toasts, setToasts] = useState([]);
  const [viewKey, setViewKey] = useState(0);
  const [dataVersion, setDataVersion] = useState(0);
  const [view, setView] = useState("board");   // board | timesheet/timesheets | delivery

  // accent application (theme lives in Root)
  useEffect(() => {
    const root = document.documentElement.style;
    root.setProperty("--accent", t.accent);
    root.setProperty("--accent-hover", t.accent);
    root.setProperty("--accent-soft", t.accent + "26");
  }, [t.accent]);

  useEffect(() => { setViewKey(k => k + 1); }, [project]);

  // re-render when the timesheet store changes (badges, review queue, card logger)
  useEffect(() => window.TQ_TIME.subscribe(() => setDataVersion(v => v + 1)), []);

  // ---------- initial load + realtime ----------
  async function reloadTasks() { setTasks(await BE.loadTasks()); }
  async function reloadDeliverables() { setDeliverables(await BE.loadDeliverables()); }
  async function reloadBonus() { setBonus(await BE.loadBonus()); }
  async function reloadNotifs() { setNotifs(await BE.loadNotifications()); }
  async function reloadProfiles() { const m = await BE.loadProfiles(); if (m) { window.TQ.applyProfiles(m); setDataVersion(v => v + 1); } }
  async function reloadProjects() { const m = await BE.loadProjects(); if (m) { window.TQ.applyProjects(m); setDataVersion(v => v + 1); } }
  async function reloadConfig() { const c = await BE.loadConfig(); if (c) { window.TQ.applyConfig(c); setDataVersion(v => v + 1); } }

  useEffect(() => {
    let unsub = function () {};
    (async () => {
      try {
        const profiles = await BE.loadProfiles();
        if (profiles) window.TQ.applyProfiles(profiles);
        const projects = await BE.loadProjects();
        if (projects) window.TQ.applyProjects(projects);
        const cfg = await BE.loadConfig();
        if (cfg) window.TQ.applyConfig(cfg);

        const [tk, dl, bn, nt] = await Promise.all([BE.loadTasks(), BE.loadDeliverables(), BE.loadBonus(), BE.loadNotifications()]);
        setTasks(tk); setDeliverables(dl); setBonus(bn); setNotifs(nt);
        // Demo hours are seeded ONLY in offline preview — never against a live workspace.
        if (!BE.CONFIGURED) { try { window.TQ_TIME.ensureSeed(tk); } catch (e) {} }

        // pick a sensible default project for this user
        const me = RA[authUser];
        if (me && me.type === "dm") setProject("all");
        else if (!PA[project]) {
          const first = Object.keys(PA)[0];
          if (first) setProject(first);
        }

        setBooted(true);
        unsub = BE.subscribe({ reloadTasks, reloadDeliverables, reloadBonus, reloadNotifs, reloadProfiles, reloadProjects, reloadConfig });
      } catch (err) {
        console.error("[TaskQuest] load failed", err);
        setBootErr(err.message || "Could not load your workspace.");
        setBooted(true);
      }
    })();
    return () => { try { unsub(); } catch (e) {} };
  }, []);

  // ---------- persistence helper ----------
  function persist(fn) {
    Promise.resolve().then(fn).catch(err => {
      console.error("[TaskQuest] save failed", err);
      addToast("Save failed — change not stored", Ic.Alert, "#eb5757");
    });
  }

  function setRole(next) {
    setRoleRaw(next);
    setViewKey(k => k + 1);
    setView("board");
    const nr = RA[next];
    if (!nr) return;
    if (nr.type === "dm") setProject("all");
    else if (nr.type === "pm" || nr.type === "consultant") setProject(window.TQ.projectsOf(nr)[0] || Object.keys(PA)[0] || "alpha");
    else if (project === "all") setProject(Object.keys(PA)[0] || "alpha");
  }

  // ---------- gates ----------
  if (!booted) return <BootScreen />;
  if (bootErr) return <BootScreen label={bootErr} />;

  const r = RA[role] || RA[authUser];
  if (!r) return <BootScreen label="Your profile isn't set up yet. Ask an admin to add you." />;
  const isManager = r.type === "pm" || r.type === "dm";
  const canSwitchRole = !BE.CONFIGURED || r.type === "admin"; // prototype tool: locked in prod (admins may impersonate)

  const detailTask = detailId ? tasks.find(x => x.id === detailId) : null;
  const moveTask = moveModal ? tasks.find(x => x.id === moveModal.taskId) : null;
  const detailDeliv = delivId ? deliverables.find(x => x.id === delivId) : null;

  // ---------- helpers ----------
  function addToast(text, icon, color) {
    const id = genId("t");
    setToasts(ts => [...ts, { id, text, icon, color: color || "var(--accent)" }]);
    setTimeout(() => setToasts(ts => ts.filter(x => x.id !== id)), 3200);
  }
  function pushNotif(n) {
    const full = { id: genId("n"), read: false, time: "just now", ...n };
    setNotifs(ns => [full, ...ns]);
    persist(() => BE.insertNotif(full));
  }
  function updateTask(id, fn) {
    setTasks(ts => {
      const next = ts.map(x => x.id === id ? fn(x) : x);
      const changed = next.find(x => x.id === id);
      if (changed) persist(() => BE.saveTask(changed));
      return next;
    });
  }
  function addComment(id, author, text, system) {
    updateTask(id, x => ({ ...x, comments: [...(x.comments || []), { author, text, time: "just now", system: !!system }] }));
  }

  // ---------- move / drag ----------
  function onMove(task, toCol) {
    const kind = window.needsModal(task.status, toCol);
    if (!kind) {
      updateTask(task.id, x => ({ ...x, status: toCol }));
      const col = COLA.find(c => c.key === toCol);
      addComment(task.id, role, `Moved to ${col.name}.`, true);
      addToast(`Moved to ${col.name}`, Ic.Arrow, col.color);
      return;
    }
    setMoveModal({ kind, taskId: task.id });
  }

  function submitMove(data) {
    const task = moveTask;
    if (!task) return;
    if (moveModal.kind === "date") {
      updateTask(task.id, x => ({ ...x, status: "unhappy",
        request: { newDate: data.newDate, reason: data.reason || "—", comments: data.comments, status: "pending" } }));
      addComment(task.id, role, `Requested a date change to ${window.TQ.fmtDate(data.newDate)}.${data.comments ? " " + data.comments : ""}`);
      pushNotif({ type: "date_request", who: role, project: task.project, forRoles: ["pm", "dm"],
        text: `<b>${RA[task.assignee].name}</b> requested a date change on <b>${task.name}</b>` });
      addToast("Date change submitted for approval", Ic.Calendar, "#f2c94c");
    } else if (moveModal.kind === "hold") {
      updateTask(task.id, x => ({ ...x, status: "onhold",
        request: { reason: data.reason, comments: data.comments, status: "hold" } }));
      addComment(task.id, role, `Moved to On Hold — ${data.reason}.${data.comments ? " " + data.comments : ""}`);
      pushNotif({ type: "comment", who: role, project: task.project, forRoles: ["pm", "dm"],
        text: `<b>${RA[task.assignee].name}</b> put <b>${task.name}</b> on hold — ${data.reason}` });
      addToast("Task moved to On Hold", Ic.Pause, "#878b94");
    } else if (moveModal.kind === "done") {
      updateTask(task.id, x => ({ ...x, status: "done",
        completion: { onTime: data.onTime, status: "pending", comments: data.comments || "Marked complete." } }));
      addComment(task.id, role, `Marked done${data.onTime ? " on time" : " (late)"}.${data.comments ? " " + data.comments : ""}`);
      pushNotif({ type: "done", who: role, project: task.project, forRoles: ["pm", "dm"],
        text: `<b>${RA[task.assignee].name}</b> marked <b>${task.name}</b> as done — needs approval` });
      addToast(data.onTime ? "Completed on time! Pending sign-off" : "Marked done — pending sign-off",
        data.onTime ? Ic.Star : Ic.Check, data.onTime ? "#4cb782" : "#878b94");
    }
    setMoveModal(null);
  }

  // ---------- approvals ----------
  function approveDate(task, approvedDate) {
    updateTask(task.id, x => ({ ...x, due: approvedDate,
      request: { ...x.request, status: "approved", approvedDate } }));
    addComment(task.id, role, `Approved the date change. New due date: ${window.TQ.fmtDateLong(approvedDate)}. Move it back to Happy Path to resume.`, true);
    pushNotif({ type: "date_approved", who: role, project: task.project, forRoles: [task.assignee],
      text: `Your date change on <b>${task.name}</b> was approved — new date ${window.TQ.fmtDate(approvedDate)}` });
    addToast("Date change approved", Ic.CheckCircle, "#4cb782");
  }
  function rejectDate(task) {
    updateTask(task.id, x => ({ ...x, request: { ...x.request, status: "rejected" } }));
    addComment(task.id, role, `Rejected the date change. Please keep the original due date.`, true);
    pushNotif({ type: "rejected", who: role, project: task.project, forRoles: [task.assignee],
      text: `Your date change on <b>${task.name}</b> was rejected` });
    addToast("Date change rejected", Ic.X, "#eb5757");
  }
  function approveDone(task) {
    updateTask(task.id, x => ({ ...x, completion: { ...x.completion, status: "approved" } }));
    addComment(task.id, role, `Approved the completion.${task.completion.onTime ? " +10 points awarded." : ""}`, true);
    pushNotif({ type: "approved", who: role, project: task.project, forRoles: [task.assignee],
      text: `Your completion of <b>${task.name}</b> was approved${task.completion.onTime ? " — +10 points" : ""}` });
    addToast(task.completion.onTime ? "Completion approved · +10 points" : "Completion approved", Ic.CheckCircle, "#4cb782");
  }

  // ---------- delete task (manager removes a task that is no longer needed) ----------
  async function removeTask(id) {
    setTasks(ts => ts.filter(t => t.id !== id));
    try { await BE.deleteTask(id); } catch (err) { console.error(err); }
  }

  // ---------- reassign (manager moves a task to a different team member) ----------
  function reassignTask(task, newAssignee) {
    if (!newAssignee || newAssignee === task.assignee) return;
    const prev = task.assignee;
    updateTask(task.id, x => ({ ...x, assignee: newAssignee }));
    addComment(task.id, role, `Reassigned from ${RA[prev] ? RA[prev].name : prev} to ${RA[newAssignee] ? RA[newAssignee].name : newAssignee}.`, true);
    pushNotif({ type: "comment", who: role, project: task.project, forRoles: [newAssignee],
      text: `<b>${RA[role].name}</b> reassigned <b>${task.name}</b> to you` });
    addToast("Task reassigned", Ic.Users, "#5E6AD2");
  }

  // ---------- comment ----------
  function onComment(task, text) {
    addComment(task.id, role, text);
    if (r.type === "consultant") {
      pushNotif({ type: "comment", who: role, project: task.project, forRoles: ["pm", "dm"],
        text: `<b>${RA[role].name}</b> added a progress comment on <b>${task.name}</b>` });
    }
  }

  // ---------- create ----------
  function createTask(data) {
    const nt = window.TQ.mkTask({ name: data.name, project: data.project, assignee: data.assignee, due: data.due, status: "happy",
      deliverable: data.deliverable || null,
      description: data.description || "",
      subcategory: data.subcategory || null,
      comments: data.comments ? [{ author: role, text: data.comments, time: "just now" }] : [] });
    setTasks(ts => [nt, ...ts]);
    persist(() => BE.insertTask(nt));
    pushNotif({ type: "comment", who: role, project: data.project, forRoles: [data.assignee],
      text: `<b>${RA[role].name}</b> assigned you a new task: <b>${data.name}</b>` });
    if (nt.deliverable) {
      const dl = deliverables.find(d => d.id === nt.deliverable);
      addDeliverableComment(nt.deliverable, role, `Added task “${data.name}” → ${RA[data.assignee] ? RA[data.assignee].name : "the team"}.`, true);
      if (dl) pushNotif({ type: "comment", who: role, project: dl.project, forRoles: [dl.createdBy || "dm", "dm"],
        text: `<b>${RA[role].name}</b> broke down <b>${dl.name}</b> into a new task` });
    }
    addToast("Task created", Ic.Plus, "#5E6AD2");
    setCreateOpen(false);
    setCreateCtx(null);
  }

  // ---------- deliverables (DM creates & tracks; PM breaks down) ----------
  function addDeliverableComment(id, author, text, system) {
    setDeliverables(ds => {
      const next = ds.map(x => x.id === id ? { ...x, comments: [...(x.comments || []), { author, text, time: "just now", system: !!system }] } : x);
      const ch = next.find(x => x.id === id);
      if (ch) persist(() => BE.saveDeliverable(ch));
      return next;
    });
  }
  function onDeliverableComment(d, text) { addDeliverableComment(d.id, role, text); }
  function createDeliverable(data) {
    const nd = window.TQ.mkDeliverable({ name: data.name, project: data.project, pm: data.pm, due: data.due,
      priority: data.priority, desc: data.desc, createdBy: role });
    setDeliverables(ds => [nd, ...ds]);
    persist(() => BE.insertDeliverable(nd));
    pushNotif({ type: "comment", who: role, project: data.project, forRoles: [data.pm],
      text: `<b>${RA[role].name}</b> assigned you a deliverable: <b>${data.name}</b>` });
    addToast("Deliverable created", Ic.Layers, "var(--accent)");
    setCreateDelivOpen(false);
  }
  function acceptDeliverable(d) {
    setDeliverables(ds => {
      const next = ds.map(x => x.id === d.id ? { ...x, accepted: true } : x);
      const ch = next.find(x => x.id === d.id);
      if (ch) persist(() => BE.saveDeliverable(ch));
      return next;
    });
    addDeliverableComment(d.id, role, "Accepted the delivery — signed off and closed.", true);
    pushNotif({ type: "approved", who: role, project: d.project, forRoles: [d.pm],
      text: `<b>${RA[role].name}</b> accepted your delivery of <b>${d.name}</b>` });
    addToast("Delivery accepted", Ic.CheckCircle, "#8b7cf6");
  }
  function openCreateForDeliverable(d) {
    setCreateCtx({ deliverable: d.id, project: d.project });
    setCreateOpen(true);
  }

  // ---------- bonus ----------
  function awardBonus(data) {
    const b = { id: genId("b"), to: data.to, pts: data.pts, by: role, comment: data.comment, project: data.project, time: "just now" };
    setBonus(bs => [b, ...bs]);
    persist(() => BE.insertBonus(b));
    pushNotif({ type: "bonus", who: role, project: data.project, forRoles: [data.to],
      text: `<b>${RA[role].name}</b> awarded you <b>+${data.pts} points</b> — \u201c${data.comment}\u201d` });
    addToast(`Awarded +${data.pts} to ${RA[data.to].short}`, Ic.Sparkle, "#e0902b");
    setBonusModal(null);
  }

  // ---------- notifications ----------
  const visibleNotifs = notifs.filter(n => window.TQ.notifVisible(n, role, r.type));
  function markAll() {
    const ids = visibleNotifs.filter(n => !n.read).map(n => n.id);
    setNotifs(ns => ns.map(n => window.TQ.notifVisible(n, role, r.type) ? { ...n, read: true } : n));
    persist(() => BE.markNotifsRead(ids));
  }
  function clickNotif(n) {
    setNotifs(ns => ns.map(x => x.id === n.id ? { ...x, read: true } : x));
    persist(() => BE.markNotifsRead([n.id]));
    setNotifOpen(false);
  }

  // ---------- admin: users, projects, points config ----------
  async function createUser(data) {
    try {
      await BE.provisionUser(data);
      await reloadProfiles();
      setDataVersion(v => v + 1);
      addToast(`Login created for ${data.name}`, Ic.Users, "#5E6AD2");
      return { ok: true };
    } catch (err) {
      console.error(err);
      addToast("Could not create the account", Ic.Alert, "#eb5757");
      return { ok: false, message: (err && err.message) || "Could not create the account." };
    }
  }
  async function createProject(data) {
    try {
      const key = await BE.insertProject(data);
      await reloadProjects();
      setDataVersion(v => v + 1);
      addToast(`Created ${(PA[key] && PA[key].name) || data.name}`, Ic.Folder, "#16a394");
    } catch (err) { console.error(err); addToast("Could not create project", Ic.Alert, "#eb5757"); }
  }
  async function createProjects(list) {
    try {
      const keys = await BE.insertProjects(list);
      await reloadProjects();
      setDataVersion(v => v + 1);
      const n = (keys && keys.length) || list.length;
      addToast(`Created ${n} project${n === 1 ? "" : "s"}`, Ic.Folder, "#16a394");
      return { ok: true };
    } catch (err) {
      console.error(err);
      addToast("Could not create projects", Ic.Alert, "#eb5757");
      return { ok: false, message: (err && err.message) || "Could not create the projects." };
    }
  }
  async function editProject(key, data) {
    try {
      await BE.updateProject(key, data);
      await reloadProjects();
      setDataVersion(v => v + 1);
      addToast("Project updated", Ic.Folder, "#16a394");
      return { ok: true };
    } catch (err) {
      console.error(err);
      addToast("Could not update project", Ic.Alert, "#eb5757");
      return { ok: false, message: (err && err.message) || "Could not update the project." };
    }
  }
  async function removeProject(key) {
    try {
      await BE.deleteProject(key);
      await reloadProjects();
      await reloadTasks();
      setDataVersion(v => v + 1);
      addToast("Project removed", Ic.Check, "#16a394");
      return { ok: true };
    } catch (err) {
      console.error(err);
      addToast("Could not remove project", Ic.Alert, "#eb5757");
      return { ok: false, message: (err && err.message) || "Could not remove the project." };
    }
  }
  async function updateUser(data) {
    try {
      await BE.updateProfile(data.id, { name: data.name, type: data.type, username: data.username, projects: data.projects });
      if (data.password) await BE.resetPassword(data.id, data.password);
      await reloadProfiles();
      setDataVersion(v => v + 1);
      addToast(`Updated ${data.name}`, Ic.Check, "#16a394");
      return { ok: true };
    } catch (err) {
      console.error(err);
      addToast("Could not update the profile", Ic.Alert, "#eb5757");
      return { ok: false, message: (err && err.message) || "Could not update the profile." };
    }
  }
  async function deleteUser(id) {
    try {
      await BE.deleteUser(id);
      await reloadProfiles();
      await reloadTasks();
      setDataVersion(v => v + 1);
      addToast("User removed", Ic.Check, "#16a394");
      return { ok: true };
    } catch (err) {
      console.error(err);
      addToast("Could not remove the user", Ic.Alert, "#eb5757");
      return { ok: false, message: (err && err.message) || "Could not remove the user." };
    }
  }
  function saveConfig(cfg) {
    window.TQ.applyConfig(cfg);
    setDataVersion(v => v + 1);
    persist(() => BE.saveConfig(cfg));
    addToast("Points system updated", Ic.Target, "#e0902b");
  }

  // page heading
  const heading = r.type === "consultant" ? "My Board"
    : r.type === "pm" ? "Project Board"
    : r.type === "admin" ? "System Administration" : "Delivery Backlog";
  const subline = r.type === "consultant" ? "Your assigned tasks, points and rank"
    : r.type === "pm" ? "Break down deliverables, track the team and approve work"
    : r.type === "admin" ? "Manage members, projects and the points system"
    : "Assign deliverables to project managers and track delivery";

  const tileLayout = t.tileLayout === "comfortable" ? "" : t.tileLayout;

  // ---------- view tabs (Board / Timesheet) ----------
  const tsWeek = window.TQ_TIME.thisWeekStart();
  let tsBadge = 0;
  if (isManager) {
    const scopeCons = CA.filter(c => (r.type === "dm" && project === "all") ? true : window.TQ.projectsOf(RA[c]).indexOf(project) >= 0);
    tsBadge = scopeCons.filter(c => window.TQ_TIME.statusOf(c, tsWeek).status === "submitted").length;
  }
  const tabs = r.type === "consultant"
    ? [{ key: "board", label: "My Board", icon: Ic.Layers }, { key: "timesheet", label: "Timesheet", icon: Ic.Clock }]
    : r.type === "pm"
    ? [{ key: "board", label: "Project Board", icon: Ic.Layers }, { key: "timesheets", label: "Timesheets", icon: Ic.Clock, count: tsBadge }]
    : r.type === "dm"
    ? [{ key: "delivery", label: "Delivery", icon: Ic.Target }, { key: "timesheets", label: "Timesheets", icon: Ic.Clock, count: tsBadge }]
    : null;
  const curView = (tabs && tabs.some(x => x.key === view)) ? view : (tabs ? tabs[0].key : "board");
  const onTsView = curView === "timesheet" || curView === "timesheets";

  return (
    <>
      <Header role={role} setRole={setRole} project={project} setProject={setProject}
        theme={theme} toggleTheme={() => setTheme(th => th === "dark" ? "light" : "dark")}
        authUser={authUser} onLogout={onLogout} canSwitchRole={canSwitchRole}
        notifications={visibleNotifs}
        onOpenNotif={() => setNotifOpen(o => !o)} notifOpen={notifOpen}
        notifPanel={<NotificationsPanel notifications={visibleNotifs} onClose={() => setNotifOpen(false)} onMarkAll={markAll} onClickNotif={clickNotif} />} />

      <div className="app-body">
        <div className="scroll-area">
          <div className="content" key={viewKey}>
            <div className="view-fade">
              <div className="page-head">
                <div>
                  <div className="page-title">{heading}</div>
                  <div className="page-sub">{subline}</div>
                </div>
                <div style={{ display: "flex", alignItems: "center", gap: 12 }}>
                  {r.type === "dm" && !onTsView && (
                    <div className="view-toggle dm-tabs-inline">
                      <button className={dmTab === "backlog" ? "on" : ""} onClick={() => { setDmTab("backlog"); try { localStorage.setItem("tq_dm_tab", "backlog"); } catch(e) {} }}>
                        <Ic.Layers size={14} />Delivery Backlog
                      </button>
                      <button className={dmTab === "dashboard" ? "on" : ""} onClick={() => { setDmTab("dashboard"); try { localStorage.setItem("tq_dm_tab", "dashboard"); } catch(e) {} }}>
                        <Ic.Target size={14} />Team Dashboard
                      </button>
                    </div>
                  )}
                  {tabs && (
                    <div className="tabs-bar">
                      {tabs.map(tb => (
                        <button key={tb.key} className={curView === tb.key ? "on" : ""} onClick={() => setView(tb.key)}>
                          {React.createElement(tb.icon, { size: 15 })}{tb.label}
                          {tb.count > 0 && <span className="tab-count">{tb.count}</span>}
                        </button>
                      ))}
                    </div>
                  )}
                </div>
              </div>

              {r.type === "admin" ? (
                <AdminPanel meId={authUser} onCreateUser={createUser} onUpdateUser={updateUser} onDeleteUser={deleteUser} onCreateProject={createProject} onCreateProjects={createProjects} onEditProject={editProject} onDeleteProject={removeProject} onSaveConfig={saveConfig} />
              ) : r.type === "dm" ? (
                onTsView ? (
                  <ManagerTimesheets role={role} project={project} tasks={tasks} addToast={addToast} pushNotif={pushNotif} />
                ) : (
                  <DmDashboard tab={dmTab} project={project} deliverables={deliverables} tasks={tasks} dmLayout={t.dmLayout}
                    bonus={bonus} onOpen={(d) => setDelivId(d.id)} />
                )
              ) : onTsView ? (
                r.type === "consultant" ? (
                  <ConsultantTimesheet role={role} tasks={tasks} addToast={addToast} pushNotif={pushNotif} />
                ) : (
                  <ManagerTimesheets role={role} project={project} tasks={tasks} addToast={addToast} pushNotif={pushNotif} />
                )
              ) : (
                <>
                  <SummarySection role={role} project={project} tasks={tasks} bonus={bonus} />

                  {r.type === "pm" && (
                    <PmDeliverablesStrip role={role} project={project} deliverables={deliverables} tasks={tasks}
                      onOpen={(d) => setDelivId(d.id)} onAddTask={openCreateForDeliverable} />
                  )}

                  {r.type === "pm" && <UnassignedBanner project={project} tasks={tasks} />}

                  {/* Employee filter — PM/DM only */}
                  {(r.type === "pm" || r.type === "dm") && (
                    <BoardFilterBar project={project} tasks={tasks} filterAssignee={filterAssignee} setFilterAssignee={setFilterAssignee} />
                  )}

                  <KanbanBoard role={role} project={project} tasks={tasks} layout={tileLayout} deliverables={deliverables}
                    filterAssignee={filterAssignee}
                    onTileClick={(task) => setDetailId(task.id)} onMove={onMove} />
                </>
              )}
            </div>
          </div>
        </div>
      </div>

      {/* FAB — DM creates deliverables; PM creates tasks */}
      {r.type === "dm" && !onTsView && (
        <button className="fab" onClick={() => setCreateDelivOpen(true)}><Ic.Plus size={18} sw={2.4} />New deliverable</button>
      )}
      {r.type === "pm" && !onTsView && (
        <button className="fab" onClick={() => { setCreateCtx(null); setCreateOpen(true); }}><Ic.Plus size={18} sw={2.4} />New task</button>
      )}

      {/* Modals */}
      {detailTask && (
        <TaskDetailModal task={detailTask} role={role} onClose={() => setDetailId(null)}
          onComment={onComment} onApproveDate={approveDate} onRejectDate={rejectDate} onApproveDone={approveDone}
          onReassign={reassignTask}
          onDelete={(id) => { removeTask(id); setDetailId(null); addToast("Task deleted", Ic.Trash, "var(--red, #e05252)"); }}
          onEdit={(id, fields) => { updateTask(id, t => ({ ...t, ...fields })); addToast("Task updated", Ic.Check, "#4cb782"); }}
          onAwardBonus={(to, proj) => { setDetailId(null); setBonusModal({ to, project: proj }); }} />
      )}
      {moveModal && moveTask && (
        <MoveModal kind={moveModal.kind} task={moveTask} onClose={() => setMoveModal(null)} onSubmit={submitMove} />
      )}
      {createOpen && (
        <CreateTaskModal role={role} project={createCtx ? createCtx.project : project}
          deliverables={deliverables.filter(d => d.pm === role)}
          presetDeliverable={createCtx ? createCtx.deliverable : null}
          onClose={() => { setCreateOpen(false); setCreateCtx(null); }} onCreate={createTask} />
      )}
      {createDelivOpen && (
        <CreateDeliverableModal onClose={() => setCreateDelivOpen(false)} onCreate={createDeliverable} />
      )}
      {detailDeliv && (
        <DeliverableDetailModal d={detailDeliv} role={role} tasks={tasks}
          onClose={() => setDelivId(null)} onComment={onDeliverableComment} onAccept={(d) => { acceptDeliverable(d); }}
          onAddTask={(d) => { setDelivId(null); openCreateForDeliverable(d); }}
          onOpenTask={(tk) => { setDelivId(null); setDetailId(tk.id); }} />
      )}
      {bonusModal && (
        <BonusModal toKey={bonusModal.to} project={bonusModal.project} onClose={() => setBonusModal(null)} onAward={awardBonus} />
      )}

      <ToastHost toasts={toasts} />

      <TweaksPanel>
        <TweakSection label="Delivery backlog" />
        <TweakRadio label="List layout" value={t.dmLayout}
          options={[{ value: "cards", label: "Cards" }, { value: "table", label: "Table" }]}
          onChange={(v) => setTweak("dmLayout", v)} />
        <TweakSection label="Task tiles" />
        <TweakRadio label="Layout" value={t.tileLayout}
          options={[{ value: "comfortable", label: "Comfortable" }, { value: "compact", label: "Compact" }, { value: "bar", label: "Color bar" }]}
          onChange={(v) => setTweak("tileLayout", v)} />
        <TweakSection label="Theme" />
        <TweakColor label="Accent" value={t.accent}
          options={["#5E6AD2", "#3E63DD", "#7C3AED", "#0D9488"]}
          onChange={(v) => setTweak("accent", v)} />
        <TweakRadio label="Mode" value={theme} options={["dark", "light"]} onChange={setTheme} />
      </TweaksPanel>
    </>
  );
}

function RecognitionActivity({ bonus, project, onAward }) {
  const scoped = project === "all" ? bonus : bonus.filter(b => b.project === project);
  return (
    <div style={{ marginTop: 26 }}>
      <div style={{ display: "flex", alignItems: "center", marginBottom: 12 }}>
        <div className="section-label" style={{ margin: 0 }}>Recognition activity</div>
        <button className="btn btn-ghost" style={{ marginLeft: "auto", height: 30 }} onClick={onAward}><Ic.Gift size={14} style={{ color: "var(--amber)" }} />Award points</button>
      </div>
      <div className="activity-card">
        {scoped.length === 0 && <div className="comment-empty">No recognition yet — award the first bonus.</div>}
        {scoped.map((b, i) => (
          <div className="activity-row" key={b.id}>
            <span className="stat-ic" style={{ background: "#e0902b22", color: "#e0902b", width: 34, height: 34 }}><Ic.Sparkle size={16} /></span>
            <Avatar who={b.to} size="sm" />
            <div className="activity-text">
              <b>{RA[b.to] ? RA[b.to].name : "—"}</b> <span style={{ color: "var(--muted)" }}>· {b.comment}</span>
              <div className="activity-time">{b.time}{b.project && PA[b.project] ? " · " + PA[b.project].name : ""} · by {RA[b.by] ? RA[b.by].short : "—"}</div>
            </div>
            <span className="activity-pts">+{b.pts}</span>
          </div>
        ))}
      </div>
    </div>
  );
}

function Root() {
  const [theme, setTheme] = useState(() => localStorage.getItem("tq_theme") || "dark");
  const [session, setSession] = useState(undefined); // undefined = checking, null = logged out

  useEffect(() => { document.documentElement.setAttribute("data-theme", theme); }, [theme]);
  useEffect(() => { localStorage.setItem("tq_theme", theme); }, [theme]);

  useEffect(() => {
    let alive = true;
    BE.getSession().then(s => { if (alive) setSession(s); });
    const unsub = BE.onAuthChange(s => { if (alive) setSession(s); });
    return () => { alive = false; try { unsub(); } catch (e) {} };
  }, []);

  async function login(role) {
    const s = role ? { id: role } : await BE.getSession();
    setSession(s);
  }
  async function logout() {
    try { await BE.signOut(); } catch (e) {}
    setSession(null);
  }

  if (session === undefined) return <BootScreen label="Starting TaskQuest…" />;
  if (!session) return <LoginPage theme={theme} toggleTheme={() => setTheme(th => th === "dark" ? "light" : "dark")} onLogin={login} />;
  return <App key={session.id} authUser={session.id} onLogout={logout} theme={theme} setTheme={setTheme} />;
}

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