/* ════════════════════════════════════════════════════════════════════════
   MSINDAHA Operations — TASKS & NOTES
   Personal to-dos and delegated work with a real workflow
   (pending → accepted → in progress → completed, or declined/cancelled),
   per-person progress for multi-assignee tasks, a detail drawer with
   history and comments, plus quick private/shared notes.
════════════════════════════════════════════════════════════════════════ */

const TASK_PAGE = 40;
const TASK_PREFS_DEFAULTS = { view: "mine", status: "active", priority: "all", person: "all", period: EMPTY_PERIOD, sort: "due" };
const TASK_PREFS_KEY = "msindaha_task_prefs_v2";
const TASK_STATUS_VERBS = {
  pending: "reopened",
  accepted: "accepted",
  "in-progress": "started",
  completed: "completed",
  rejected: "declined",
  cancelled: "cancelled",
};

function taskStatusPatch(t, next, me) {
  const now = nowISO();
  const patch = {
    status: next,
    done: next === "completed",
    updatedAt: now,
    completedAt: next === "completed" ? now : null,
  };
  if (isSharedAssignment(assignmentOf(t))) {
    if (next === "completed" && !t.completion?.completedBy) patch.completion = { completedBy: authorOf(me), completedAt: now };
    else if (next !== "completed" && t.completion?.completedBy) patch.completion = { completedBy: null, completedAt: null };
  }
  return patch;
}
function allowedTaskStatuses(t, perms) {
  if (!perms.status) return [];
  if (perms.sharedDone && !perms.manage) return ["completed"];
  if (perms.manage) return TASK_STATUSES;
  // Assignees run the workflow; cancelling is the owner's decision.
  return TASK_STATUSES.filter((s) => s !== "cancelled");
}
function taskNextSteps(t, perms, allowed) {
  const s = taskStatusOf(t);
  const can = (x) => allowed.includes(x) && x !== s;
  const steps = [];
  if (perms.shared && !perms.sharedDone && can("completed"))
    return [{ next: "completed", label: "Complete for the team", icon: IC.CheckCircle, cls: "btn-success" }];
  // Several people: each member closes their own part (progress card);
  // only the owner/manager moves the task as a whole.
  if (assignmentOf(t)?.mode === "multiple") {
    if (!perms.manage) return [];
    if (s === "completed") return can("pending") ? [{ next: "pending", label: "Reopen", icon: IC.Reopen, cls: "btn" }] : [];
    return can("completed") ? [{ next: "completed", label: "Complete the whole task", icon: IC.CheckCircle, cls: "btn" }] : [];
  }
  if (s === "pending") {
    if (perms.individualAssignee && can("accepted")) steps.push({ next: "accepted", label: "Accept", icon: IC.Check, cls: "btn-primary" });
    else if (can("in-progress")) steps.push({ next: "in-progress", label: "Start", icon: IC.Play, cls: "btn-primary" });
    if (can("completed")) steps.push({ next: "completed", label: "Complete", icon: IC.CheckCircle, cls: "btn" });
    if (perms.individualAssignee && can("rejected")) steps.push({ next: "rejected", label: "Decline", icon: IC.Ban, cls: "btn-ghost" });
  } else if (s === "accepted") {
    if (can("in-progress")) steps.push({ next: "in-progress", label: "Start", icon: IC.Play, cls: "btn-primary" });
    if (can("completed")) steps.push({ next: "completed", label: "Complete", icon: IC.CheckCircle, cls: "btn" });
  } else if (s === "in-progress") {
    if (can("completed")) steps.push({ next: "completed", label: "Complete", icon: IC.CheckCircle, cls: "btn-success" });
  } else if (can("pending")) {
    steps.push({ next: "pending", label: "Reopen", icon: IC.Reopen, cls: "btn" });
  }
  return steps;
}

function useTaskActions() {
  const app = useApp();
  const toast = useToast();
  const confirm = useConfirm();
  const { me, users, tasks, canEdit } = app;
  const { arrayUnion } = window.__fb;
  const fail = (title, e) => {
    console.error(title, e);
    toast({ type: "error", title, msg: writeErrorMsg(e), duration: 8000 });
  };
  const base = (t) => ({ collName: "tasks", itemId: t._id, displayId: t.displayId });
  const personalOf = (a) => !hasTarget(a) || (a.mode === "individual" && a.uids[0] === me.uid);

  const create = async (form) => {
    const assignment = normalizeAssignment(form.assignment);
    const personal = personalOf(assignment);
    const shared = !personal && isSharedAssignment(assignment);
    const visibility = personal ? (canEdit ? form.visibility : "private") : "shared";
    const displayId = await nextDisplayId("TASK", tasks.rows);
    const now = nowISO();
    const data = plain({
      displayId,
      title: form.title.trim(),
      description: form.description.trim(),
      dueDate: form.dueDate || null,
      priority: form.priority,
      done: false,
      status: "pending",
      visibility,
      assignment: personal ? null : assignment,
      assigneeProgress:
        !personal && assignment.mode === "multiple"
          ? Object.fromEntries(assignment.uids.map((u) => [u, { status: "pending", completedAt: null }]))
          : null,
      completion: shared ? { completedBy: null, completedAt: null } : null,
      departments: itemDepartments(personal ? null : assignment),
      ownerUid: me.uid,
      createdAt: now,
      updatedAt: now,
      createdBy: authorOf(me),
      comments: [],
      ...workspaceFields(),
    });
    const ref = await tasks.add(data);
    const t = { ...data, _id: ref.id };
    if (!personal) {
      const people = resolveAssignees(assignment, users).map((p) => p.uid);
      if (people.length)
        notifyUsers(people, { ...base(t), type: "assigned", title: `New task from ${me.name}`, msg: `${displayId} · ${data.title}` }, me);
      if (shared)
        notifyUsers(
          deptMemberUids(users, assignment.departments),
          { ...base(t), type: "assigned", title: `New team task ${displayId}`, msg: data.title },
          me,
        );
    }
    return t;
  };

  const saveEdits = async (t, form) => {
    const patch = {};
    const events = [];
    const title = form.title.trim();
    const description = form.description.trim();
    if (title !== (t.title || "")) {
      patch.title = title;
      events.push(makeEvent(me, "edit", `renamed this to “${title}”`));
    }
    if (description !== (t.description || "")) {
      patch.description = description;
      events.push(makeEvent(me, "edit", "updated the description"));
    }
    if (form.priority !== (t.priority || "medium")) {
      patch.priority = form.priority;
      events.push(makeEvent(me, "priority", `changed the priority from ${priorityLabel(t.priority)} to ${priorityLabel(form.priority)}`));
    }
    if ((form.dueDate || null) !== (t.dueDate || null)) {
      patch.dueDate = form.dueDate || null;
      events.push(makeEvent(me, "due", form.dueDate ? `set the due date to ${fmtDate(form.dueDate)}` : "removed the due date"));
    }
    let newly = [];
    let becameShared = null;
    if (canEdit) {
      const next = normalizeAssignment(form.assignment);
      const personal = personalOf(next);
      const prevA = t.assignment ? assignmentOf(t) : null;
      const nextA = personal ? null : next;
      const same =
        (!prevA && !nextA) ||
        (prevA && nextA && prevA.mode === nextA.mode && sameSet(prevA.uids, nextA.uids) && sameSet(prevA.departments, nextA.departments));
      if (!same) {
        patch.assignment = nextA;
        const depts = itemDepartments(nextA);
        if (!sameSet(depts, t.departments || [])) patch.departments = depts;
        if (nextA?.mode === "multiple")
          patch.assigneeProgress = Object.fromEntries(
            nextA.uids.map((u) => [u, t.assigneeProgress?.[u] || { status: "pending", completedAt: null }]),
          );
        else if (t.assigneeProgress) patch.assigneeProgress = null;
        if (isSharedAssignment(nextA) && !isSharedAssignment(prevA)) patch.completion = { completedBy: null, completedAt: null };
        else if (!isSharedAssignment(nextA) && t.completion) patch.completion = null;
        events.push(
          makeEvent(me, "assign", nextA ? `assigned this to ${assignmentSummaryText({ assignment: nextA }, users)}` : "made this a personal task"),
        );
        newly = resolveAssignees(nextA, users)
          .map((p) => p.uid)
          .filter((u) => !(prevA?.uids || []).includes(u));
        if (isSharedAssignment(nextA) && !(isSharedAssignment(prevA) && sameSet(prevA.departments, nextA.departments)))
          becameShared = nextA;
      }
      const visibility = personal ? form.visibility : "shared";
      if (visibility !== (t.visibility || "shared")) {
        patch.visibility = visibility;
        events.push(makeEvent(me, "visibility", visibility === "shared" ? "shared this with the team" : "made this private"));
      }
    }
    if (!Object.keys(patch).length) return false;
    patch.updatedAt = nowISO();
    if (events.length) patch.comments = arrayUnion(...events);
    await tasks.update(t._id, patch);
    if (newly.length)
      notifyUsers(newly, { ...base(t), type: "assigned", title: `${me.name} assigned you a task`, msg: `${t.displayId} · ${title}` }, me);
    if (becameShared)
      notifyUsers(deptMemberUids(users, becameShared.departments), { ...base(t), type: "assigned", title: `New team task ${t.displayId}`, msg: title }, me);
    return true;
  };

  const setStatus = async (t, next) => {
    const cur = taskStatusOf(t);
    if (!next || next === cur) return;
    const patch = taskStatusPatch(t, next, me);
    patch.comments = arrayUnion(
      makeEvent(me, next === "completed" ? "completed" : "status", `changed the status from ${taskStatusLabel(cur)} to ${taskStatusLabel(next)}`, {
        from: cur,
        to: next,
      }),
    );
    try {
      await tasks.update(t._id, patch);
    } catch (e) {
      if (isSharedAssignment(assignmentOf(t)) && next === "completed")
        toast({ type: "warning", title: "Already completed", msg: "Someone on the team completed this task first." });
      else fail("Couldn't update the task", e);
      return;
    }
    if (next === "completed") toast({ type: "success", title: `${t.displayId} completed`, duration: 3000 });
    notifyUsers(
      [t.ownerUid, ...assigneeUidsOf(t)],
      {
        ...base(t),
        type: next === "completed" ? "completed" : "status",
        title: `${me.name} ${TASK_STATUS_VERBS[next] || "updated"} ${t.displayId}`,
        msg: t.title,
      },
      me,
    );
  };

  const toggleMine = async (t) => {
    const mineDone = t.assigneeProgress?.[me.uid]?.status === "completed";
    const now = nowISO();
    try {
      await tasks.update(t._id, {
        [`assigneeProgress.${me.uid}`]: { status: mineDone ? "pending" : "completed", completedAt: mineDone ? null : now },
        updatedAt: now,
        comments: arrayUnion(makeEvent(me, "progress", mineDone ? "reopened their part" : "finished their part")),
      });
    } catch (e) {
      return fail("Couldn't update your progress", e);
    }
    if (!mineDone) {
      const uids = assigneeUidsOf(t);
      const doneCount = uids.filter((u) => u !== me.uid && t.assigneeProgress?.[u]?.status === "completed").length + 1;
      notifyUsers(
        [t.ownerUid],
        {
          ...base(t),
          type: "progress",
          title: doneCount >= uids.length ? `Everyone finished ${t.displayId}` : `${me.name} finished their part of ${t.displayId}`,
          msg: `${doneCount} of ${uids.length} done · ${t.title}`,
        },
        me,
      );
    }
  };

  // The round checkbox: does the right thing for each kind of task.
  const toggleDone = (t, perms) => {
    if (perms.toggleMine) return toggleMine(t);
    if (!perms.status) return;
    const done = taskStatusOf(t) === "completed";
    if (perms.shared) {
      if (!perms.sharedDone) return setStatus(t, "completed");
      if (perms.manage || canEdit) return setStatus(t, "pending");
      return;
    }
    return setStatus(t, done ? "pending" : "completed");
  };

  const setField = async (t, field, value, event) => {
    try {
      await tasks.update(t._id, { [field]: value, updatedAt: nowISO(), comments: arrayUnion(event) });
    } catch (e) {
      fail("Couldn't save the change", e);
    }
  };
  const setPriority = (t, p) =>
    setField(t, "priority", p, makeEvent(me, "priority", `changed the priority from ${priorityLabel(t.priority)} to ${priorityLabel(p)}`));
  const setDue = (t, d) =>
    setField(t, "dueDate", d || null, makeEvent(me, "due", d ? `set the due date to ${fmtDate(d)}` : "removed the due date"));

  const comment = async (t, text) => {
    const entry = plain({ text, author: authorOf(me), at: nowISO() });
    try {
      await tasks.update(t._id, { comments: arrayUnion(entry) });
    } catch (e) {
      fail("Couldn't post your comment", e);
      throw e;
    }
    notifyUsers(
      [...participantsOf(t, users)],
      { ...base(t), type: "comment", title: `${me.name} commented on ${t.displayId || "a task"}`, msg: text.slice(0, 160) },
      me,
    );
  };

  const remove = async (t) => {
    const ok = await confirm({
      title: "Delete this task?",
      message: (
        <>
          <strong>“{t.title}”</strong> will be permanently deleted
          {t.visibility === "shared" ? " for everyone it's shared with" : ""}.
        </>
      ),
      confirmLabel: "Delete task",
      danger: true,
    });
    if (!ok) return false;
    const _id = t._id;
    const data = omit(t, ["_id"]);
    try {
      await tasks.remove(_id);
    } catch (e) {
      fail("Couldn't delete the task", e);
      return false;
    }
    toast({
      type: "success",
      title: "Task deleted",
      duration: 8000,
      action:
        t.ownerUid === me.uid
          ? {
              label: "Undo",
              onClick: () =>
                tasks
                  .set(_id, data)
                  .then(() => toast({ type: "success", title: "Task restored", duration: 3000 }))
                  .catch((e) => fail("Couldn't restore the task", e)),
            }
          : null,
    });
    return true;
  };

  return { create, saveEdits, setStatus, toggleMine, toggleDone, setPriority, setDue, comment, remove };
}

/* ═══════════════════════ PAGE ═══════════════════════ */
function TasksPage({ route }) {
  const app = useApp();
  const { me, users, tasks, navigate, closeDetail, role, canEdit } = app;
  const actions = useTaskActions();
  useNow();
  const [prefsRaw, setPrefsRaw] = useLocalStorage(TASK_PREFS_KEY, TASK_PREFS_DEFAULTS);
  const p = { ...TASK_PREFS_DEFAULTS, ...prefsRaw };
  const setP = (patch) => setPrefsRaw((prev) => ({ ...TASK_PREFS_DEFAULTS, ...prev, ...patch }));
  const [search, setSearch] = React.useState("");
  const [limit, setLimit] = React.useState(TASK_PAGE);
  const [form, setForm] = React.useState(null);
  const rows = tasks.rows;

  React.useEffect(() => {
    if (route.id === "new") {
      setForm({ mode: "create" });
      navigate("tasks", null, { replace: true });
    }
  }, [route.id]);
  React.useEffect(() => {
    const onKey = (e) => {
      if (e.key !== "c" || e.ctrlKey || e.metaKey || e.altKey) return;
      if (isTypingTarget(document.activeElement) || __layers.length) return;
      e.preventDefault();
      setForm({ mode: "create" });
    };
    document.addEventListener("keydown", onKey);
    return () => document.removeEventListener("keydown", onKey);
  }, []);

  const viewFns = {
    mine: (t) => taskIsMineToDo(t, me.uid),
    delegated: (t) => t.ownerUid === me.uid && hasTarget(assignmentOf(t)) && !isAssignedToMe(t, me.uid),
    team: (t) => t.visibility === "shared",
    all: () => true,
  };
  const q = search.trim().toLowerCase();
  const baseFiltered = React.useMemo(
    () =>
      rows.filter((t) => {
        const finished = taskFinished(t, me.uid);
        if (p.status === "active" && finished) return false;
        if (p.status === "done" && !finished) return false;
        if (p.priority !== "all" && (t.priority || "medium") !== p.priority) return false;
        if (p.person !== "all" && !(t.ownerUid === p.person || assigneeUidsOf(t).includes(p.person))) return false;
        if (!matchesPeriod(t, p.period, finished)) return false;
        if (q) {
          const hay = `${t.displayId || ""} ${t.title} ${t.description || ""} ${t.createdBy?.name || ""} ${assignmentSummaryText(t, users)}`.toLowerCase();
          if (!q.split(/\s+/).every((w) => hay.includes(w))) return false;
        }
        return true;
      }),
    [rows, users, p.status, p.priority, p.person, JSON.stringify(p.period), q],
  );
  const sorters = {
    due: (a, b) => {
      const fa = taskFinished(a, me.uid);
      const fb = taskFinished(b, me.uid);
      if (fa !== fb) return fa ? 1 : -1;
      if (!a.dueDate && !b.dueDate) return (PRIORITY_RANK[a.priority] ?? 2) - (PRIORITY_RANK[b.priority] ?? 2);
      if (!a.dueDate) return 1;
      if (!b.dueDate) return -1;
      return a.dueDate.localeCompare(b.dueDate) || (PRIORITY_RANK[a.priority] ?? 2) - (PRIORITY_RANK[b.priority] ?? 2);
    },
    priority: (a, b) => (PRIORITY_RANK[a.priority] ?? 2) - (PRIORITY_RANK[b.priority] ?? 2) || (b.createdAt || "").localeCompare(a.createdAt || ""),
    newest: (a, b) => (b.createdAt || "").localeCompare(a.createdAt || ""),
    updated: (a, b) => lastActivityAt(b).localeCompare(lastActivityAt(a)),
  };
  const filtered = React.useMemo(
    () => baseFiltered.filter(viewFns[p.view] || viewFns.mine).sort(sorters[p.sort] || sorters.due),
    [baseFiltered, p.view, p.sort],
  );
  React.useEffect(() => setLimit(TASK_PAGE), [p.view, p.status, p.priority, p.person, JSON.stringify(p.period), q, p.sort]);

  const counts = {
    mine: baseFiltered.filter(viewFns.mine).length,
    delegated: baseFiltered.filter(viewFns.delegated).length,
    team: baseFiltered.filter(viewFns.team).length,
    all: baseFiltered.length,
  };
  const myActive = rows.filter((t) => viewFns.mine(t) && !taskFinished(t, me.uid));
  const summary = {
    active: myActive.length,
    overdue: myActive.filter((t) => dueInfo(t.dueDate, false)?.state === "overdue").length,
    today: myActive.filter((t) => dueInfo(t.dueDate, false)?.state === "today").length,
  };
  const people = assignableUsers(users, role);
  const filtersActive = p.priority !== "all" || p.person !== "all" || p.period.kind !== "all" || !!q;

  const openId = route.id && route.id !== "new" ? route.id : null;
  const openTask = openId ? rows.find((t) => t._id === openId) : null;
  const navList = openTask && filtered.some((t) => t._id === openId) ? filtered : rows;
  const idx = openTask ? navList.findIndex((t) => t._id === openId) : -1;
  const shown = filtered.slice(0, limit);

  return (
    <div className="page-tasks">
      <div className="page-header">
        <div>
          <h2>Tasks &amp; Notes</h2>
          <p>
            {summary.active
              ? `You have ${plural(summary.active, "open task")}${summary.overdue ? ` · ${summary.overdue} overdue` : ""}${summary.today ? ` · ${summary.today} due today` : ""}.`
              : "Your to-dos, delegated work, and quick notes in one place."}
          </p>
        </div>
        <div className="page-header-actions">
          <button className="btn btn-primary" onClick={() => setForm({ mode: "create" })} title="New task (C)">
            <IC.Plus /> New task
          </button>
        </div>
      </div>

      <div className="tasks-layout">
        <div className="list-shell">
          <div className="list-tabs">
            <ViewTabs
              value={p.view}
              onChange={(view) => setP({ view })}
              tabs={[
                { value: "mine", label: "My tasks", count: counts.mine },
                { value: "delegated", label: "Delegated", count: counts.delegated },
                { value: "team", label: "Team", count: counts.team },
                { value: "all", label: "All", count: counts.all },
              ]}
            />
            <Segmented
              size="sm"
              value={p.status}
              onChange={(status) => setP({ status })}
              options={[
                { value: "active", label: "Active" },
                { value: "done", label: "Done" },
                { value: "all", label: "All" },
              ]}
            />
          </div>
          <div className="list-toolbar">
            <SearchInput value={search} onChange={setSearch} placeholder="Search tasks…" />
            <FilterMenu
              label="Priority"
              icon={IC.Flag}
              value={p.priority}
              onChange={(priority) => setP({ priority })}
              options={[{ value: "all", label: "Any priority" }, ...PRIORITIES.map((x) => ({ value: x, label: priorityLabel(x), node: <PriorityTag p={x} /> }))]}
            />
            <FilterMenu
              label="Person"
              icon={IC.User}
              value={p.person}
              onChange={(person) => setP({ person })}
              width={260}
              options={[
                { value: "all", label: "Anyone" },
                ...people.map((u) => ({
                  value: u._id,
                  label: u._id === me.uid ? `${userLabel(u)} (you)` : userLabel(u),
                  node: (
                    <span className="assignee-one">
                      <Avatar name={userLabel(u)} email={u.email} size={20} />
                      {userLabel(u)}
                      {u._id === me.uid && <span className="you-tag">you</span>}
                    </span>
                  ),
                })),
              ]}
            />
            <PeriodFilter value={p.period} onChange={(period) => setP({ period })} />
            {filtersActive && (
              <button
                className="btn btn-ghost btn-sm"
                onClick={() => {
                  setSearch("");
                  setP({ priority: "all", person: "all", period: { ...EMPTY_PERIOD } });
                }}
              >
                <IC.X /> Clear
              </button>
            )}
            <span className="toolbar-spacer" />
            <Dropdown
              width={190}
              placement="bottom-end"
              trigger={({ ref, toggle, open }) => (
                <button ref={ref} type="button" className={cx("btn btn-ghost btn-sm", open && "active")} onClick={toggle}>
                  <IC.ArrowUpDown />
                  {{ due: "Due date", priority: "Priority", newest: "Newest", updated: "Last updated" }[p.sort]}
                </button>
              )}
            >
              {({ close }) => (
                <MenuList>
                  <MenuLabel>Sort by</MenuLabel>
                  {[
                    ["due", "Due date"],
                    ["priority", "Priority"],
                    ["newest", "Newest first"],
                    ["updated", "Last updated"],
                  ].map(([v, l]) => (
                    <MenuItem
                      key={v}
                      checkable
                      active={p.sort === v}
                      onSelect={() => {
                        setP({ sort: v });
                        close();
                      }}
                    >
                      {l}
                    </MenuItem>
                  ))}
                </MenuList>
              )}
            </Dropdown>
          </div>

          {!tasks.loaded && rows.length === 0 ? (
            <div className="task-list">
              {Array.from({ length: 5 }).map((_, i) => (
                <div key={i} className="task-row skeleton-row">
                  <Skeleton w={20} h={20} r={99} />
                  <div style={{ flex: 1 }}>
                    <Skeleton w="50%" h={12} />
                    <Skeleton w="30%" h={10} style={{ marginTop: 8 }} />
                  </div>
                </div>
              ))}
            </div>
          ) : filtered.length === 0 ? (
            <EmptyState
              icon={IC.Tasks}
              title={p.status === "active" && p.view === "mine" && !filtersActive ? "You're all caught up" : "No tasks here"}
              action={
                <button className="btn btn-primary" onClick={() => setForm({ mode: "create" })}>
                  <IC.Plus /> New task
                </button>
              }
            >
              {p.status === "active" && p.view === "mine" && !filtersActive
                ? "Nothing on your plate right now."
                : "Try another view or clear the filters."}
            </EmptyState>
          ) : (
            <div className="task-list">
              {shown.map((t, i) => (
                <TaskRow key={t._id} t={t} i={i} selected={t._id === openId} onOpen={() => navigate("tasks", t._id)} actions={actions} />
              ))}
              <LoadMoreSentinel onVisible={() => setLimit((l) => l + TASK_PAGE)} shown={shown.length} total={filtered.length} label="tasks" />
            </div>
          )}
        </div>

        <NotesPanel />
      </div>

      <Drawer open={!!openId} onClose={() => closeDetail("tasks")} width={880} label="Task details">
        <TaskDetail
          task={openTask}
          loading={!tasks.loaded}
          onClose={() => closeDetail("tasks")}
          onPrev={idx > 0 ? () => navigate("tasks", navList[idx - 1]._id, { replace: true }) : null}
          onNext={idx >= 0 && idx < navList.length - 1 ? () => navigate("tasks", navList[idx + 1]._id, { replace: true }) : null}
          position={idx >= 0 ? { index: idx, total: navList.length } : null}
          onEdit={(t) => setForm({ mode: "edit", task: t })}
          actions={actions}
        />
      </Drawer>

      <TaskFormModal
        state={form}
        onClose={() => setForm(null)}
        actions={actions}
        onCreated={(t) =>
          app.toast({
            type: "success",
            title: "Task created",
            msg: t.title,
            action: { label: "Open", onClick: () => navigate("tasks", t._id) },
          })
        }
      />
    </div>
  );
}

function VisTag({ v }) {
  return v === "private" ? (
    <span className="vis-tag private" title="Only you can see this">
      <IC.Lock /> Private
    </span>
  ) : (
    <span className="vis-tag shared" title="Visible to your team">
      <IC.Globe /> Shared
    </span>
  );
}

function TaskRow({ t, i, selected, onOpen, actions }) {
  const app = useApp();
  const { me, users } = app;
  const perms = taskPerms(t, app);
  const status = taskStatusOf(t);
  const done = taskDoneFor(t, me.uid);
  const finished = taskFinished(t, me.uid);
  const canCheck = perms.toggleMine || (perms.status && (!perms.sharedDone || perms.manage || app.canEdit));
  const a = t.assignment ? assignmentOf(t) : null;
  const comments = realComments(t).length;
  const progress =
    a?.mode === "multiple"
      ? `${a.uids.filter((u) => t.assigneeProgress?.[u]?.status === "completed").length}/${a.uids.length}`
      : null;
  const fromOther = t.ownerUid !== me.uid;
  return (
    <div
      className={cx("task-row", finished && "finished", selected && "selected")}
      style={{ "--i": Math.min(i, 14) }}
      onClick={onOpen}
      role="button"
      tabIndex={0}
      onKeyDown={(e) => {
        if (e.key === "Enter") onOpen();
      }}
    >
      <button
        type="button"
        className={cx("task-check", done && "checked", !canCheck && "locked")}
        disabled={!canCheck}
        title={
          !canCheck ? "You can't change this task" : perms.toggleMine ? (done ? "Undo my part" : "Mark my part done") : done ? "Mark as not done" : "Mark complete"
        }
        aria-label={done ? "Mark as not done" : "Mark complete"}
        onClick={(e) => {
          e.stopPropagation();
          actions.toggleDone(t, perms);
        }}
      >
        <IC.Check />
      </button>
      <div className="task-main">
        <div className="task-title">{t.title}</div>
        <div className="task-meta">
          {t.displayId && <span className="tk-id">{t.displayId}</span>}
          {!["pending", "completed"].includes(status) && <StatusBadge status={status} kind="task" />}
          <DueChip due={t.dueDate} done={finished} compact />
          {(t.priority === "high" || t.priority === "critical") && <PriorityTag p={t.priority} />}
          {a ? (
            <span className="task-assign">
              <AssigneeCell item={t} users={users} size={18} />
              {progress && <span className="muted">· {progress}</span>}
            </span>
          ) : (
            <VisTag v={t.visibility || "shared"} />
          )}
          {comments > 0 && (
            <span className="tk-comments">
              <IC.Comment />
              {comments}
            </span>
          )}
        </div>
      </div>
      {fromOther && t.createdBy?.name && (
        <span className="task-from" title={`From ${t.createdBy.name}`}>
          <span className="muted">from</span>
          <Avatar name={t.createdBy.name} email={t.createdBy.email} size={22} />
        </span>
      )}
      <IC.ChevronRight />
    </div>
  );
}

/* ═══════════════════════ TASK DETAIL ═══════════════════════ */
function TaskDetail({ task, loading, onClose, onPrev, onNext, position, onEdit, actions }) {
  const app = useApp();
  const { me, users, canEdit } = app;
  const [busy, setBusy] = React.useState(false);
  useDetailKeys(!!task, { onPrev, onNext });
  if (!task) return <DetailNotFound what="task" onClose={onClose} loading={loading} />;
  const t = task;
  const perms = taskPerms(t, app);
  const allowed = allowedTaskStatuses(t, perms);
  const status = taskStatusOf(t);
  const steps = taskNextSteps(t, perms, allowed);
  const a = t.assignment ? assignmentOf(t) : null;
  const run = async (fn) => {
    setBusy(true);
    try {
      await fn();
    } finally {
      setBusy(false);
    }
  };
  const copyLink = async () => {
    const ok = await copyText(`${location.origin}${location.pathname}#/tasks/${t._id}`);
    app.toast({ type: ok ? "success" : "error", title: ok ? "Link copied" : "Couldn't copy the link", duration: 2500 });
  };
  const creator = t.createdBy || {};
  return (
    <div className="detail">
      <div className="drawer-hdr">
        <div className="drawer-crumb">
          <IC.Tasks />
          <span>Tasks</span>
          <IC.ChevronRight />
          <span className="tk-id">{t.displayId || "Task"}</span>
        </div>
        <div className="drawer-actions">
          {position && (
            <span className="drawer-pos">
              {position.index + 1} / {position.total}
            </span>
          )}
          <button className="icon-btn" onClick={onPrev} disabled={!onPrev} title="Previous (K)" aria-label="Previous task">
            <IC.ChevronUp />
          </button>
          <button className="icon-btn" onClick={onNext} disabled={!onNext} title="Next (J)" aria-label="Next task">
            <IC.ChevronDown />
          </button>
          {t.visibility === "shared" && (
            <button className="icon-btn" onClick={copyLink} title="Copy link" aria-label="Copy link">
              <IC.Link />
            </button>
          )}
          {(perms.manage || perms.del) && (
            <Dropdown
              width={190}
              placement="bottom-end"
              trigger={({ ref, toggle }) => (
                <button ref={ref} className="icon-btn" onClick={toggle} title="More actions" aria-label="More actions">
                  <IC.More />
                </button>
              )}
            >
              {({ close }) => (
                <MenuList>
                  {perms.manage && (
                    <MenuItem
                      icon={IC.Edit}
                      onSelect={() => {
                        close();
                        onEdit(t);
                      }}
                    >
                      Edit task
                    </MenuItem>
                  )}
                  {perms.del && (
                    <MenuItem
                      icon={IC.Trash}
                      danger
                      onSelect={async () => {
                        close();
                        if (await actions.remove(t)) onClose();
                      }}
                    >
                      Delete task
                    </MenuItem>
                  )}
                </MenuList>
              )}
            </Dropdown>
          )}
          <span className="hdr-sep" />
          <button className="icon-btn" onClick={onClose} title="Close (Esc)" aria-label="Close">
            <IC.X />
          </button>
        </div>
      </div>

      <div className="drawer-body">
        <div className="detail-main">
          <div className="detail-badges">
            <StatusBadge status={status} kind="task" />
            <PriorityTag p={t.priority || "medium"} />
            <VisTag v={t.visibility || "shared"} />
            {perms.shared && <DeptPill assignment={a} />}
            {perms.isAssignee && <span className="pill-me">Assigned to you</span>}
          </div>
          <h2 className="detail-title">{t.title}</h2>
          <div className="detail-sub">
            <Avatar name={creator.name} email={creator.email} size={20} />
            <span>
              <b>{t.ownerUid === me.uid ? "You" : creator.name || "Someone"}</b> created this{" "}
              <span title={fmtDateTime(t.createdAt)}>{timeAgo(t.createdAt)}</span>
            </span>
            {perms.manage && (
              <button className="link-btn" onClick={() => onEdit(t)}>
                <IC.Edit /> Edit
              </button>
            )}
          </div>

          {steps.length > 0 && (
            <div className="detail-cta">
              {steps.map((s) => (
                <button key={s.next} className={cx("btn", s.cls)} disabled={busy} onClick={() => run(() => actions.setStatus(t, s.next))}>
                  <s.icon /> {s.label}
                </button>
              ))}
            </div>
          )}
          <CompletionLine completion={t.completion} verb="Completed" />

          <section className="detail-section">
            <div className="detail-section-head">
              <h4>Description</h4>
            </div>
            <div className={cx("detail-desc", !t.description && "empty")}>
              {t.description ? <RichText text={t.description} /> : "No description."}
            </div>
          </section>

          <AssigneeProgress
            item={t}
            users={users}
            meUid={me.uid}
            canToggle={perms.toggleMine}
            busy={busy}
            onToggleMine={() => run(() => actions.toggleMine(t))}
          />

          <ActivitySection item={t} kind="task" me={me} onComment={(text) => actions.comment(t, text)} />
        </div>

        <aside className="detail-props">
          <div className="props-title">Details</div>
          <PropRow label="Status" icon={IC.Activity}>
            <StatusMenu status={status} kind="task" allowed={allowed} onChange={(s) => actions.setStatus(t, s)} />
          </PropRow>
          <PropRow label="Priority" icon={IC.Flag}>
            <PriorityMenu value={t.priority || "medium"} disabled={!perms.manage} onChange={(v) => actions.setPriority(t, v)} />
          </PropRow>
          <PropRow label="Due date" icon={IC.Calendar}>
            {perms.manage ? (
              <DateField value={t.dueDate || ""} onChange={(d) => actions.setDue(t, d)} variant="prop" placeholder="Set due date" />
            ) : (
              <DueChip due={t.dueDate} done={taskFinished(t, me.uid)} empty={<span className="muted">No due date</span>} />
            )}
          </PropRow>
          <PropRow label="Assigned to" icon={IC.User}>
            {a ? (
              <span className="prop-inline">
                <AssigneeCell item={t} users={users} size={22} />
                {perms.manage && canEdit && (
                  <button className="icon-btn sm" title="Change assignment" onClick={() => onEdit(t)}>
                    <IC.Edit />
                  </button>
                )}
              </span>
            ) : (
              <span className="prop-text">Personal task</span>
            )}
          </PropRow>
          <PropRow label="Visibility" icon={IC.Eye}>
            <VisTag v={t.visibility || "shared"} />
          </PropRow>
          <div className="props-sep" />
          <PropRow label="Owner" icon={IC.User}>
            <span className="assignee-one">
              <Avatar name={creator.name} email={creator.email} size={20} />
              <span className="assignee-name">{t.ownerUid === me.uid ? "You" : creator.name || "—"}</span>
            </span>
          </PropRow>
          <PropRow label="Created" icon={IC.Clock}>
            <span className="prop-text" title={fmtDateTime(t.createdAt)}>
              {fmtDate(t.createdAt)} · {fmtTime(t.createdAt)}
            </span>
          </PropRow>
          <PropRow label="Updated" icon={IC.Refresh}>
            <span className="prop-text" title={fmtDateTime(lastActivityAt(t))}>
              {timeAgo(lastActivityAt(t))}
            </span>
          </PropRow>
          {t.completedAt && (
            <PropRow label="Completed" icon={IC.CheckCircle}>
              <span className="prop-text" title={fmtDateTime(t.completedAt)}>
                {fmtDate(t.completedAt)}
              </span>
            </PropRow>
          )}
          <div className="props-hint">
            <Kbd>J</Kbd> <Kbd>K</Kbd> next / previous · <Kbd>Esc</Kbd> close
          </div>
        </aside>
      </div>
    </div>
  );
}

/* ═══════════════════════ TASK FORM ═══════════════════════ */
function TaskFormModal({ state, onClose, actions, onCreated }) {
  const app = useApp();
  const { users, role, me, canEdit } = app;
  const open = !!state;
  const editing = state?.mode === "edit" ? state.task : null;
  const blank = () => ({
    title: "",
    description: "",
    dueDate: "",
    priority: "medium",
    visibility: canEdit ? "shared" : "private",
    assignment: blankAssignment(),
  });
  const [form, setForm] = React.useState(blank);
  const [initial, setInitial] = React.useState(blank);
  const [errors, setErrors] = React.useState({});
  const [busy, setBusy] = React.useState(false);
  React.useEffect(() => {
    if (!open) return;
    const f = editing
      ? {
          title: editing.title || "",
          description: editing.description || "",
          dueDate: editing.dueDate || "",
          priority: editing.priority || "medium",
          visibility: editing.visibility || "shared",
          assignment: editing.assignment ? assignmentOf(editing) : blankAssignment(),
        }
      : blank();
    setForm(f);
    setInitial(f);
    setErrors({});
    setBusy(false);
  }, [open, editing?._id]);
  const set = (k, v) => {
    setForm((f) => ({ ...f, [k]: v }));
    if (errors[k]) setErrors((e) => ({ ...e, [k]: null }));
  };
  const dirty = open && JSON.stringify(form) !== JSON.stringify(initial);
  const a = normalizeAssignment(form.assignment);
  const personal = !hasTarget(a) || (a.mode === "individual" && a.uids[0] === me.uid);

  const submit = async () => {
    const errs = {};
    if (!form.title.trim()) errs.title = "What needs to be done?";
    const aErr = canEdit ? assignmentError(form.assignment) : null;
    if (aErr) errs.assignment = aErr;
    setErrors(errs);
    if (Object.keys(errs).length) return;
    setBusy(true);
    try {
      if (editing) {
        const changed = await actions.saveEdits(editing, form);
        app.toast({ type: "success", title: changed ? "Task updated" : "No changes to save", duration: 3000 });
      } else {
        const t = await actions.create(form);
        onCreated?.(t);
      }
      setInitial(form);
      onClose();
    } catch (e) {
      console.error(e);
      app.toast({ type: "error", title: editing ? "Couldn't save the task" : "Couldn't create the task", msg: writeErrorMsg(e), duration: 8000 });
    } finally {
      setBusy(false);
    }
  };

  return (
    <Modal
      open={open}
      onClose={onClose}
      dirty={dirty && !busy}
      size="lg"
      icon={editing ? IC.Edit : IC.Tasks}
      title={editing ? "Edit task" : "New task"}
      subtitle={editing ? "Changes are recorded in the task's history." : canEdit ? "Plan it for yourself or delegate it to your team." : "Add something to your to-do list."}
      footer={
        <>
          <span className="modal-footer-hint">
            <Kbd>Ctrl</Kbd> + <Kbd>Enter</Kbd> to save
          </span>
          <button className="btn btn-ghost" onClick={onClose} disabled={busy}>
            Cancel
          </button>
          <button className="btn btn-primary" onClick={submit} disabled={busy}>
            {busy ? <Spinner size={14} /> : <IC.Check />}
            {editing ? "Save changes" : "Create task"}
          </button>
        </>
      }
    >
      <div
        className="form-stack"
        onKeyDown={(e) => {
          if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) {
            e.preventDefault();
            submit();
          }
        }}
      >
        <Field label="Task" required error={errors.title}>
          <input
            className="form-input input-lg"
            placeholder="What needs to be done?"
            value={form.title}
            maxLength={220}
            onChange={(e) => set("title", e.target.value)}
            data-autofocus
          />
        </Field>
        <Field label="Details" hint="Optional — instructions, links, or acceptance criteria.">
          <AutosizeTextarea minRows={3} maxRows={10} value={form.description} onChange={(e) => set("description", e.target.value)} placeholder="Add more detail…" />
        </Field>
        <Field label="Priority">
          <PrioritySelector value={form.priority} onChange={(v) => set("priority", v)} />
        </Field>
        <div className="form-grid">
          <Field label="Due date">
            <DateField value={form.dueDate} onChange={(d) => set("dueDate", d)} variant="input" />
          </Field>
          {canEdit && personal ? (
            <Field label="Visibility">
              <Segmented
                value={form.visibility}
                onChange={(v) => set("visibility", v)}
                options={[
                  { value: "shared", label: "Shared", icon: IC.Globe },
                  { value: "private", label: "Private", icon: IC.Lock },
                ]}
              />
            </Field>
          ) : (
            <Field label="Visibility" hint={!canEdit ? "Your personal tasks are private to you." : "Assigned tasks are shared with the people doing them."}>
              <div className="static-field">
                {canEdit ? (
                  <>
                    <IC.Globe /> Shared with assignees
                  </>
                ) : (
                  <>
                    <IC.Lock /> Private
                  </>
                )}
              </div>
            </Field>
          )}
        </div>
        {canEdit && (
          <Field label="Assign to" error={errors.assignment} hint={personal ? "Leave unassigned (or pick yourself) for a personal task." : null}>
            <AssignmentPicker value={form.assignment} onChange={(v) => set("assignment", v)} users={users} role={role} meUid={me.uid} />
          </Field>
        )}
      </div>
    </Modal>
  );
}

/* ═══════════════════════ NOTES ═══════════════════════ */
function NotesPanel() {
  const app = useApp();
  const { me, notes, canEdit, canDelete } = app;
  const toast = useToast();
  const confirm = useConfirm();
  const [text, setText] = React.useState("");
  const [vis, setVis] = useLocalStorage("msindaha_note_vis", "private");
  const [busy, setBusy] = React.useState(false);
  const [q, setQ] = React.useState("");
  const [editing, setEditing] = React.useState(null); // { id, text }
  const [limit, setLimit] = React.useState(20);
  const visibility = canEdit ? vis : "private";

  const add = async () => {
    const v = text.trim();
    if (!v || busy) return;
    setBusy(true);
    try {
      await notes.add(
        plain({
          displayId: `NOTE-${Date.now().toString(36).toUpperCase()}`,
          text: v,
          visibility,
          departments: [MY_DEPARTMENT],
          ownerUid: me.uid,
          createdAt: nowISO(),
          createdBy: authorOf(me),
        }),
      );
      setText("");
    } catch (e) {
      toast({ type: "error", title: "Couldn't save the note", msg: writeErrorMsg(e) });
    } finally {
      setBusy(false);
    }
  };
  const saveEdit = async () => {
    const v = editing?.text.trim();
    if (!v) return;
    try {
      await notes.update(editing.id, { text: v, updatedAt: nowISO() });
      setEditing(null);
    } catch (e) {
      toast({ type: "error", title: "Couldn't update the note", msg: writeErrorMsg(e) });
    }
  };
  const del = async (n) => {
    const ok = await confirm({ title: "Delete this note?", message: "It will be removed permanently.", confirmLabel: "Delete", danger: true });
    if (!ok) return;
    try {
      await notes.remove(n._id);
    } catch (e) {
      toast({ type: "error", title: "Couldn't delete the note", msg: writeErrorMsg(e) });
    }
  };

  const list = notes.rows
    .slice()
    .sort((a, b) => (b.createdAt || "").localeCompare(a.createdAt || ""))
    .filter((n) => !q.trim() || (n.text || "").toLowerCase().includes(q.trim().toLowerCase()));
  const shown = list.slice(0, limit);

  return (
    <aside className="notes-col">
      <div className="card notes-card">
        <div className="section-head">
          <IC.Note /> Quick notes
          <span className="section-head-count">{notes.rows.length}</span>
        </div>
        <div className="note-composer">
          <AutosizeTextarea
            minRows={3}
            maxRows={8}
            value={text}
            onChange={(e) => setText(e.target.value)}
            placeholder="Capture a quick note…"
            onKeyDown={(e) => {
              if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) {
                e.preventDefault();
                add();
              }
            }}
          />
          <div className="note-composer-foot">
            {canEdit ? (
              <Segmented
                size="sm"
                value={vis}
                onChange={setVis}
                options={[
                  { value: "private", label: "Private", icon: IC.Lock },
                  { value: "shared", label: "Shared", icon: IC.Globe },
                ]}
              />
            ) : (
              <VisTag v="private" />
            )}
            <button className="btn btn-primary btn-sm" onClick={add} disabled={!text.trim() || busy}>
              {busy ? <Spinner size={13} /> : <IC.Plus />} Add note
            </button>
          </div>
        </div>
        {notes.rows.length > 3 && (
          <div className="notes-search">
            <SearchInput value={q} onChange={setQ} placeholder="Search notes…" shortcut={false} />
          </div>
        )}
        <div className="notes-list">
          {!notes.loaded && notes.rows.length === 0 ? (
            <div className="notif-loading">
              <Spinner />
            </div>
          ) : list.length === 0 ? (
            <div className="notes-empty">{q ? "No notes match your search." : "No notes yet — jot something down above."}</div>
          ) : (
            shown.map((n) => {
              const mine = n.ownerUid === me.uid;
              const canDel = mine || (canDelete && n.visibility === "shared");
              const isEditing = editing?.id === n._id;
              return (
                <div key={n._id} className={cx("note-item", (n.visibility || "shared") === "private" && "private")}>
                  {isEditing ? (
                    <>
                      <AutosizeTextarea
                        minRows={2}
                        maxRows={10}
                        value={editing.text}
                        autoFocus
                        onChange={(e) => setEditing({ ...editing, text: e.target.value })}
                        onKeyDown={(e) => {
                          if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) {
                            e.preventDefault();
                            saveEdit();
                          }
                          if (e.key === "Escape") {
                            e.stopPropagation();
                            setEditing(null);
                          }
                        }}
                      />
                      <div className="note-edit-foot">
                        <button className="btn btn-ghost btn-sm" onClick={() => setEditing(null)}>
                          Cancel
                        </button>
                        <button className="btn btn-primary btn-sm" onClick={saveEdit}>
                          Save
                        </button>
                      </div>
                    </>
                  ) : (
                    <>
                      <div className="note-text">
                        <RichText text={n.text} />
                      </div>
                      <div className="note-foot">
                        <VisTag v={n.visibility || "shared"} />
                        <span className="note-meta" title={fmtDateTime(n.createdAt)}>
                          {!mine && n.createdBy?.name ? `${n.createdBy.name} · ` : ""}
                          {timeAgo(n.createdAt)}
                        </span>
                        <span className="note-actions">
                          {mine && (
                            <button className="icon-btn sm" title="Edit" aria-label="Edit note" onClick={() => setEditing({ id: n._id, text: n.text })}>
                              <IC.Edit />
                            </button>
                          )}
                          {canDel && (
                            <button className="icon-btn sm danger" title="Delete" aria-label="Delete note" onClick={() => del(n)}>
                              <IC.Trash />
                            </button>
                          )}
                        </span>
                      </div>
                    </>
                  )}
                </div>
              );
            })
          )}
          {list.length > shown.length && (
            <button className="btn btn-ghost btn-sm notes-more" onClick={() => setLimit((l) => l + 20)}>
              Show {Math.min(20, list.length - shown.length)} more
            </button>
          )}
        </div>
      </div>
    </aside>
  );
}
