"use client";

import { ClipboardEvent, FormEvent, type ReactNode, useEffect, useMemo, useRef, useState } from "react";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import clsx from "clsx";
import { ArrowDown, ArrowUp, Bold, CalendarDays, CheckSquare2, Clock3, Eraser, FolderSymlink, Highlighter, Italic, Link, List, ListOrdered, Maximize2, Minimize2, MoreHorizontal, Palette, Plus, RefreshCw, Pencil, Share2, Trash2, Underline, X } from "lucide-react";
import { api } from "@/lib/api";
import { createTranslator } from "@/lib/i18n";
import { ShareRecipientAutocomplete } from "./share-recipient-autocomplete";
import type { CalendarStatus, NativeZimbraTask } from "@/lib/types";
import type { NativeZimbraShareEntry } from "@/lib/types";
import type { NativeZimbraTaskList } from "@/lib/types";
import type { Contact } from "@/lib/types";

const taskListColorOptions = [
  { value: 1, label: "Blue", hex: "#60a5fa" },
  { value: 2, label: "Cyan", hex: "#22d3ee" },
  { value: 3, label: "Green", hex: "#4ade80" },
  { value: 4, label: "Purple", hex: "#a78bfa" },
  { value: 5, label: "Red", hex: "#fb7185" },
  { value: 6, label: "Yellow", hex: "#facc15" },
  { value: 7, label: "Pink", hex: "#f472b6" },
  { value: 8, label: "Gray", hex: "#94a3b8" },
  { value: 9, label: "Orange", hex: "#fb923c" },
] as const;

const TASK_DETAIL_MAX_CHARS = 200_000;

type NativeZimbraTasksTabProps = {
  serviceLabel?: string;
  contacts: Contact[];
  t: ReturnType<typeof createTranslator>;
  status?: CalendarStatus;
  statusLoading?: boolean;
  connectPending?: boolean;
  onConnect?: () => void;
};

type TaskDraft = {
  subject: string;
  location: string;
  description: string;
  due: string;
  start: string;
  completed: string;
  reminder_date: string;
  reminder_time: string;
  status: string;
  percent_complete: number;
  priority: string;
  reminder_minutes: number;
};

const DEFAULT_DRAFT: TaskDraft = {
  subject: "",
  location: "",
  description: "",
  due: "",
  start: "",
  completed: "",
  reminder_date: "",
  reminder_time: "19:00",
  status: "NEEDS-ACTION",
  percent_complete: 0,
  priority: "",
  reminder_minutes: 0,
};

export function NativeZimbraTasksAddonTab({ serviceLabel = "Task", contacts, t, status, statusLoading = false, connectPending = false, onConnect, active = true }: NativeZimbraTasksTabProps & { active?: boolean }) {
  const queryClient = useQueryClient();
  const [draft, setDraft] = useState<TaskDraft>(DEFAULT_DRAFT);
  const [editingTask, setEditingTask] = useState<NativeZimbraTask | null>(null);
  const [dialogOpen, setDialogOpen] = useState(false);
  const [deleteConfirmTask, setDeleteConfirmTask] = useState<NativeZimbraTask | null>(null);
  const [notice, setNotice] = useState<{ tone: "success" | "error" | "info"; message: string } | null>(null);
  const [selectedTaskListId, setSelectedTaskListId] = useState("");
  const [shareEmail, setShareEmail] = useState("");
  const [sharePermissions, setSharePermissions] = useState("r");
  const [newTaskListName, setNewTaskListName] = useState("");
  const [actionMenuTaskListId, setActionMenuTaskListId] = useState("");
  const [shareTaskListId, setShareTaskListId] = useState("");
  const [colorTaskListId, setColorTaskListId] = useState("");
  const [renameTaskListId, setRenameTaskListId] = useState("");
  const [renameTaskListName, setRenameTaskListName] = useState("");
  const canLoadTasks = Boolean(status?.configured && (status.connected || status.mode === "public"));

  const taskLists = useQuery({
    queryKey: ["native-zimbra-task-lists"],
    queryFn: api.nativeZimbraTaskLists,
    enabled: canLoadTasks,
    staleTime: 30_000,
    refetchOnWindowFocus: false,
  });

  const tasks = useQuery({
    queryKey: ["native-zimbra-tasks", selectedTaskListId],
    queryFn: () => api.nativeZimbraTasks(selectedTaskListId ? { folder_id: selectedTaskListId } : undefined),
    enabled: canLoadTasks,
    staleTime: 30_000,
    refetchOnWindowFocus: false,
  });
  const taskShares = useQuery({
    queryKey: ["native-zimbra-task-shares", shareTaskListId || selectedTaskListId],
    queryFn: () => api.nativeZimbraTaskShares(shareTaskListId || selectedTaskListId),
    staleTime: 30_000,
    refetchOnWindowFocus: false,
    enabled: canLoadTasks && Boolean(shareTaskListId || selectedTaskListId),
  });
  const visibleTaskShares = useMemo(
    () => (taskShares.data?.shares ?? []).filter(hasShareRecipient),
    [taskShares.data?.shares]
  );
  const shareTaskList = useMemo(
    () => (taskLists.data?.lists ?? []).find((list) => list.id === shareTaskListId) ?? null,
    [shareTaskListId, taskLists.data?.lists]
  );

  const saveTask = useMutation({
    mutationFn: async (payload: { taskId?: string; data: TaskDraft }) => {
      const folderId = selectedTaskListId || editingTask?.folder_id || "";
      if (payload.taskId) {
        return api.updateNativeZimbraTask(payload.taskId, {
          folder_id: folderId,
          invite_id: editingTask?.invite_id,
          subject: payload.data.subject,
          location: payload.data.location,
          description: payload.data.description,
          due: payload.data.due,
          start: payload.data.start,
          completed: "",
          reminder_at: taskReminderAt(payload.data),
          status: payload.data.status,
          percent_complete: payload.data.percent_complete,
          priority: payload.data.priority,
          reminder_minutes: payload.data.reminder_minutes,
        });
      }
      return api.createNativeZimbraTask({
        folder_id: folderId,
        subject: payload.data.subject,
        location: payload.data.location,
        description: payload.data.description,
        due: payload.data.due,
        start: payload.data.start,
        completed: "",
        reminder_at: taskReminderAt(payload.data),
        status: payload.data.status,
        percent_complete: payload.data.percent_complete,
        priority: payload.data.priority,
        reminder_minutes: payload.data.reminder_minutes,
      });
    },
    onSuccess: async (response, variables) => {
      const savedTask = response.task;
      const savedFolderId = savedTask.folder_id || selectedTaskListId;
      if (!variables.taskId) {
        queryClient.setQueryData<{ tasks: NativeZimbraTask[] }>(["native-zimbra-tasks", savedFolderId], (current) => {
          const currentTasks = current?.tasks ?? [];
          if (currentTasks.some((task) => task.id === savedTask.id)) {
            return { tasks: currentTasks };
          }
          return { tasks: [savedTask, ...currentTasks] };
        });
        queryClient.setQueryData<{ lists: NativeZimbraTaskList[] }>(["native-zimbra-task-lists"], (current) => {
          if (!current) return current;
          return {
            lists: current.lists.map((list) =>
              list.id === savedFolderId ? { ...list, total_count: Math.max(0, (list.total_count ?? 0) + 1) } : list
            ),
          };
        });
      }
      setNotice({ tone: "success", message: "Task saved." });
      setEditingTask(null);
      setDialogOpen(false);
      setDraft(DEFAULT_DRAFT);
      await queryClient.invalidateQueries({ queryKey: ["native-zimbra-tasks", selectedTaskListId] });
      await queryClient.invalidateQueries({ queryKey: ["native-zimbra-task-lists"] });
    },
    onError: (error) => {
      setNotice({ tone: "error", message: error instanceof Error ? error.message : t("task.saveFailed") });
    },
  });

  const deleteTask = useMutation({
    mutationFn: ({ taskId, folderId }: { taskId: string; folderId: string }) => api.deleteNativeZimbraTask(taskId, folderId),
    onSuccess: async () => {
      setNotice({ tone: "success", message: "Task deleted." });
      await queryClient.invalidateQueries({ queryKey: ["native-zimbra-tasks", selectedTaskListId] });
    },
    onError: (error) => {
      setNotice({ tone: "error", message: error instanceof Error ? error.message : t("task.deleteFailed") });
    },
  });
  const loadTaskForEdit = useMutation({
    mutationFn: (task: NativeZimbraTask) => api.nativeZimbraTask(task.id, task.folder_id || selectedTaskListId),
    onSuccess: (response, fallbackTask) => {
      openEditor(mergeTaskDetail(fallbackTask, response.task));
    },
    onError: (error) => {
      setNotice({ tone: "info", message: error instanceof Error ? error.message : "Loaded available task details." });
    },
  });

  const toggleComplete = useMutation({
    mutationFn: async (task: NativeZimbraTask) => {
      const complete = task.percent_complete >= 100 || task.status === "COMPLETED";
      return api.updateNativeZimbraTask(task.id, {
        folder_id: task.folder_id,
        invite_id: task.invite_id,
        subject: task.subject,
        location: task.location,
        description: task.description,
        due: task.due,
        start: task.start,
        completed: complete ? "" : new Date().toISOString(),
        status: complete ? "NEEDS-ACTION" : "COMPLETED",
        percent_complete: complete ? 0 : 100,
        priority: task.priority,
        reminder_minutes: task.reminder_minutes,
      });
    },
    onSuccess: async () => {
      await queryClient.invalidateQueries({ queryKey: ["native-zimbra-tasks", selectedTaskListId] });
    },
  });
  const shareTask = useMutation({
    mutationFn: async (payload: { email: string; permissions: string }) =>
      api.nativeZimbraShareTask({ resource_id: shareTaskListId || selectedTaskListId, ...payload }),
    onSuccess: async () => {
      setShareEmail("");
      await queryClient.invalidateQueries({ queryKey: ["native-zimbra-task-shares"] });
      await queryClient.invalidateQueries({ queryKey: ["native-zimbra-task-shares", shareTaskListId || selectedTaskListId] });
      setShareTaskListId("");
    },
  });
  const revokeTaskShare = useMutation({
    mutationFn: async (payload: { email: string }) => api.nativeZimbraRevokeTaskShare({ resource_id: shareTaskListId || selectedTaskListId, ...payload }),
    onSuccess: async () => {
      await queryClient.invalidateQueries({ queryKey: ["native-zimbra-task-shares"] });
      await queryClient.invalidateQueries({ queryKey: ["native-zimbra-task-shares", shareTaskListId || selectedTaskListId] });
      setShareTaskListId("");
    },
  });
  const createTaskList = useMutation({
    mutationFn: api.createNativeZimbraTaskList,
    onSuccess: async (response) => {
      setNewTaskListName("");
      setSelectedTaskListId(response.list.id);
      setNotice({ tone: "success", message: `Task list "${response.list.name}" created.` });
      await queryClient.invalidateQueries({ queryKey: ["native-zimbra-task-lists"] });
    },
    onError: (error) => {
      setNotice({ tone: "error", message: error instanceof Error ? error.message : t("task.createListFailed") });
    },
  });
  const renameTaskList = useMutation({
    mutationFn: ({ listId, name }: { listId: string; name: string }) => api.renameNativeZimbraTaskList(listId, { name }),
    onSuccess: async () => {
      setRenameTaskListId("");
      setRenameTaskListName("");
      setNotice({ tone: "success", message: "Task list renamed." });
      await queryClient.invalidateQueries({ queryKey: ["native-zimbra-task-lists"] });
      await queryClient.invalidateQueries({ queryKey: ["native-zimbra-tasks", selectedTaskListId] });
    },
    onError: (error) => {
      setNotice({ tone: "error", message: error instanceof Error ? error.message : "Unable to rename task list." });
    },
  });
  const updateTaskListColor = useMutation({
    mutationFn: ({ listId, color, rgb }: { listId: string; color: number; rgb: string }) => api.updateNativeZimbraTaskListColor(listId, { color, rgb }),
    onSuccess: async () => {
      setNotice({ tone: "success", message: "Task list color updated." });
      await queryClient.invalidateQueries({ queryKey: ["native-zimbra-task-lists"] });
    },
    onError: (error) => {
      setNotice({ tone: "error", message: error instanceof Error ? error.message : "Unable to update task list color." });
    },
  });
  const deleteTaskList = useMutation({
    mutationFn: api.deleteNativeZimbraTaskList,
    onSuccess: async () => {
      setActionMenuTaskListId("");
      setColorTaskListId("");
      setRenameTaskListId("");
      setNotice({ tone: "success", message: "Task list deleted." });
      await queryClient.invalidateQueries({ queryKey: ["native-zimbra-task-lists"] });
      await queryClient.invalidateQueries({ queryKey: ["native-zimbra-tasks"] });
      setSelectedTaskListId("");
    },
    onError: (error) => {
      setNotice({ tone: "error", message: error instanceof Error ? error.message : "Unable to delete task list." });
    },
  });

  useEffect(() => {
    const lists = taskLists.data?.lists ?? [];
    if (!lists.length) {
      if (selectedTaskListId) setSelectedTaskListId("");
      return;
    }
    if (!selectedTaskListId || !lists.some((list) => list.id === selectedTaskListId)) {
      setSelectedTaskListId(lists[0].id);
    }
  }, [selectedTaskListId, taskLists.data?.lists]);

  const selectedTaskList = useMemo(
    () => (taskLists.data?.lists ?? []).find((list) => list.id === selectedTaskListId) ?? null,
    [selectedTaskListId, taskLists.data?.lists]
  );
  const selectedTaskListLabel = selectedTaskList?.name || "Task";
  const selectedTaskListWritable = canWriteNativeShare(selectedTaskList);
  const activeTaskEditorWritable = editingTask ? canWriteTaskFolder(editingTask, taskLists.data?.lists ?? [], selectedTaskList) : selectedTaskListWritable;

  const sortedTasks = useMemo(() => {
    const items = [...(tasks.data?.tasks ?? [])];
    items.sort((left, right) => {
      const leftDone = left.percent_complete >= 100 || left.status === "COMPLETED";
      const rightDone = right.percent_complete >= 100 || right.status === "COMPLETED";
      if (leftDone !== rightDone) return leftDone ? 1 : -1;
      return (left.due || left.subject).localeCompare(right.due || right.subject);
    });
    return items;
  }, [tasks.data?.tasks]);

  function beginCreate() {
    if (!selectedTaskListWritable) return;
    setEditingTask(null);
    setDraft(DEFAULT_DRAFT);
    setNotice(null);
    setDialogOpen(true);
  }

  function beginEdit(task: NativeZimbraTask) {
    if (!canWriteTaskFolder(task, taskLists.data?.lists ?? [], selectedTaskList)) return;
    setNotice(null);
    openEditor(task);
    void loadTaskForEdit.mutateAsync(task);
  }

  function openEditor(task: NativeZimbraTask) {
    const reminderAt = task.reminder_at || task.reminderAt || "";
    const reminderEnabled = task.reminder_minutes > 0 || Boolean(reminderAt);
    setEditingTask(task);
    setDraft({
      subject: task.subject,
      location: task.location,
      description: normalizeTaskDetailHtml(task.description),
      due: toDateInputValue(task.due),
      start: toDateInputValue(task.start),
      completed: toDatetimeLocalValue(task.completed),
      reminder_date: toDateInputValue(reminderAt || task.completed || task.due || task.start),
      reminder_time: toTimeInputValue(reminderAt || task.completed) || "19:00",
      status: task.status,
      percent_complete: task.percent_complete,
      priority: task.priority,
      reminder_minutes: reminderEnabled ? task.reminder_minutes || 15 : 0,
    });
    setDialogOpen(true);
  }

  return (
    <>
    <section className={clsx("min-h-0 overflow-auto p-2 lg:p-3", !active && "hidden")} aria-hidden={!active}>
      <div className="control-surface rounded-xl p-3 sm:p-3">
        <div className="mb-4 flex flex-wrap items-center justify-between gap-3">
          <div>
            <p className="text-xs font-black uppercase text-[var(--accent)]">{serviceLabel}</p>
            <h2 className="text-xl font-black">{t("task.workspace")}</h2>
            <p className="mt-2 text-xs font-semibold text-slate-400">
              {t("task.tasksLoaded", { count: tasks.data?.tasks.length ?? selectedTaskList?.total_count ?? 0, list: selectedTaskListLabel.toLowerCase() })}
            </p>
          </div>
          <div className="flex w-full flex-wrap items-center gap-2 sm:w-auto sm:gap-3">
            <button type="button" onClick={beginCreate} disabled={!selectedTaskListWritable} className="theme-button h-11 w-full px-4 transition hover:-translate-y-0.5 disabled:cursor-not-allowed disabled:opacity-60 sm:h-10 sm:w-auto">
              <Plus size={16} />
              {t("task.newTask")}
            </button>
            <label className="flex h-11 min-w-0 flex-1 items-center gap-2 rounded-lg border border-slate-200 bg-white px-3 text-sm font-semibold text-slate-600 sm:h-10 sm:min-w-[220px]">
              <span className="shrink-0 text-slate-400">{t("task.taskList")}</span>
              <select
                value={selectedTaskListId}
                onChange={(event) => setSelectedTaskListId(event.target.value)}
                className="min-w-0 flex-1 bg-transparent outline-none"
                disabled={taskLists.isLoading || !(taskLists.data?.lists.length ?? 0)}
              >
                {!taskLists.data?.lists.length ? <option value="">{t("task.noTaskListsFound")}</option> : null}
                {(taskLists.data?.lists ?? []).map((list: NativeZimbraTaskList) => (
                  <option key={list.id} value={list.id}>
                    {list.name}
                    {list.is_system ? ` (${t("task.primary")})` : ""}
                    {typeof list.total_count === "number" ? ` • ${list.total_count}` : ""}
                  </option>
                ))}
              </select>
            </label>
            <button
              type="button"
              onClick={() => {
                void taskLists.refetch();
                void tasks.refetch();
              }}
              disabled={taskLists.isFetching || tasks.isFetching}
              className="icon-button h-10 w-10"
              aria-label={t("task.refreshLists")}
              title={t("task.refreshLists")}
            >
              <RefreshCw size={16} className={clsx((taskLists.isFetching || tasks.isFetching) && "animate-spin")} />
            </button>
          </div>
        </div>

        {notice ? (
          <div
            className={clsx(
              "mb-4 flex items-start justify-between gap-3 rounded-lg border px-4 py-3 text-sm font-semibold",
              notice.tone === "success"
                ? "border-[color-mix(in_srgb,var(--accent)_20%,white)] bg-[color-mix(in_srgb,var(--accent)_8%,white)] text-[var(--accent)]"
                : notice.tone === "info"
                  ? "border-slate-200 bg-slate-50 text-slate-700"
                  : "border-red-200 bg-red-50 text-red-900"
            )}
          >
            <span>{notice.message}</span>
            <button type="button" onClick={() => setNotice(null)} className="text-current/70 transition hover:text-current" aria-label="Dismiss notice">
              <X size={16} />
            </button>
          </div>
        ) : null}

        {statusLoading ? (
          <div className="rounded-xl border border-slate-200 bg-slate-50 p-4">
            <h3 className="text-lg font-black text-slate-900">{t("calendar.loading", { service: serviceLabel })}</h3>
          </div>
        ) : null}
        {!statusLoading && !status?.configured ? (
          <div className="rounded-xl border border-dashed border-slate-200 bg-slate-50 p-4">
            <h3 className="text-lg font-black text-slate-900">{t("calendar.notConfigured", { service: serviceLabel })}</h3>
          </div>
        ) : null}
        {!statusLoading && status?.oauth_configured && !status.connected ? (
          <div className="rounded-xl border border-[color-mix(in_srgb,var(--accent)_16%,white)] bg-[linear-gradient(135deg,rgba(236,253,245,0.95)_0%,rgba(240,249,255,0.95)_100%)] p-4">
            <div className="flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between">
              <div>
                <h3 className="text-lg font-black text-slate-900">{t("calendar.connectTitle", { service: serviceLabel })}</h3>
              </div>
              <button type="button" onClick={onConnect} disabled={connectPending || !onConnect} className="theme-button h-11 px-5 transition hover:-translate-y-0.5 disabled:opacity-50">
                {connectPending ? t("calendar.connecting") : t("calendar.connect", { service: serviceLabel })}
              </button>
            </div>
          </div>
        ) : null}

        {canLoadTasks ? <div className="grid gap-3 xl:grid-cols-[minmax(0,1.72fr)_320px]">
          <div className="min-h-0 rounded-xl border border-slate-200 bg-slate-50 p-3">
          {tasks.isLoading ? <p className="p-4 text-sm font-semibold text-slate-500">{t("task.loading")}</p> : null}
          {!tasks.isLoading && !sortedTasks.length ? (
            <div className="grid place-items-center gap-2 rounded-xl border border-dashed border-slate-200 bg-white p-8 text-center">
              <CheckSquare2 size={28} className="text-slate-300" />
              <p className="text-sm font-bold text-slate-900">{t("task.empty")}</p>
            </div>
          ) : null}
          <div className="grid gap-2">
            {sortedTasks.map((task) => {
              const done = task.percent_complete >= 100 || task.status === "COMPLETED";
              const priority = taskPriorityMeta(task.priority, t);
              const taskWritable = canWriteTaskFolder(task, taskLists.data?.lists ?? [], selectedTaskList);
              return (
                <div key={task.id} className={clsx("rounded-xl border px-3 py-2.5 transition", done ? "border-emerald-200 bg-emerald-50/60" : "border-slate-200 bg-white")}>
                  <div className="flex items-start gap-2.5">
                    <button
                      type="button"
                      className={clsx("grid h-10 w-10 shrink-0 place-items-center rounded-full transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--accent)] focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50", done ? "text-emerald-600" : "text-slate-400 hover:bg-slate-100")}
                      onClick={() => void toggleComplete.mutateAsync(task)}
                      disabled={!taskWritable || toggleComplete.isPending}
                      title={done ? t("task.markIncomplete") : t("task.markComplete")}
                      aria-label={done ? "Mark task incomplete" : "Mark task complete"}
                    >
                      <span className={clsx("grid h-5 w-5 place-items-center rounded-full border", done ? "border-emerald-500 bg-emerald-500 text-white" : "border-slate-300 bg-white text-transparent")}>
                        <CheckSquare2 size={13} />
                      </span>
                    </button>
                    <div className="min-w-0 flex-1">
                      <div className="flex flex-wrap items-center gap-2">
                        <button
                          type="button"
                          onClick={() => beginEdit(task)}
                          disabled={!taskWritable}
                          className={clsx(
                            "min-w-0 max-w-full truncate rounded-md px-1 py-0.5 text-left text-sm font-black transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--accent)] focus-visible:ring-offset-2 disabled:cursor-not-allowed",
                            done ? "text-emerald-900 line-through" : "text-slate-950",
                            taskWritable ? "hover:text-[var(--accent)]" : "opacity-70"
                          )}
                          title={taskWritable ? t("task.edit") : undefined}
                          aria-label={`${t("task.edit")}: ${task.subject || t("task.untitled")}`}
                        >
                          {task.subject || t("task.untitled")}
                        </button>
                        <span className="rounded-full bg-slate-100 px-2 py-0.5 text-[11px] font-black uppercase tracking-[0.08em] text-slate-500">
                          {done ? t("task.completed") : task.status.toLowerCase()}
                        </span>
                        {priority ? (
                          <span className={clsx("inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-[11px] font-black uppercase tracking-[0.08em]", priority.className)} title={priority.label}>
                            {priority.kind === "high" ? <ArrowUp size={12} /> : <ArrowDown size={12} />}
                            {priority.label}
                          </span>
                        ) : null}
                      </div>
                      <p className="mt-1 text-xs font-semibold text-slate-500">
                        {task.due ? (
                          <span className="inline-flex items-center gap-1.5"><CalendarDays size={13} /> {formatTaskDate(task.due)}</span>
                        ) : (
                          <span className="inline-flex items-center gap-1.5"><Clock3 size={13} /> {t("task.noDueDate")}</span>
                        )}
                        {task.percent_complete ? <span className="ml-2">{t("task.percentComplete", { percent: task.percent_complete })}</span> : null}
                      </p>
                    </div>
                    <div className="flex items-center gap-1">
                      <button type="button" className="icon-button h-9 w-9 disabled:cursor-not-allowed disabled:opacity-50" onClick={() => beginEdit(task)} disabled={!taskWritable} title={t("task.edit")}>
                        <Pencil size={16} />
                      </button>
                      <button
                        type="button"
                        className="icon-button h-9 w-9 text-red-700 disabled:cursor-not-allowed disabled:opacity-50"
                        onClick={() => setDeleteConfirmTask(task)}
                        disabled={!taskWritable || deleteTask.isPending}
                        title={t("task.delete")}
                      >
                        <Trash2 size={16} />
                      </button>
                    </div>
                  </div>
                </div>
              );
            })}
          </div>
          </div>

          <aside className="hidden content-start gap-3 rounded-xl border border-slate-200 bg-white p-3 xl:grid">
            <div className="mb-1 flex items-center gap-2">
              <CheckSquare2 size={18} className="text-[var(--accent)]" />
              <div>
                <p className="text-xs font-black uppercase tracking-[0.12em] text-slate-400">{t("task.tasks")}</p>
                <h3 className="text-lg font-black text-slate-900">{t("task.lists")}</h3>
              </div>
            </div>
            <form
              className="flex flex-wrap gap-2"
              onSubmit={(event) => {
                event.preventDefault();
                const name = newTaskListName.trim();
                if (!name) return;
                void createTaskList.mutateAsync({ name });
              }}
            >
              <input
                value={newTaskListName}
                onChange={(event) => setNewTaskListName(event.target.value)}
                className="field h-10 min-w-0 flex-1 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--accent)] focus-visible:ring-offset-2"
                placeholder={t("task.newListName")}
              />
              <button type="submit" className="theme-button h-10 px-4 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--accent)] focus-visible:ring-offset-2" disabled={!newTaskListName.trim() || createTaskList.isPending}>
                <Plus size={16} />
                {createTaskList.isPending ? t("task.creating") : t("task.newList")}
              </button>
            </form>
            <div className="grid gap-3">
              {(taskLists.data?.lists ?? []).map((list) => {
                const selected = selectedTaskListId === list.id;
                const mountedShare = isMountedTaskList(list);
                const listWritable = canWriteNativeShare(list);
                return (
                  <div
                    key={list.id}
                    className={clsx(
                      "rounded-xl border px-3 py-3 transition",
                      selected || shareTaskListId === list.id ? "border-[color-mix(in_srgb,var(--accent)_22%,white)] bg-[color-mix(in_srgb,var(--accent)_6%,white)]" : "border-slate-200 bg-slate-50"
                    )}
                  >
                    <div className="flex items-start justify-between gap-3">
                      <button
                        type="button"
                        onClick={() => {
                          setSelectedTaskListId(list.id);
                          setActionMenuTaskListId("");
                          setColorTaskListId("");
                          setRenameTaskListId("");
                        }}
                        className="min-w-0 flex-1 rounded-md text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--accent)] focus-visible:ring-offset-2"
                      >
                        <div className="flex items-center gap-2">
                          <span className="h-3 w-3 shrink-0 rounded-full border border-black/5" style={{ backgroundColor: taskListColor(list) }} />
                          {mountedShare ? <FolderSymlink size={15} className="shrink-0 text-[var(--accent)]" aria-label={t("sharing.sharedWithMe")} /> : null}
                          <p className="truncate text-sm font-black text-slate-900">{list.name}</p>
                          {list.is_system ? <span className="rounded-full bg-white px-2 py-0.5 text-[10px] font-black uppercase tracking-[0.08em] text-slate-500">{t("calendar.primary")}</span> : null}
                        </div>
                        <p className="mt-1 text-xs font-semibold text-slate-500">{taskListAccessRoleLabel(list, t)} · {t("task.count", { count: list.total_count })}</p>
                      </button>
                      <div>
                        <button
                          type="button"
                          onClick={() => {
                            setActionMenuTaskListId((current) => (current === list.id ? "" : list.id));
                            setShareTaskListId("");
                            setColorTaskListId("");
                            setRenameTaskListId("");
                          }}
                          className="theme-button-soft h-8 px-2.5 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--accent)] focus-visible:ring-offset-2"
                          aria-label={`Task list actions for ${list.name}`}
                          aria-haspopup="menu"
                          aria-expanded={actionMenuTaskListId === list.id}
                        >
                          <MoreHorizontal size={15} />
                        </button>
                      </div>
                    </div>
                    {actionMenuTaskListId === list.id ? (
                      <div role="menu" className="mt-3 rounded-lg border border-slate-200 bg-white p-1.5">
                        <div className="px-3 py-2 text-[11px] font-black uppercase tracking-[0.08em] text-slate-400">{t("calendar.manage")}</div>
                        {!list.is_system && !mountedShare ? (
                          <button
                            type="button"
                            role="menuitem"
                            onClick={() => {
                              setActionMenuTaskListId("");
                              setColorTaskListId("");
                              setRenameTaskListId(list.id);
                              setRenameTaskListName(list.name);
                            }}
                            className="flex h-9 w-full items-center gap-2 rounded-md px-3 text-sm font-semibold text-slate-700 transition hover:bg-slate-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--accent)] focus-visible:ring-inset"
                          >
                            <Pencil size={14} />
                            {t("calendar.rename")}
                          </button>
                        ) : null}
                        <button
                          type="button"
                          role="menuitem"
                          onClick={() => {
                            setActionMenuTaskListId("");
                            setRenameTaskListId("");
                            setColorTaskListId((current) => (current === list.id ? "" : list.id));
                          }}
                          className="flex h-9 w-full items-center gap-2 rounded-md px-3 text-sm font-semibold text-slate-700 transition hover:bg-slate-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--accent)] focus-visible:ring-inset"
                        >
                          <span className="h-3 w-3 rounded-full border border-black/5" style={{ backgroundColor: taskListColor(list) }} />
                          {t("calendar.changeColor")}
                        </button>
                        {listWritable ? (
                          <>
                            <div className="px-3 py-2 text-[11px] font-black uppercase tracking-[0.08em] text-slate-400">{t("task.access")}</div>
                            <button
                              type="button"
                              role="menuitem"
                              onClick={() => {
                                setActionMenuTaskListId("");
                                setShareTaskListId(list.id);
                                setShareEmail("");
                                void queryClient.invalidateQueries({ queryKey: ["native-zimbra-task-shares", list.id] });
                              }}
                              className="flex h-9 w-full items-center gap-2 rounded-md px-3 text-sm font-semibold text-slate-700 transition hover:bg-slate-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--accent)] focus-visible:ring-inset"
                            >
                              <Share2 size={14} />
                              {t("task.shareList")}
                            </button>
                          </>
                        ) : null}
                        {!list.is_system && !mountedShare ? (
                          <>
                            <div className="my-1 border-t border-slate-100" />
                            <div className="px-3 py-2 text-[11px] font-black uppercase tracking-[0.08em] text-slate-400">{t("calendar.danger")}</div>
                            <button
                              type="button"
                              role="menuitem"
                              onClick={() => {
                                setActionMenuTaskListId("");
                                if (!window.confirm(`Delete task list "${list.name}"?`)) return;
                                void deleteTaskList.mutateAsync(list.id);
                              }}
                              className="flex h-9 w-full items-center gap-2 rounded-md px-3 text-sm font-semibold text-red-600 transition hover:bg-red-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-red-300 focus-visible:ring-inset"
                            >
                              <Trash2 size={14} />
                              {t("common.delete")}
                            </button>
                          </>
                        ) : null}
                      </div>
                    ) : null}
                    {!list.is_system && renameTaskListId === list.id ? (
                      <form
                        className="mt-3 flex flex-wrap gap-2"
                        onSubmit={(event) => {
                          event.preventDefault();
                          const nextName = renameTaskListName.trim();
                          if (!nextName || nextName === list.name) {
                            setRenameTaskListId("");
                            setRenameTaskListName("");
                            return;
                          }
                          void renameTaskList.mutateAsync({ listId: list.id, name: nextName });
                        }}
                      >
                        <input
                          value={renameTaskListName}
                          onChange={(event) => setRenameTaskListName(event.target.value)}
                          className="field h-10 min-w-0 flex-1 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--accent)] focus-visible:ring-offset-2"
                          placeholder={t("task.newListName")}
                          autoFocus
                        />
                        <button type="submit" className="theme-button h-10 px-3 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--accent)] focus-visible:ring-offset-2" disabled={!renameTaskListName.trim() || renameTaskList.isPending}>
                          {t("common.save")}
                        </button>
                        <button
                          type="button"
                          className="theme-button-soft h-10 px-3 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--accent)] focus-visible:ring-offset-2"
                          onClick={() => {
                            setRenameTaskListId("");
                            setRenameTaskListName("");
                          }}
                          disabled={renameTaskList.isPending}
                        >
                          {t("common.cancel")}
                        </button>
                      </form>
                    ) : null}
                    {colorTaskListId === list.id ? (
                      <div className="mt-3 flex flex-wrap gap-1.5">
                        {taskListColorOptions.map((option) => {
                          const colorSelected = taskListColorMatches(list, option.value, option.hex);
                          return (
                            <button
                              key={`${list.id}:${option.value}`}
                              type="button"
                              onClick={() => void updateTaskListColor.mutateAsync({ listId: list.id, color: option.value, rgb: option.hex })}
                              disabled={updateTaskListColor.isPending}
                              className={clsx(
                                "grid h-7 w-7 place-items-center rounded-full border transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--accent)] focus-visible:ring-offset-2",
                                updateTaskListColor.isPending ? "cursor-not-allowed border-slate-100 opacity-50" : colorSelected ? "scale-105 border-slate-900" : "border-slate-200 hover:border-slate-400"
                              )}
                              style={{ backgroundColor: option.hex }}
                              aria-label={`Set ${list.name} color to ${option.label}`}
                              title={option.label}
                            >
                              {colorSelected ? <span className="text-xs font-black text-slate-950">✓</span> : null}
                            </button>
                          );
                        })}
                      </div>
                    ) : null}
                  </div>
                );
              })}
              {!taskLists.isLoading && !(taskLists.data?.lists.length ?? 0) ? <p className="rounded-lg bg-slate-50 p-4 text-sm font-semibold text-slate-500">{t("task.noListsAvailable")}</p> : null}
            </div>
          </aside>
        </div> : null}
      </div>
    </section>
      {deleteConfirmTask ? (
        <TaskDeleteConfirmDialog
          title="Delete task?"
          message="This removes the task from this task list."
          confirmLabel="Delete"
          busy={deleteTask.isPending}
          onClose={() => setDeleteConfirmTask(null)}
          onConfirm={async () => {
            const task = deleteConfirmTask;
            if (!task) return;
            try {
              await deleteTask.mutateAsync({ taskId: task.id, folderId: task.folder_id });
              setDeleteConfirmTask(null);
            } catch {
              // Mutation onError surfaces the notice; keep the dialog open for retry/cancel.
            }
          }}
        />
      ) : null}
      {dialogOpen ? (
        <TaskEditorDialog
          t={t}
          mode={editingTask ? "edit" : "create"}
          draft={draft}
          taskLists={taskLists.data?.lists ?? []}
          selectedTaskListId={selectedTaskListId}
          busy={saveTask.isPending}
          detailsLoading={loadTaskForEdit.isPending && Boolean(editingTask)}
          onDraftChange={setDraft}
          onClose={() => {
            setDialogOpen(false);
            setEditingTask(null);
            setDraft(DEFAULT_DRAFT);
          }}
          onSubmit={() => {
            if (!activeTaskEditorWritable) return;
            void saveTask.mutateAsync({
              taskId: editingTask?.id,
              data: draft,
            });
          }}
        />
      ) : null}
      {shareTaskListId ? (
        <div className="fixed inset-0 z-40 grid place-items-center bg-slate-950/35 p-4">
          <div role="dialog" aria-modal="true" aria-label={t("task.shareList")} className="w-full max-w-xl rounded-2xl border border-slate-200 bg-white p-4 shadow-2xl">
            <div className="flex items-start justify-between gap-3">
              <div className="min-w-0">
                <p className="text-xs font-black uppercase tracking-[0.12em] text-slate-400">{t("task.access")}</p>
                <h3 className="mt-1 truncate text-lg font-black text-slate-900">{t("task.shareList")}</h3>
                {shareTaskList ? <p className="mt-1 truncate text-sm font-semibold text-slate-500">{shareTaskList.name}</p> : null}
              </div>
              <button type="button" onClick={() => setShareTaskListId("")} className="theme-button-soft h-9 px-3 text-sm">
                <X size={14} />
              </button>
            </div>
            <form
              className="mt-4 grid gap-3"
              onSubmit={(event) => {
                event.preventDefault();
                const email = shareEmail.trim();
                if (!email) return;
                void shareTask.mutateAsync({ email, permissions: sharePermissions.trim() || "r" });
              }}
            >
              <ShareRecipientAutocomplete
                value={shareEmail}
                onChange={setShareEmail}
                contacts={contacts}
                label={t("dialog.emailAddress")}
                autoFocus
                disabled={shareTask.isPending}
              />
              <div className="flex flex-wrap gap-2">
                <select value={sharePermissions} onChange={(event) => setSharePermissions(event.target.value)} className="field h-11 min-w-[160px] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--accent)] focus-visible:ring-offset-2">
                  <option value="r">{t("dialog.viewer")}</option>
                  <option value="rwidx">{t("dialog.manager")}</option>
                </select>
                <span className={clsx("inline-flex h-11 items-center rounded-full px-3 text-xs font-black uppercase tracking-[0.08em]", nativeSharePermissionBadgeClass(sharePermissions || "r"))}>
                  {nativeSharePermissionLabel(sharePermissions || "r", t)}
                </span>
                <button type="submit" className="theme-button h-11 px-4 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--accent)] focus-visible:ring-offset-2" disabled={!shareEmail.trim() || shareTask.isPending}>
                  <Share2 size={14} />
                  {shareTask.isPending ? t("common.sharing") : t("common.share")}
                </button>
              </div>
            </form>
            <div className="mt-4 grid gap-2">
              <div className="flex items-center justify-between gap-3">
                <p className="text-sm font-black text-slate-900">{t("dialog.currentShares")}</p>
                <button type="button" className="text-xs font-bold text-[var(--accent)] disabled:opacity-50" onClick={() => void taskShares.refetch()} disabled={taskShares.isFetching}>
                  {t("common.refresh")}
                </button>
              </div>
              {taskShares.isLoading ? <p className="rounded-lg bg-slate-50 p-4 text-sm font-semibold text-slate-500">{t("dialog.loadingShares")}</p> : null}
              {!taskShares.isLoading && !visibleTaskShares.length ? <p className="rounded-lg bg-slate-50 p-4 text-sm font-semibold text-slate-500">{t("task.noSharesYet")}</p> : null}
              {visibleTaskShares.map((share: NativeZimbraShareEntry) => (
                <div key={`${share.email}:${share.permissions}`} className="flex flex-wrap items-center justify-between gap-2 rounded-xl border border-slate-200 bg-slate-50 px-3 py-3">
                  <div className="min-w-0">
                    <p className="truncate text-sm font-bold text-slate-900">{share.display_name || share.email || t("dialog.sharedAccess")}</p>
                    <div className="mt-1 flex flex-wrap items-center gap-2">
                      <span className={clsx("inline-flex items-center rounded-full px-2 py-0.5 text-[11px] font-black uppercase tracking-[0.08em]", nativeShareStateBadgeClass(share.status || "pending"))}>
                        {nativeShareStatusLabel(share.status || "pending", t)}
                      </span>
                      <span className={clsx("inline-flex items-center rounded-full px-2 py-0.5 text-[11px] font-black uppercase tracking-[0.08em]", nativeSharePermissionBadgeClass(share.permissions || "r"))}>
                        {nativeSharePermissionLabel(share.permissions || "r", t)}
                      </span>
                      <span className="text-xs font-semibold text-slate-500">{share.email || t("calendar.unknownRecipient")}</span>
                    </div>
                  </div>
                  <button type="button" className="theme-button-soft h-9 px-3 text-sm text-red-700 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-red-300 focus-visible:ring-offset-2" disabled={revokeTaskShare.isPending || !share.email} onClick={() => void revokeTaskShare.mutateAsync({ email: share.email })}>
                    <Trash2 size={14} />
                    {t("common.revoke")}
                  </button>
                </div>
              ))}
            </div>
          </div>
        </div>
      ) : null}
    </>
  );
}


function TaskDeleteConfirmDialog({
  title,
  message,
  confirmLabel,
  busy,
  onClose,
  onConfirm,
}: {
  title: string;
  message: string;
  confirmLabel: string;
  busy: boolean;
  onClose: () => void;
  onConfirm: () => void | Promise<void>;
}) {
  const dialogRef = useRef<HTMLDivElement | null>(null);

  useEffect(() => {
    const previous = document.activeElement instanceof HTMLElement ? document.activeElement : null;
    const firstButton = dialogRef.current?.querySelector<HTMLButtonElement>("button");
    firstButton?.focus();
    function onKeyDown(event: KeyboardEvent) {
      if (event.key === "Escape") onClose();
    }
    window.addEventListener("keydown", onKeyDown);
    return () => {
      window.removeEventListener("keydown", onKeyDown);
      previous?.focus();
    };
  }, [onClose]);

  return (
    <div className="fixed inset-0 z-[10001] grid place-items-center bg-slate-950/35 p-4 backdrop-blur-sm">
      <div ref={dialogRef} role="dialog" aria-modal="true" aria-label={title} className="w-full max-w-md rounded-xl border border-slate-200 bg-white p-5 shadow-soft">
        <h3 className="text-lg font-black text-slate-900">{title}</h3>
        <p className="mt-2 text-sm leading-6 text-slate-600">{message}</p>
        <div className="mt-5 flex flex-wrap justify-end gap-2">
          <button type="button" onClick={onClose} className="theme-button-soft h-10 px-4 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--accent)] focus-visible:ring-offset-2" disabled={busy}>Cancel</button>
          <button type="button" onClick={() => void onConfirm()} className="h-10 rounded-lg bg-red-600 px-4 text-sm font-bold text-white hover:bg-red-500 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-red-300 focus-visible:ring-offset-2" disabled={busy}>
            {busy ? "Working…" : confirmLabel}
          </button>
        </div>
      </div>
    </div>
  );
}
function TaskEditorDialog({
  t,
  mode,
  draft,
  taskLists,
  selectedTaskListId,
  busy,
  detailsLoading = false,
  onDraftChange,
  onClose,
  onSubmit,
}: {
  t: ReturnType<typeof createTranslator>;
  mode: "create" | "edit";
  draft: TaskDraft;
  taskLists: NativeZimbraTaskList[];
  selectedTaskListId: string;
  busy: boolean;
  detailsLoading?: boolean;
  onDraftChange: (next: TaskDraft | ((current: TaskDraft) => TaskDraft)) => void;
  onClose: () => void;
  onSubmit: () => void;
}) {
  const dialogRef = useRef<HTMLFormElement | null>(null);
  const [submitError, setSubmitError] = useState("");
  const [minimized, setMinimized] = useState(false);
  const selectedList = taskLists.find((list) => list.id === selectedTaskListId);
  const detailLength = draft.description.length;
  const detailRemaining = TASK_DETAIL_MAX_CHARS - detailLength;
  const detailTooLong = detailRemaining < 0;
  const disabled = busy || detailsLoading || !selectedTaskListId || !draft.subject.trim() || detailTooLong;
  const dialogTitle = mode === "edit" ? t("task.editTask") : t("task.newTask");
  const onCloseRef = useRef(onClose);

  useEffect(() => {
    onCloseRef.current = onClose;
  }, [onClose]);

  useEffect(() => {
    const previous = document.activeElement instanceof HTMLElement ? document.activeElement : null;
    const firstInput = dialogRef.current?.querySelector<HTMLInputElement>("input[name='task-subject']");
    firstInput?.focus();
    function onKeyDown(event: KeyboardEvent) {
      if (event.key === "Escape") onCloseRef.current();
    }
    window.addEventListener("keydown", onKeyDown);
    return () => {
      window.removeEventListener("keydown", onKeyDown);
      previous?.focus();
    };
  }, []);

  function update(patch: Partial<TaskDraft>) {
    setSubmitError("");
    onDraftChange((current) => ({ ...current, ...patch }));
  }

  const fieldClass = "field h-9 w-full rounded border-slate-300 bg-white text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--accent)] focus-visible:ring-offset-1";
  const draftPriority = taskPriorityMeta(draft.priority || "5", t);

  if (minimized) {
    return (
      <div className="pointer-events-none fixed inset-0 z-[10000] flex items-end justify-end p-3 sm:p-4">
        <button
          type="button"
          onClick={() => setMinimized(false)}
          className="pointer-events-auto flex min-h-12 w-[min(calc(100vw-1.5rem),26rem)] items-center justify-between gap-3 rounded-2xl border border-slate-200 bg-white px-4 py-3 text-left shadow-[0_20px_70px_-28px_rgba(15,23,42,0.55)] transition hover:border-[var(--accent)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--accent)] focus-visible:ring-offset-2"
          aria-label="Restore task draft"
          title="Restore task draft"
        >
          <span className="min-w-0">
            <span className="block text-xs font-black uppercase tracking-[0.12em] text-[var(--accent)]">{dialogTitle}</span>
            <span className="block truncate text-sm font-semibold text-slate-700">{draft.subject.trim() || "Task draft"}</span>
          </span>
          <Maximize2 size={17} className="shrink-0 text-slate-500" />
        </button>
      </div>
    );
  }

  return (
    <div className="fixed inset-0 z-[10000] grid place-items-end bg-slate-950/30 p-0 sm:place-items-center sm:p-4">
      <form
        ref={dialogRef}
        role="dialog"
        aria-modal="true"
        aria-label={dialogTitle}
        className="grid h-[100dvh] w-full max-w-none grid-rows-[auto_minmax(0,1fr)] overflow-hidden rounded-none border border-slate-200 bg-white shadow-soft sm:h-auto sm:max-h-[94vh] sm:max-w-6xl sm:rounded-xl"
        onClick={(event) => event.stopPropagation()}
        onSubmit={(event) => {
          event.preventDefault();
          if (!selectedTaskListId) {
            setSubmitError(t("task.chooseListError"));
            return;
          }
          if (!draft.subject.trim()) {
            setSubmitError(t("task.subjectRequired"));
            return;
          }
          if (draft.start.trim() && !draft.due.trim()) {
            setSubmitError(t("task.dueRequiredWithStart"));
            return;
          }
          if (draft.description.length > TASK_DETAIL_MAX_CHARS) {
            setSubmitError(t("task.detailsTooLong", { count: formatCount(draft.description.length - TASK_DETAIL_MAX_CHARS) }));
            return;
          }
          onSubmit();
        }}
      >
        <div className="flex items-center justify-between border-b border-slate-200 px-4 py-3 sm:px-5">
          <div className="min-w-0">
            <p className="text-xs font-black uppercase text-slate-500">Task</p>
            <h3 className="truncate text-lg font-black text-slate-950">{dialogTitle}</h3>
            <p className="mt-1 truncate text-sm font-medium text-slate-500">{selectedList?.name || t("task.chooseList")}</p>
          </div>
          <div className="flex items-center gap-2">
            <button type="button" onClick={() => setMinimized(true)} className="hidden h-9 items-center gap-1.5 rounded-lg border border-transparent px-3 text-xs font-bold text-slate-600 transition hover:bg-slate-100 hover:text-slate-900 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--accent)] focus-visible:ring-offset-2 sm:inline-flex" aria-label="Minimize task draft" title="Minimize task draft">
              <Minimize2 size={15} />
              <span>Minimize</span>
            </button>
            <button type="button" onClick={onClose} className="icon-button h-10 w-10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--accent)] focus-visible:ring-offset-2" aria-label={t("task.closeDialog")}>
              <X size={17} />
            </button>
          </div>
        </div>

        <div className="grid min-h-0 grid-rows-[auto_minmax(320px,1fr)_auto] overflow-auto">
          <section className="grid gap-3 border-b border-slate-200 p-4 sm:p-5">
            <h3 className="text-sm font-bold text-slate-800">{t("calendar.details")}</h3>
            <div className="grid gap-3 lg:grid-cols-12">
              <label className="grid gap-1 lg:col-span-12">
                <span className="text-sm font-semibold text-slate-700">* {t("task.subject")}</span>
                <input
                  name="task-subject"
                  value={draft.subject}
                  onChange={(event) => update({ subject: event.target.value })}
                  className={`${fieldClass} bg-yellow-50/70`}
                  placeholder={t("task.subjectPlaceholder")}
                />
              </label>
              <label className="grid gap-1 lg:col-span-7">
                <span className="text-sm font-semibold text-slate-700">{t("task.location")}</span>
                <input
                  name="task-location"
                  value={draft.location}
                  onChange={(event) => update({ location: event.target.value })}
                  className={fieldClass}
                  placeholder={t("task.locationPlaceholder")}
                />
              </label>
              <label className="grid gap-1 lg:col-span-5">
                <span className="text-sm font-semibold text-slate-700">{t("task.priority")}</span>
                <div className="flex min-w-0 items-center gap-2">
                  <select value={draft.priority || "5"} onChange={(event) => update({ priority: event.target.value })} className={fieldClass}>
                    <option value="1">{t("task.high")}</option>
                    <option value="5">{t("task.normal")}</option>
                    <option value="9">{t("task.low")}</option>
                  </select>
                  {draftPriority ? (
                    <span className={clsx("inline-flex h-8 shrink-0 items-center gap-1 rounded-md border px-2 text-xs font-black", draftPriority.className)} title={draftPriority.label}>
                      {draftPriority.kind === "high" ? <ArrowUp size={14} /> : <ArrowDown size={14} />}
                      {draftPriority.label}
                    </span>
                  ) : null}
                </div>
              </label>
              <label className="grid gap-1 lg:col-span-5">
                <span className="text-sm font-semibold text-slate-700">{t("task.status")}</span>
                <div className="grid min-w-0 grid-cols-[minmax(0,1fr)_64px] gap-2">
                  <select value={draft.status} onChange={(event) => update(statusDraftPatch(event.target.value, draft.percent_complete))} className={fieldClass}>
                    <option value="NEEDS-ACTION">{t("task.notStarted")}</option>
                    <option value="COMPLETED">{t("task.completed")}</option>
                    <option value="IN-PROGRESS">{t("task.inProgress")}</option>
                    <option value="WAITING">{t("task.waiting")}</option>
                    <option value="DEFERRED">{t("task.deferred")}</option>
                  </select>
                  <div className="flex items-center justify-center rounded border border-slate-300 bg-white px-2 text-sm font-semibold text-slate-700">{draft.percent_complete}%</div>
                </div>
              </label>
              <label className="grid gap-1 lg:col-span-7">
                <span className="text-sm font-semibold text-slate-700">{t("task.complete")}</span>
                <input type="range" min={0} max={100} step={1} value={draft.percent_complete} onChange={(event) => update(progressDraftPatch(Number(event.target.value)))} className="mt-2 min-w-0 accent-[var(--accent)]" />
              </label>
              <label className="grid gap-1 sm:col-span-1 lg:col-span-3">
                <span className="text-sm font-semibold text-slate-700">{t("task.startDate")}</span>
                <input value={draft.start} onChange={(event) => update({ start: event.target.value })} type="date" className={fieldClass} />
              </label>
              <label className="grid gap-1 sm:col-span-1 lg:col-span-3">
                <span className="text-sm font-semibold text-slate-700">{t("task.dueDate")}</span>
                <input value={draft.due} onChange={(event) => update({ due: event.target.value })} type="date" className={fieldClass} />
              </label>
              <div className="grid gap-1 lg:col-span-6">
                <span className="text-sm font-semibold text-slate-700">{t("task.reminder")}</span>
                <div className="grid min-w-0 grid-cols-[auto_minmax(0,1fr)_minmax(0,1fr)] gap-2">
                  <input type="checkbox" checked={draft.reminder_minutes > 0} onChange={(event) => update(event.target.checked ? enableReminderPatch() : { reminder_minutes: 0 })} className="mt-2 h-4 w-4 accent-[var(--accent)]" />
                  <input value={draft.reminder_date} onChange={(event) => update({ reminder_date: event.target.value })} type="date" disabled={draft.reminder_minutes <= 0} className={`${fieldClass} min-w-0`} />
                  <input value={draft.reminder_time} onChange={(event) => update({ reminder_time: event.target.value })} type="time" disabled={draft.reminder_minutes <= 0} className={`${fieldClass} min-w-0`} />
                </div>
              </div>
            </div>
          </section>

          <div className="grid min-h-[320px] grid-rows-[auto_minmax(0,1fr)] border-b border-slate-200 p-4 sm:min-h-[460px] sm:p-5">
            <div className="mb-2 flex flex-wrap items-center justify-between gap-2">
              <label className="text-sm font-bold text-slate-700">{t("calendar.description")}</label>
              <span id="task-details-counter" className={clsx("text-xs font-bold", detailTooLong ? "text-red-600" : "text-slate-500")}>
                {t("task.detailsCounter", { current: formatCount(detailLength), max: formatCount(TASK_DETAIL_MAX_CHARS), remaining: formatCount(Math.max(0, detailRemaining)) })}
              </span>
            </div>
            <TaskRichDetailEditor
              value={draft.description}
              onChange={(description) => update({ description })}
              className={clsx("field min-h-0 w-full overflow-auto p-3 text-sm leading-6 whitespace-pre-wrap outline-none empty:before:text-slate-400 empty:before:content-[attr(data-placeholder)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--accent)] focus-visible:ring-offset-2 [&_a]:text-[var(--accent)] [&_a]:underline [&_blockquote]:my-3 [&_blockquote]:border-l-4 [&_blockquote]:border-slate-300 [&_blockquote]:pl-3 [&_code]:rounded [&_code]:bg-slate-100 [&_code]:px-1 [&_code]:py-0.5 [&_h1]:mt-4 [&_h1]:text-lg [&_h1]:font-black [&_h2]:mt-4 [&_h2]:text-base [&_h2]:font-black [&_h3]:mt-3 [&_h3]:font-black [&_li]:ml-5 [&_li]:list-item [&_ol]:my-3 [&_ol]:list-decimal [&_ol]:pl-5 [&_p]:my-3 [&_pre]:my-3 [&_pre]:overflow-auto [&_pre]:rounded-lg [&_pre]:bg-slate-100 [&_pre]:p-3 [&_pre]:whitespace-pre [&_table]:my-3 [&_table]:w-full [&_table]:border-collapse [&_td]:border [&_td]:border-slate-200 [&_td]:p-2 [&_th]:border [&_th]:border-slate-200 [&_th]:bg-slate-100 [&_th]:p-2 [&_ul]:my-3 [&_ul]:list-disc [&_ul]:pl-5", detailTooLong && "border-red-300 focus-visible:ring-red-300")}
              placeholder={t("task.detailsPlaceholder")}
            />
            {detailTooLong ? <p className="mt-2 text-xs font-semibold text-red-600">{t("task.detailsTooLong", { count: formatCount(detailLength - TASK_DETAIL_MAX_CHARS) })}</p> : null}
          </div>

          <div className="flex flex-col gap-3 border-t border-slate-200 bg-slate-50 px-4 py-3 sm:flex-row sm:items-center sm:justify-between sm:px-5">
            <div className="min-h-5 text-sm font-bold text-red-600">{submitError}</div>
            <div className="grid grid-cols-2 gap-2 sm:flex sm:justify-end">
              <button type="button" onClick={onClose} className="h-10 rounded-lg border border-slate-200 bg-white px-4 text-sm font-bold text-slate-700 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--accent)] focus-visible:ring-offset-2" disabled={busy}>{t("common.cancel")}</button>
              <button type="submit" disabled={disabled} className="theme-button h-10 px-4 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--accent)] focus-visible:ring-offset-2 disabled:opacity-50">
                {busy ? t("common.saving") : mode === "edit" ? t("calendar.saveChanges") : t("task.create")}
              </button>
            </div>
          </div>
        </div>
      </form>
    </div>
  );
}

function formatTaskDate(value: string) {
  const parsed = new Date(value);
  if (Number.isNaN(parsed.getTime())) {
    return value;
  }
  return parsed.toLocaleString([], {
    dateStyle: "medium",
  });
}

function TaskRichDetailEditor({ value, onChange, className, placeholder }: { value: string; onChange: (value: string) => void; className: string; placeholder: string }) {
  const editorRef = useRef<HTMLDivElement | null>(null);
  const selectionRef = useRef<Range | null>(null);

  useEffect(() => {
    const editor = editorRef.current;
    if (!editor || document.activeElement === editor) return;
    const normalizedValue = normalizeTaskDetailHtml(value);
    if (editor.innerHTML !== normalizedValue) {
      editor.innerHTML = normalizedValue;
    }
  }, [value]);

  function saveSelection() {
    const editor = editorRef.current;
    const selection = window.getSelection();
    if (!editor || !selection?.rangeCount) return;
    const range = selection.getRangeAt(0);
    if (editor.contains(range.commonAncestorContainer)) {
      selectionRef.current = range.cloneRange();
    }
  }

  function restoreSelection() {
    const editor = editorRef.current;
    const range = selectionRef.current;
    editor?.focus();
    if (!range) return;
    const selection = window.getSelection();
    selection?.removeAllRanges();
    selection?.addRange(range);
  }

  function syncEditor() {
    const editor = editorRef.current;
    if (!editor) return;
    onChange(sanitizeTaskDetailHtml(editor.innerHTML));
    saveSelection();
  }

  function format(command: string, commandValue?: string) {
    restoreSelection();
    if (command === "insertUnorderedList" && !editorRef.current?.innerText.trim()) {
      document.execCommand("insertHTML", false, "<ul><li><br></li></ul>");
      syncEditor();
      return;
    }
    if (command === "insertOrderedList" && !editorRef.current?.innerText.trim()) {
      document.execCommand("insertHTML", false, "<ol><li><br></li></ol>");
      syncEditor();
      return;
    }
    document.execCommand(command, false, commandValue);
    syncEditor();
  }

  function addLink() {
    const href = window.prompt("Link URL");
    if (!href) return;
    const clean = href.trim();
    if (!/^(https?:|mailto:|tel:)/i.test(clean)) return;
    format("createLink", clean);
  }

  function handlePaste(event: ClipboardEvent<HTMLDivElement>) {
    const html = event.clipboardData.getData("text/html");
    const text = event.clipboardData.getData("text/plain");
    if (!html && !text) return;
    event.preventDefault();
    if (html) {
      document.execCommand("insertHTML", false, sanitizeTaskDetailHtml(html));
    } else {
      document.execCommand("insertText", false, text);
    }
    syncEditor();
  }

  return (
    <div className="grid min-h-0 grid-rows-[auto_minmax(0,1fr)] overflow-hidden rounded-xl border border-slate-200 bg-white">
      <div className="flex flex-wrap items-center gap-1 border-b border-slate-200 bg-slate-50 px-2 py-2">
        <TaskFormatButton label="Bold" onClick={() => format("bold")} icon={<Bold size={16} />} />
        <TaskFormatButton label="Italic" onClick={() => format("italic")} icon={<Italic size={16} />} />
        <TaskFormatButton label="Underline" onClick={() => format("underline")} icon={<Underline size={16} />} />
        <TaskFormatButton label="Bulleted list" onClick={() => format("insertUnorderedList")} icon={<List size={16} />} />
        <TaskFormatButton label="Numbered list" onClick={() => format("insertOrderedList")} icon={<ListOrdered size={16} />} />
        <TaskFormatButton label="Insert link" onClick={addLink} icon={<Link size={16} />} />
        <TaskColorButton label="Font color" icon={<Palette size={16} />} onChange={(color) => format("foreColor", color)} />
        <TaskColorButton label="Highlight color" icon={<Highlighter size={16} />} onChange={(color) => format("hiliteColor", color)} />
        <TaskFormatButton label="Clear formatting" onClick={() => format("removeFormat")} icon={<Eraser size={16} />} />
      </div>
      <div
        ref={editorRef}
        contentEditable
        suppressContentEditableWarning
        onInput={syncEditor}
        onMouseUp={saveSelection}
        onKeyUp={saveSelection}
        onPaste={handlePaste}
        className={className}
        data-placeholder={placeholder}
        aria-describedby="task-details-counter"
        role="textbox"
      />
    </div>
  );
}

function TaskFormatButton({ label, icon, onClick }: { label: string; icon: ReactNode; onClick: () => void }) {
  return (
    <button
      type="button"
      title={label}
      aria-label={label}
      onMouseDown={(event) => event.preventDefault()}
      onClick={onClick}
      className="inline-flex h-8 min-w-8 items-center justify-center rounded-lg border border-slate-200 bg-white px-2 text-slate-600 transition hover:border-[var(--accent)] hover:text-[var(--accent)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--accent)] focus-visible:ring-offset-1"
    >
      {icon}
    </button>
  );
}

function TaskColorButton({ label, icon, onChange }: { label: string; icon: ReactNode; onChange: (color: string) => void }) {
  return (
    <label
      className="relative inline-flex h-8 min-w-8 cursor-pointer items-center justify-center rounded-lg border border-slate-200 bg-white px-2 text-slate-600 transition hover:border-[var(--accent)] hover:text-[var(--accent)] focus-within:ring-2 focus-within:ring-[var(--accent)] focus-within:ring-offset-1"
      aria-label={label}
      title={label}
    >
      {icon}
      <input
        type="color"
        className="absolute inset-0 h-full w-full cursor-pointer opacity-0"
        onMouseDown={(event) => event.preventDefault()}
        onChange={(event) => onChange(event.target.value)}
      />
    </label>
  );
}

function sanitizeTaskDetailHtml(value: string) {
  if (typeof document === "undefined") return value;
  const template = document.createElement("template");
  template.innerHTML = value;
  template.content.querySelectorAll("script,style,link,meta,base,noscript,iframe,object,embed,form,input,button,select,textarea").forEach((node) => node.remove());
  template.content.querySelectorAll("*").forEach((node) => {
    const element = node as HTMLElement;
    const colorAttr = element.getAttribute("color") || "";
    const bgColorAttr = element.getAttribute("bgcolor") || "";
    const attrStyle = [
      safeCssColor(colorAttr) ? `color: ${colorAttr.trim()}` : "",
      safeCssColor(bgColorAttr) ? `background-color: ${bgColorAttr.trim()}` : "",
    ].filter(Boolean).join("; ");
    for (const attribute of Array.from(element.attributes)) {
      const name = attribute.name.toLowerCase();
      const rawValue = attribute.value || "";
      if (name.startsWith("on") || name === "srcdoc") {
        element.removeAttribute(attribute.name);
        continue;
      }
      if ((name === "href" || name === "src") && rawValue.trim() && !/^(https?:|mailto:|tel:|data:image\/)/i.test(rawValue.trim())) {
        element.removeAttribute(attribute.name);
        continue;
      }
      if (name === "style") {
        const safeStyle = [sanitizeTaskDetailStyle(rawValue), attrStyle].filter(Boolean).join("; ");
        if (safeStyle) {
          element.setAttribute("style", safeStyle);
        } else {
          element.removeAttribute(attribute.name);
        }
        continue;
      }
      if (name === "color" || name === "bgcolor" || name === "face" || name === "size") {
        element.removeAttribute(attribute.name);
      }
    }
    if (attrStyle && !element.getAttribute("style")) {
      element.setAttribute("style", attrStyle);
    }
  });
  return template.innerHTML;
}

function sanitizeTaskDetailStyle(value: string) {
  const safe: string[] = [];
  for (const declaration of value.split(";")) {
    const [rawName, ...rawParts] = declaration.split(":");
    const name = (rawName || "").trim().toLowerCase();
    const rawValue = rawParts.join(":").trim();
    if (!rawValue || !["color", "background", "background-color"].includes(name)) continue;
    if (safeCssColor(rawValue)) {
      safe.push(`${name === "background" ? "background-color" : name}: ${rawValue}`);
    }
  }
  return safe.join("; ");
}

function safeCssColor(value: string) {
  const text = (value || "").trim();
  return /^#[0-9a-f]{3,8}$/i.test(text) || /^rgba?\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}(?:\s*,\s*(?:0|1|0?\.\d+))?\s*\)$/i.test(text) || /^(?:black|silver|gray|white|maroon|red|purple|fuchsia|green|lime|olive|yellow|navy|blue|teal|aqua|orange|transparent)$/i.test(text);
}

function normalizeTaskDetailHtml(value: string) {
  if (typeof document === "undefined") return value;
  const text = value || "";
  const htmlTagPattern = /\b(?:html|body|p|br|div|ul|ol|li|strong|b|em|i|u|pre|code|blockquote|a|span|table|thead|tbody|tr|td|th|h[1-6])\b/i;
  const escapedHtmlPattern = /&lt;\/?(?:html|body|p|br|div|ul|ol|li|strong|b|em|i|u|pre|code|blockquote|a|span|table|thead|tbody|tr|td|th|h[1-6])\b/i;
  let candidate = text;
  for (let pass = 0; pass < 3 && escapedHtmlPattern.test(candidate); pass += 1) {
    candidate = decodeHtmlEntities(candidate);
  }
  const hasLiteralHtml = /</.test(candidate) && htmlTagPattern.test(candidate);
  if (!hasLiteralHtml) return sanitizeTaskDetailHtml(candidate);
  const parser = new DOMParser();
  const parsed = parser.parseFromString(candidate, "text/html");
  const bodyHtml = parsed.body?.innerHTML || candidate;
  return sanitizeTaskDetailHtml(bodyHtml);
}

function decodeHtmlEntities(value: string) {
  const textarea = document.createElement("textarea");
  textarea.innerHTML = value;
  return textarea.value;
}

function formatCount(value: number) {
  return new Intl.NumberFormat().format(value);
}

function hasShareRecipient(share: { email?: string }) {
  return Boolean((share.email || "").trim());
}

function taskPriorityMeta(priority: string, t: ReturnType<typeof createTranslator>) {
  const normalized = (priority || "").trim();
  if (normalized === "1") {
    return {
      kind: "high" as const,
      label: t("task.high"),
      className: "border-red-200 bg-red-50 text-red-700",
    };
  }
  if (normalized === "9") {
    return {
      kind: "low" as const,
      label: t("task.low"),
      className: "border-sky-200 bg-sky-50 text-sky-700",
    };
  }
  return null;
}

function taskDescriptionIsTruncated(value: string) {
  const text = (value || "").trim();
  return Boolean(text) && (text.endsWith("...") || text.endsWith("…"));
}

function mergeTaskDetail(fallback: NativeZimbraTask, detail?: NativeZimbraTask) {
  if (!detail) return fallback;
  const detailDescription = (detail.description || "").trim();
  const fallbackDescription = (fallback.description || "").trim();
  return {
    ...fallback,
    ...detail,
    folder_id: detail.folder_id || fallback.folder_id,
    folder_name: detail.folder_name || fallback.folder_name,
    subject: detail.subject || fallback.subject,
    location: detail.location || fallback.location,
    description: detailDescription && (!taskDescriptionIsTruncated(detailDescription) || taskDescriptionIsTruncated(fallbackDescription))
      ? detail.description
      : fallback.description,
    due: detail.due || fallback.due,
    start: detail.start || fallback.start,
    completed: detail.completed || fallback.completed,
    reminder_at: detail.reminder_at || detail.reminderAt || fallback.reminder_at || fallback.reminderAt || "",
    status: detail.status || fallback.status,
    priority: detail.priority || fallback.priority,
    html_link: detail.html_link || fallback.html_link,
    percent_complete: typeof detail.percent_complete === "number" ? detail.percent_complete : fallback.percent_complete,
    reminder_minutes: typeof detail.reminder_minutes === "number" ? detail.reminder_minutes : fallback.reminder_minutes,
  };
}

function isMountedTaskList(list: NativeZimbraTaskList) {
  return Boolean((list.share_owner_email || list.share_owner_id || list.remote_id || "").trim()) || Boolean((list.permissions || "").trim());
}

function taskListAccessRoleLabel(list: NativeZimbraTaskList, t: ReturnType<typeof createTranslator>) {
  if (isMountedTaskList(list)) {
    return nativeSharePermissionLabel(list.permissions || "r", t);
  }
  return "owner";
}

function canWriteNativeShare(resource: { share_owner_email?: string; share_owner_id?: string; remote_id?: string; permissions?: string } | null | undefined) {
  if (!resource) return true;
  const mounted = Boolean((resource.share_owner_email || resource.share_owner_id || resource.remote_id || "").trim()) || Boolean((resource.permissions || "").trim());
  if (!mounted) return true;
  return nativeSharePermissionsAllowWrite(resource.permissions || "");
}

function canWriteTaskFolder(task: NativeZimbraTask, lists: NativeZimbraTaskList[], selectedList: NativeZimbraTaskList | null) {
  const taskFolderId = task.folder_id || selectedList?.id || "";
  const taskList = lists.find((list) => list.id === taskFolderId) ?? selectedList;
  return canWriteNativeShare(taskList);
}

function taskListColor(list: NativeZimbraTaskList) {
  return (list.rgb || taskListColorOptions.find((option) => option.value === list.color)?.hex || "#94a3b8").trim();
}

function taskListColorMatches(list: NativeZimbraTaskList, color: number, rgb: string) {
  const currentRgb = (list.rgb || "").trim().toLowerCase();
  const nextRgb = rgb.trim().toLowerCase();
  if (currentRgb && nextRgb) return currentRgb === nextRgb;
  return (list.color ?? 0) === color;
}

function toDatetimeLocalValue(value: string) {
  const parsed = parseTaskDateValue(value);
  if (!parsed) return normalizeCompactTaskDate(value, true);
  const year = parsed.getFullYear();
  const month = String(parsed.getMonth() + 1).padStart(2, "0");
  const day = String(parsed.getDate()).padStart(2, "0");
  const hours = String(parsed.getHours()).padStart(2, "0");
  const minutes = String(parsed.getMinutes()).padStart(2, "0");
  return `${year}-${month}-${day}T${hours}:${minutes}`;
}

function toTimeInputValue(value: string) {
  const parsed = parseTaskDateValue(value);
  if (!parsed) return normalizeCompactTaskTime(value);
  const hours = String(parsed.getHours()).padStart(2, "0");
  const minutes = String(parsed.getMinutes()).padStart(2, "0");
  return `${hours}:${minutes}`;
}

function toDateInputValue(value: string) {
  const parsed = parseTaskDateValue(value);
  if (!parsed) return normalizeCompactTaskDate(value, false);
  const year = parsed.getFullYear();
  const month = String(parsed.getMonth() + 1).padStart(2, "0");
  const day = String(parsed.getDate()).padStart(2, "0");
  return `${year}-${month}-${day}`;
}

function oneHourFromNowInputValues() {
  const reminder = new Date(Date.now() + 60 * 60 * 1000);
  const year = reminder.getFullYear();
  const month = String(reminder.getMonth() + 1).padStart(2, "0");
  const day = String(reminder.getDate()).padStart(2, "0");
  const hours = String(reminder.getHours()).padStart(2, "0");
  const minutes = String(reminder.getMinutes()).padStart(2, "0");
  return {
    date: `${year}-${month}-${day}`,
    time: `${hours}:${minutes}`,
  };
}

function enableReminderPatch(): Partial<TaskDraft> {
  const reminder = oneHourFromNowInputValues();
  return {
    reminder_minutes: 15,
    reminder_date: reminder.date,
    reminder_time: reminder.time,
  };
}

function nativeShareStatusLabel(value: string, t: ReturnType<typeof createTranslator>) {
  const normalized = (value || "").trim().toLowerCase();
  if (normalized === "accepted") return t("calendar.accepted");
  if (normalized === "active") return t("sharing.active");
  if (normalized === "declined") return t("calendar.declined");
  if (normalized === "revoked") return "Revoked";
  return "Pending";
}

function nativeShareStateBadgeClass(value: string) {
  const normalized = (value || "").trim().toLowerCase();
  if (normalized === "accepted" || normalized === "active") return "border border-emerald-200 bg-emerald-50 text-emerald-800";
  if (normalized === "revoked" || normalized === "declined") return "border border-rose-200 bg-rose-50 text-rose-800";
  return "border border-amber-200 bg-amber-50 text-amber-800";
}

function nativeSharePermissionLabel(value: string, t: ReturnType<typeof createTranslator>) {
  return isNativeShareManagerPermission(value) ? t("dialog.manager") : t("dialog.viewer");
}

function nativeSharePermissionBadgeClass(value: string) {
  return isNativeShareManagerPermission(value)
    ? "bg-[color-mix(in_srgb,var(--accent)_10%,white)] text-[var(--accent)]"
    : "bg-slate-100 text-slate-500";
}

function isNativeShareManagerPermission(value: string) {
  return nativeSharePermissionsAllowWrite(value);
}

function nativeSharePermissionsAllowWrite(value: string) {
  const normalized = (value || "").trim().toLowerCase();
  if (!normalized || ["r", "read", "reader", "view", "viewer"].includes(normalized)) {
    return false;
  }
  return (
    normalized === "owner" ||
    normalized === "manager" ||
    normalized === "admin" ||
    normalized === "rwidx" ||
    normalized === "rwid" ||
    normalized === "rwd" ||
    normalized === "rw" ||
    normalized.includes("w") ||
    normalized.includes("d") ||
    normalized.includes("i") ||
    normalized.includes("x")
  );
}

function taskReminderAt(draft: TaskDraft) {
  if (draft.reminder_minutes <= 0 || !draft.reminder_date || !draft.reminder_time) return "";
  return `${draft.reminder_date}T${draft.reminder_time}`;
}

function statusDraftPatch(status: string, currentPercent: number): Partial<TaskDraft> {
  return { status, percent_complete: currentPercent };
}

function progressDraftPatch(percent: number): Partial<TaskDraft> {
  return { percent_complete: percent, status: "IN-PROGRESS" };
}

function parseTaskDateValue(value: string) {
  const clean = (value || "").trim();
  if (!clean) return null;
  const compact = clean.match(/^(\d{4})(\d{2})(\d{2})(?:T?(\d{2})(\d{2})(\d{2})?)?/);
  if (compact) {
    const [, year, month, day, hour = "00", minute = "00", second = "00"] = compact;
    return new Date(Number(year), Number(month) - 1, Number(day), Number(hour), Number(minute), Number(second));
  }
  const numeric = clean.match(/^\d{10,13}$/);
  if (numeric) {
    const epoch = Number(clean);
    return new Date(clean.length === 13 ? epoch : epoch * 1000);
  }
  const parsed = new Date(clean);
  return Number.isNaN(parsed.getTime()) ? null : parsed;
}

function normalizeCompactTaskDate(value: string, includeTime: boolean) {
  const clean = (value || "").trim();
  const dashedDate = clean.match(/^(\d{4})-(\d{2})-(\d{2})(?:[T ](\d{2}):?(\d{2}))?/);
  if (dashedDate) {
    const [, year, month, day, hour = "00", minute = "00"] = dashedDate;
    return includeTime ? `${year}-${month}-${day}T${hour}:${minute}` : `${year}-${month}-${day}`;
  }
  return includeTime ? clean.slice(0, 16) : clean.slice(0, 10);
}

function normalizeCompactTaskTime(value: string) {
  const clean = (value || "").trim();
  const time = clean.match(/(?:T| )?(\d{2}):?(\d{2})(?::?\d{2})?/);
  return time ? `${time[1]}:${time[2]}` : "";
}
