import { Head, Link, router, useForm } from "@inertiajs/react";
import {
    ArrowLeft,
    Inbox as InboxIcon,
    Mail,
    MessageSquare,
    Paperclip,
    Plus,
    Search,
    Send,
    UserPlus,
    Users,
    X,
} from "lucide-react";
import { useEffect, useRef, useState } from "react";
import { EmptyState, PageHeader } from "@/components/crm/page-header";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import {
    Dialog,
    DialogContent,
    DialogDescription,
    DialogFooter,
    DialogHeader,
    DialogTitle,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { useInitials } from "@/hooks/use-initials";
import { formatDate, formatDateTime } from "@/lib/format";
import { cn } from "@/lib/utils";
import {
    index as messagesIndex,
    reply as replyToThread,
    show as showThread,
    store as storeThread,
} from "@/routes/messages";
import { store as addParticipants } from "@/routes/messages/participants";
import type {
    InboxPageProps,
    InboxThread,
    MessagePerson,
    MessageThreadSummary,
} from "@/types";

/**
 * The staff inbox.
 *
 * Two panes on a wide screen, one at a time on a phone: the conversations
 * waiting for you, and the one you opened. The list refreshes itself while the
 * tab is visible so a reply from a colleague arrives without a page reload —
 * there is no websocket here, and a poll every twelve seconds is the honest
 * version of "live" for an office CRM.
 */
export default function InboxPage({
    threads,
    thread,
    search,
    staff,
    can,
    external_enabled,
}: InboxPageProps) {
    const [composing, setComposing] = useState(false);
    const [addingPeople, setAddingPeople] = useState(false);
    const [filter, setFilter] = useState(search ?? "");

    useEffect(() => {
        setFilter(search ?? "");
    }, [search]);

    useEffect(() => {
        const timer = window.setInterval(() => {
            if (document.visibilityState !== "visible") {
                return;
            }

            router.reload({
                // The sidebar count rides along, so it falls as the conversation
                // is read rather than waiting for the next full page load.
                only: thread
                    ? ["threads", "thread", "inbox"]
                    : ["threads", "inbox"],
            });
        }, 12_000);

        return () => window.clearInterval(timer);
    }, [thread]);

    const submitSearch = (event: React.FormEvent) => {
        event.preventDefault();

        router.get(
            messagesIndex().url,
            { search: filter || undefined },
            { preserveState: true, preserveScroll: true, replace: true },
        );
    };

    return (
        <>
            <Head title="Inbox" />

            <div className="flex flex-col gap-5">
                <PageHeader
                    eyebrow={
                        <>
                            <InboxIcon className="size-3.5 text-solar" />
                            <span>Team</span>
                        </>
                    }
                    title="Inbox"
                    description="Conversations between the people who work here, kept with the job you are talking about. Anyone on a conversation sees the whole thread, so a handover does not need forwarding."
                    actions={
                        <Button size="sm" onClick={() => setComposing(true)}>
                            <Plus className="size-4" />
                            New message
                        </Button>
                    }
                />

                <div className="grid min-h-[34rem] gap-4 lg:grid-cols-[21rem_minmax(0,1fr)]">
                    <aside
                        className={cn(
                            "flex flex-col gap-3 rounded-xl bg-card p-3 shadow-card ring-1 ring-hairline",
                            thread && "hidden lg:flex",
                        )}
                    >
                        <form onSubmit={submitSearch} className="relative">
                            <Search className="pointer-events-none absolute top-2.5 left-2.5 size-4 text-muted-foreground" />
                            <Input
                                value={filter}
                                onChange={(event) =>
                                    setFilter(event.target.value)
                                }
                                placeholder="Search conversations"
                                className="h-9 pl-8"
                                aria-label="Search conversations"
                            />
                        </form>

                        <div className="crm-scroll flex min-h-0 flex-1 flex-col gap-1 overflow-y-auto">
                            {threads.length === 0 ? (
                                <EmptyState
                                    title={
                                        search
                                            ? "Nothing matches that search"
                                            : "No conversations yet"
                                    }
                                    description={
                                        search
                                            ? "Try a different word, or start a new message."
                                            : "Start one with the button above — a colleague, a group, or a customer outside the company if your administrator allows it."
                                    }
                                    icon={MessageSquare}
                                />
                            ) : null}

                            {threads.map((item) => (
                                <ThreadRow
                                    key={item.id}
                                    thread={item}
                                    active={thread?.id === item.id}
                                />
                            ))}
                        </div>
                    </aside>

                    <section className="flex min-w-0 flex-col">
                        {thread ? (
                            <Conversation
                                key={thread.id}
                                thread={thread}
                                canEmail={can.email}
                                externalEnabled={external_enabled}
                                onAddPeople={() => setAddingPeople(true)}
                            />
                        ) : (
                            <div className="flex h-full flex-col items-center justify-center gap-3 rounded-xl bg-card px-6 py-16 text-center shadow-card ring-1 ring-hairline">
                                <span className="flex size-12 items-center justify-center rounded-full bg-solar-soft text-solar-ink ring-1 ring-solar/25">
                                    <Mail className="size-5" />
                                </span>
                                <div>
                                    <p className="text-sm font-semibold">
                                        Pick a conversation
                                    </p>
                                    <p className="mx-auto mt-1 max-w-sm text-xs leading-relaxed text-muted-foreground">
                                        Everything you are part of is on the
                                        left. Starting a new one keeps the
                                        subject, the people and the replies
                                        together.
                                    </p>
                                </div>
                                <Button
                                    size="sm"
                                    variant="outline"
                                    onClick={() => setComposing(true)}
                                >
                                    <Plus className="size-4" />
                                    New message
                                </Button>
                            </div>
                        )}
                    </section>
                </div>
            </div>

            <NewConversationDialog
                open={composing}
                onClose={() => setComposing(false)}
                staff={staff}
                canExternal={can.external}
                externalEnabled={external_enabled}
            />

            {thread ? (
                <AddPeopleDialog
                    open={addingPeople}
                    onClose={() => setAddingPeople(false)}
                    thread={thread}
                    staff={staff}
                />
            ) : null}
        </>
    );
}

function ThreadRow({
    thread,
    active,
}: {
    thread: MessageThreadSummary;
    active: boolean;
}) {
    return (
        <Link
            href={showThread(thread.id).url}
            preserveScroll
            className={cn(
                "group flex gap-2.5 rounded-lg px-2.5 py-2.5 transition-colors",
                active
                    ? "bg-solar-soft ring-1 ring-solar/25"
                    : "hover:bg-accent/50",
            )}
        >
            {" "}
            <span className="mt-0.5 flex size-8 shrink-0 items-center justify-center rounded-lg bg-muted text-muted-foreground">
                {thread.external ? (
                    <Mail className="size-3.5" />
                ) : (
                    <Users className="size-3.5" />
                )}
            </span>
            <div className="min-w-0 flex-1">
                <div className="flex items-baseline gap-2">
                    <p
                        className={cn(
                            "truncate text-sm",
                            thread.unread > 0 ? "font-semibold" : "font-medium",
                        )}
                    >
                        {thread.subject}
                    </p>
                    <span className="ml-auto shrink-0 text-[10px] text-muted-foreground">
                        {relativeTime(thread.last_message_at)}
                    </span>
                </div>

                <p className="truncate text-[11px] text-muted-foreground">
                    {thread.participants
                        .map((person) => person.name)
                        .join(", ")}
                    {thread.external
                        ? ` · ${thread.external.name ?? thread.external.email}`
                        : ""}
                </p>

                <div className="mt-0.5 flex items-center gap-1.5">
                    {thread.preview ? (
                        <p className="truncate text-xs text-muted-foreground">
                            {thread.last_sender
                                ? `${thread.last_sender}: `
                                : ""}
                            {thread.preview}
                        </p>
                    ) : null}

                    {thread.external ? (
                        <Mail
                            className="ml-auto size-3 shrink-0 text-muted-foreground"
                            aria-label="Includes an outside contact"
                        />
                    ) : null}

                    {thread.unread > 0 ? (
                        <span
                            className={cn(
                                "flex min-w-4 shrink-0 items-center justify-center rounded-full bg-solar px-1 text-[10px] leading-4 font-semibold text-neutral-900",
                                thread.external ? "" : "ml-auto",
                            )}
                        >
                            {thread.unread > 9 ? "9+" : thread.unread}
                        </span>
                    ) : null}
                </div>
            </div>
        </Link>
    );
}

function Conversation({
    thread,
    canEmail,
    externalEnabled,
    onAddPeople,
}: {
    thread: InboxThread;
    canEmail: boolean;
    externalEnabled: boolean;
    onAddPeople: () => void;
}) {
    const getInitials = useInitials();
    const scrollRef = useRef<HTMLDivElement>(null);

    const form = useForm({
        body: "",
        send_email: false,
    });

    useEffect(() => {
        const element = scrollRef.current;

        if (element) {
            element.scrollTop = element.scrollHeight;
        }
    }, [thread.messages.length]);

    const submit = () => {
        form.post(replyToThread(thread.id).url, {
            preserveScroll: true,
            onSuccess: () => form.reset(),
        });
    };

    return (
        <div className="flex min-h-0 flex-1 flex-col rounded-xl bg-card shadow-card ring-1 ring-hairline">
            <header className="flex flex-col gap-2 border-b border-hairline px-4 py-3.5 sm:px-5">
                <div className="flex items-start gap-2">
                    <Link
                        href={messagesIndex().url}
                        className="mt-0.5 text-muted-foreground hover:text-foreground lg:hidden"
                        aria-label="Back to the inbox"
                    >
                        <ArrowLeft className="size-4" />
                    </Link>
                    <div className="min-w-0 flex-1">
                        <h2 className="truncate text-sm font-semibold">
                            {thread.subject}
                        </h2>
                        <p className="mt-0.5 flex flex-wrap items-center gap-x-2 gap-y-0.5 text-xs text-muted-foreground">
                            <Users className="size-3.5" />
                            {thread.participants
                                .map((person) => person.name)
                                .join(", ")}
                        </p>
                    </div>

                    <Button size="sm" variant="outline" onClick={onAddPeople}>
                        <UserPlus className="size-3.5" />
                        Add people
                    </Button>
                </div>

                {thread.external ? (
                    <div className="flex flex-wrap items-center gap-2 rounded-lg bg-muted/40 px-2.5 py-1.5 text-[11px] text-muted-foreground">
                        <Mail className="size-3.5" />
                        <span>
                            Outside contact:{" "}
                            <span className="font-medium text-foreground">
                                {thread.external.name ?? thread.external.email}
                            </span>{" "}
                            · {thread.external.email}
                        </span>
                    </div>
                ) : null}
            </header>

            <div
                ref={scrollRef}
                className="crm-scroll flex min-h-[18rem] flex-1 flex-col gap-3 overflow-y-auto px-4 py-4 sm:px-5"
            >
                {thread.messages.length === 0 ? (
                    <p className="py-10 text-center text-xs text-muted-foreground">
                        No messages yet.
                    </p>
                ) : null}

                {thread.messages.map((message) => (
                    <div
                        key={message.id}
                        className={cn(
                            "flex max-w-[85%] items-end gap-2",
                            message.mine ? "ml-auto flex-row-reverse" : "",
                        )}
                    >
                        {message.user ? (
                            <Avatar className="size-7 shrink-0 rounded-lg">
                                <AvatarImage
                                    src={message.user.avatar ?? undefined}
                                    alt={message.user.name}
                                />
                                <AvatarFallback className="rounded-lg bg-muted text-[10px] font-semibold text-muted-foreground">
                                    {getInitials(message.user.name)}
                                </AvatarFallback>
                            </Avatar>
                        ) : null}

                        <div className="min-w-0">
                            <p
                                className={cn(
                                    "mb-1 flex flex-wrap items-center gap-x-1.5 text-[10px] text-muted-foreground",
                                    message.mine ? "justify-end" : "",
                                )}
                            >
                                <span className="font-medium text-foreground/80">
                                    {message.mine
                                        ? "You"
                                        : (message.user?.name ??
                                          "Former staff member")}
                                </span>
                                <span>
                                    {formatDateTime(message.created_at)}
                                </span>
                                {message.channel === "email" ? (
                                    <span className="inline-flex items-center gap-1 rounded-full bg-solar-soft px-1.5 py-0.5 text-solar-ink">
                                        <Mail className="size-2.5" />
                                        Emailed
                                        {message.email_to
                                            ? ` to ${message.email_to}`
                                            : ""}
                                    </span>
                                ) : null}
                            </p>

                            <div
                                className={cn(
                                    "rounded-xl px-3 py-2 text-sm leading-relaxed whitespace-pre-wrap ring-1",
                                    message.mine
                                        ? "bg-solar-soft text-foreground ring-solar/25"
                                        : "bg-muted/50 ring-hairline",
                                )}
                            >
                                {message.body}
                            </div>

                            {/* A note can carry a photo or a PDF, which is
                                usually the fastest thing to read in it. */}
                            {message.attachment ? (
                                <a
                                    href={message.attachment.url}
                                    target="_blank"
                                    rel="noreferrer"
                                    className="mt-1 inline-flex max-w-full items-center gap-1.5 rounded-md bg-muted/60 px-2 py-1 text-[11px] text-muted-foreground ring-1 ring-hairline hover:text-solar"
                                >
                                    <Paperclip className="size-3 shrink-0" />
                                    <span className="truncate">
                                        {message.attachment.name}
                                    </span>
                                </a>
                            ) : null}
                        </div>
                    </div>
                ))}
            </div>

            <form
                onSubmit={(event) => {
                    event.preventDefault();
                    submit();
                }}
                className="border-t border-hairline px-4 py-3 sm:px-5"
            >
                <textarea
                    rows={3}
                    value={form.data.body}
                    onChange={(event) =>
                        form.setData("body", event.target.value)
                    }
                    placeholder="Write a reply…"
                    className="w-full resize-y rounded-lg border border-input bg-transparent px-3 py-2 text-sm outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50"
                />

                {form.errors.body ? (
                    <p className="mt-1 text-xs font-medium text-destructive">
                        {form.errors.body}
                    </p>
                ) : null}

                {thread.external ? (
                    <div className="mt-2 flex flex-wrap items-center justify-between gap-2">
                        <label className="flex items-center gap-2 text-xs text-muted-foreground">
                            {canEmail ? (
                                <>
                                    <Checkbox
                                        checked={form.data.send_email}
                                        onCheckedChange={(checked) =>
                                            form.setData(
                                                "send_email",
                                                checked === true,
                                            )
                                        }
                                    />
                                    Email this reply to{" "}
                                    <span className="font-medium text-foreground">
                                        {thread.external.email}
                                    </span>
                                </>
                            ) : (
                                <span className="inline-flex items-center gap-1.5">
                                    <Mail className="size-3.5" />
                                    {externalEnabled
                                        ? "You do not have permission to email outside the company."
                                        : "External email is turned off by your Super Admin."}
                                </span>
                            )}
                        </label>

                        {form.errors.send_email ? (
                            <p className="text-xs font-medium text-destructive">
                                {form.errors.send_email}
                            </p>
                        ) : null}
                    </div>
                ) : null}

                <div className="mt-2 flex justify-end">
                    <Button
                        type="submit"
                        size="sm"
                        disabled={
                            form.processing || form.data.body.trim() === ""
                        }
                    >
                        <Send className="size-3.5" />
                        {form.data.send_email ? "Send by email" : "Send"}
                    </Button>
                </div>
            </form>
        </div>
    );
}

function NewConversationDialog({
    open,
    onClose,
    staff,
    canExternal,
    externalEnabled,
}: {
    open: boolean;
    onClose: () => void;
    staff: MessagePerson[];
    canExternal: boolean;
    externalEnabled: boolean;
}) {
    const form = useForm<{
        subject: string;
        body: string;
        participant_ids: number[];
        external_name: string;
        external_email: string;
    }>({
        subject: "",
        body: "",
        participant_ids: [],
        external_name: "",
        external_email: "",
    });

    const [query, setQuery] = useState("");

    const visibleStaff = staff.filter((person) => {
        const term = query.trim().toLowerCase();

        return (
            term === "" ||
            person.name.toLowerCase().includes(term) ||
            person.email.toLowerCase().includes(term) ||
            (person.job_title ?? "").toLowerCase().includes(term)
        );
    });

    const selected = staff.filter((person) =>
        form.data.participant_ids.includes(person.id),
    );

    const toggle = (id: number) => {
        form.setData(
            "participant_ids",
            form.data.participant_ids.includes(id)
                ? form.data.participant_ids.filter(
                      (personId) => personId !== id,
                  )
                : [...form.data.participant_ids, id],
        );
    };

    const submit = () => {
        form.post(storeThread().url, {
            preserveScroll: true,
            onSuccess: () => {
                form.reset();
                setQuery("");
                onClose();
            },
        });
    };

    return (
        <Dialog
            open={open}
            onOpenChange={(state) => {
                if (!state) {
                    onClose();
                }
            }}
        >
            <DialogContent className="max-h-[90vh] overflow-y-auto sm:max-w-2xl">
                <DialogHeader>
                    <DialogTitle>New message</DialogTitle>
                    <DialogDescription>
                        Everybody you add sees the whole conversation, and the
                        replies come back into the inbox rather than to a
                        personal mailbox.
                    </DialogDescription>
                </DialogHeader>

                <form
                    onSubmit={(event) => {
                        event.preventDefault();
                        submit();
                    }}
                    className="grid gap-3"
                >
                    <div>
                        <label className="crm-label" htmlFor="message-subject">
                            Subject
                        </label>
                        <Input
                            id="message-subject"
                            value={form.data.subject}
                            onChange={(event) =>
                                form.setData("subject", event.target.value)
                            }
                            placeholder="e.g. 12A Marine Parade — switchboard question"
                            required
                            className="mt-1"
                        />
                        {form.errors.subject ? (
                            <p className="mt-1 text-xs font-medium text-destructive">
                                {form.errors.subject}
                            </p>
                        ) : null}
                    </div>

                    <div>
                        <span className="crm-label">People</span>

                        {selected.length > 0 ? (
                            <div className="mt-1 flex flex-wrap gap-1.5">
                                {selected.map((person) => (
                                    <span
                                        key={person.id}
                                        className="inline-flex items-center gap-1.5 rounded-full bg-solar-soft px-2 py-1 text-xs text-solar-ink"
                                    >
                                        {person.name}
                                        <button
                                            type="button"
                                            onClick={() => toggle(person.id)}
                                            aria-label={`Remove ${person.name}`}
                                            className="opacity-70 hover:opacity-100"
                                        >
                                            <X className="size-3" />
                                        </button>
                                    </span>
                                ))}
                            </div>
                        ) : null}

                        <Input
                            value={query}
                            onChange={(event) => setQuery(event.target.value)}
                            placeholder="Search staff by name, email or role"
                            className="mt-1.5 h-8 text-xs"
                        />

                        <div className="crm-scroll mt-1.5 max-h-40 overflow-y-auto rounded-lg ring-1 ring-hairline">
                            {visibleStaff.length === 0 ? (
                                <p className="px-3 py-3 text-xs text-muted-foreground">
                                    No staff match that search.
                                </p>
                            ) : null}

                            {visibleStaff.map((person) => (
                                <label
                                    key={person.id}
                                    className="flex cursor-pointer items-center gap-2.5 px-3 py-1.5 hover:bg-accent/40"
                                >
                                    <Checkbox
                                        checked={form.data.participant_ids.includes(
                                            person.id,
                                        )}
                                        onCheckedChange={() =>
                                            toggle(person.id)
                                        }
                                    />
                                    <Avatar className="size-6 rounded-md">
                                        <AvatarImage
                                            src={person.avatar ?? undefined}
                                            alt=""
                                        />
                                        <AvatarFallback className="rounded-md bg-muted text-[9px] font-semibold text-muted-foreground">
                                            {person.name
                                                .slice(0, 2)
                                                .toUpperCase()}
                                        </AvatarFallback>
                                    </Avatar>
                                    <span className="min-w-0">
                                        <span className="block truncate text-xs font-medium">
                                            {person.name}
                                        </span>
                                        <span className="block truncate text-[10px] text-muted-foreground">
                                            {person.job_title ?? person.email}
                                        </span>
                                    </span>
                                </label>
                            ))}
                        </div>

                        {form.errors.participant_ids ? (
                            <p className="mt-1 text-xs font-medium text-destructive">
                                {form.errors.participant_ids}
                            </p>
                        ) : null}
                    </div>

                    <div className="rounded-lg bg-muted/30 p-3 ring-1 ring-hairline">
                        <p className="flex items-center gap-1.5 text-xs font-semibold">
                            <Mail className="size-3.5" />
                            Outside contact
                            <span className="font-normal text-muted-foreground">
                                (optional)
                            </span>
                        </p>

                        {canExternal && externalEnabled ? (
                            <>
                                <div className="mt-2 grid gap-2 sm:grid-cols-2">
                                    <Input
                                        value={form.data.external_name}
                                        onChange={(event) =>
                                            form.setData(
                                                "external_name",
                                                event.target.value,
                                            )
                                        }
                                        placeholder="Name"
                                        aria-label="Outside contact name"
                                        className="h-8 text-xs"
                                    />
                                    <Input
                                        type="email"
                                        value={form.data.external_email}
                                        onChange={(event) =>
                                            form.setData(
                                                "external_email",
                                                event.target.value,
                                            )
                                        }
                                        placeholder="name@example.com"
                                        aria-label="Outside contact email"
                                        className="h-8 text-xs"
                                    />
                                </div>
                                <p className="mt-1.5 text-[10px] text-muted-foreground">
                                    They receive only what you choose to email
                                    them, never the internal replies around it.
                                </p>
                            </>
                        ) : (
                            <p className="mt-1 text-[11px] text-muted-foreground">
                                {externalEnabled
                                    ? "You do not have permission to message outside the company."
                                    : "A Super Admin has turned external email off, so conversations stay inside the company."}
                            </p>
                        )}

                        {form.errors.external_email ? (
                            <p className="mt-1 text-xs font-medium text-destructive">
                                {form.errors.external_email}
                            </p>
                        ) : null}
                    </div>

                    <div>
                        <label className="crm-label" htmlFor="message-body">
                            First message
                        </label>
                        <textarea
                            id="message-body"
                            rows={4}
                            value={form.data.body}
                            onChange={(event) =>
                                form.setData("body", event.target.value)
                            }
                            placeholder="What is this about, and what do you need from the people on it?"
                            className="mt-1 w-full rounded-lg border border-input bg-transparent px-3 py-2 text-sm outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50"
                            required
                        />
                        {form.errors.body ? (
                            <p className="mt-1 text-xs font-medium text-destructive">
                                {form.errors.body}
                            </p>
                        ) : null}
                    </div>

                    <DialogFooter>
                        <Button type="button" variant="ghost" onClick={onClose}>
                            Cancel
                        </Button>
                        <Button type="submit" disabled={form.processing}>
                            <Send className="size-4" />
                            Start conversation
                        </Button>
                    </DialogFooter>
                </form>
            </DialogContent>
        </Dialog>
    );
}

function AddPeopleDialog({
    open,
    onClose,
    thread,
    staff,
}: {
    open: boolean;
    onClose: () => void;
    thread: InboxThread;
    staff: MessagePerson[];
}) {
    const form = useForm({
        participant_ids: [] as number[],
    });

    const existing = new Set(thread.participants.map((person) => person.id));
    const available = staff.filter((person) => !existing.has(person.id));

    const toggle = (id: number) => {
        form.setData(
            "participant_ids",
            form.data.participant_ids.includes(id)
                ? form.data.participant_ids.filter(
                      (personId) => personId !== id,
                  )
                : [...form.data.participant_ids, id],
        );
    };

    const submit = () => {
        form.post(addParticipants(thread.id).url, {
            preserveScroll: true,
            onSuccess: () => {
                form.reset();
                onClose();
            },
        });
    };

    return (
        <Dialog
            open={open}
            onOpenChange={(state) => {
                if (!state) {
                    onClose();
                }
            }}
        >
            <DialogContent className="sm:max-w-md">
                <DialogHeader>
                    <DialogTitle>Add people</DialogTitle>
                    <DialogDescription>
                        They see the entire history when they open it, not just
                        the replies from now on.
                    </DialogDescription>
                </DialogHeader>

                <form
                    onSubmit={(event) => {
                        event.preventDefault();
                        submit();
                    }}
                    className="grid gap-3"
                >
                    <div className="crm-scroll max-h-64 overflow-y-auto rounded-lg ring-1 ring-hairline">
                        {available.length === 0 ? (
                            <p className="px-3 py-4 text-xs text-muted-foreground">
                                Everybody who can use the inbox is already on
                                this conversation.
                            </p>
                        ) : null}

                        {available.map((person) => (
                            <label
                                key={person.id}
                                className="flex cursor-pointer items-center gap-2.5 px-3 py-2 hover:bg-accent/40"
                            >
                                <Checkbox
                                    checked={form.data.participant_ids.includes(
                                        person.id,
                                    )}
                                    onCheckedChange={() => toggle(person.id)}
                                />
                                <span className="min-w-0">
                                    <span className="block truncate text-xs font-medium">
                                        {person.name}
                                    </span>
                                    <span className="block truncate text-[10px] text-muted-foreground">
                                        {person.job_title ?? person.email}
                                    </span>
                                </span>
                            </label>
                        ))}
                    </div>

                    {form.errors.participant_ids ? (
                        <p className="text-xs font-medium text-destructive">
                            {form.errors.participant_ids}
                        </p>
                    ) : null}

                    <DialogFooter>
                        <Button type="button" variant="ghost" onClick={onClose}>
                            Cancel
                        </Button>
                        <Button
                            type="submit"
                            disabled={
                                form.processing ||
                                form.data.participant_ids.length === 0
                            }
                        >
                            <UserPlus className="size-4" />
                            Add to conversation
                        </Button>
                    </DialogFooter>
                </form>
            </DialogContent>
        </Dialog>
    );
}

/**
 * "12m ago" for anything recent enough to be worth counting, then a date. The
 * inbox is read in the order things arrived, so the top of the list only needs
 * to say how long ago that was.
 */
function relativeTime(value: string | null): string {
    if (!value) {
        return "";
    }

    const date = new Date(value);

    if (Number.isNaN(date.getTime())) {
        return "";
    }

    const minutes = Math.round((Date.now() - date.getTime()) / 60_000);

    if (minutes < 1) {
        return "now";
    }

    if (minutes < 60) {
        return `${minutes}m`;
    }

    const hours = Math.round(minutes / 60);

    if (hours < 24) {
        return `${hours}h`;
    }

    const days = Math.round(hours / 24);

    if (days < 7) {
        return `${days}d`;
    }

    return formatDate(value);
}
