import { useEffect, useMemo, useRef, useState } from "react";
import { useSearchParams } from "react-router-dom";
import type { ChangeEvent } from "react";
// Slice 6b: compose dialog (shadcn Dialog family) + lucide icons. The file is
// now fully @mui-free (6a migrated the directory surface, drawer, and the
// compose content's shared leaf components).
import {
	X,
	Paperclip,
	Bold,
	Italic,
	Link,
	List,
	Mail,
	Send,
	Trash2,
} from "lucide-react";
import {
	Dialog,
	DialogContent,
	DialogDescription,
	DialogFooter,
	DialogHeader,
	DialogTitle,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
import { Separator } from "@/components/ui/separator";
import { Label } from "@/components/ui/label";
// Slice 6a directory surface + drawer primitives.
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Badge } from "@/components/ui/badge";
import { Button as UiButton } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import { Alert as UIAlert, AlertDescription } from "@/components/ui/alert";
import { Progress } from "@/components/ui/progress";
import {
	Tooltip,
	TooltipContent,
	TooltipTrigger,
} from "@/components/ui/tooltip";
import { Sheet, SheetContent } from "@/components/ui/sheet";
import { SheetForm } from "@/components/ui/sheet-form";
import {
	Table,
	TableBody,
	TableCell,
	TableHead,
	TableHeader,
	TableRow,
} from "@/components/ui/table";
import { cn } from "@/lib/utils";
import { MetricCard } from "../components/MetricCard";
import { SessionActivityPanel } from "../components/SessionActivityPanel";
import {
	MobileCardRow,
	type MobileCardField,
} from "@/components/ui/mobile-card";
import { useUsers } from "../hooks/useUsers";
import { useActivity } from "../hooks/useDashboard";
import { useIsMobile } from "../hooks/useIsMobile";
import { useSendUserMessage } from "../hooks/useSendUserMessage";
import { useUserMessageQueueStatus } from "../hooks/useUserMessageQueueStatus";
import type { UserDirectoryItem } from "../types";
import { buildUserDrawerModel } from "../users";
import {
	mergeUsersWithActivity,
	resolveUserSelection,
	type UserStateItem,
} from "../userState";

// Local breakpoint for the compose dialog (slice 6b uses 900px for fullScreen).
// The shared `useIsMobile` from hooks/ (768px) drives the directory table branch.
function useComposeViewport(query = "(max-width: 900px)") {
	const [mobile, setMobile] = useState(() =>
		typeof window !== "undefined" && typeof window.matchMedia === "function"
			? window.matchMedia(query).matches
			: false,
	);
	useEffect(() => {
		if (
			typeof window === "undefined" ||
			typeof window.matchMedia !== "function"
		) {
			return;
		}
		const mql = window.matchMedia(query);
		const onChange = (event: MediaQueryListEvent) => setMobile(event.matches);
		mql.addEventListener("change", onChange);
		return () => mql.removeEventListener("change", onChange);
	}, [query]);
	return mobile;
}

function userLabel(user: UserDirectoryItem) {
	return user.display_name || user.username || user.jellyfin_id;
}

// Activity → Badge status variant (design §2.3: healthy/active = success chart-2,
// paused = warning chart-3, neutral = secondary).
function activityBadgeVariant(
	label: string,
): "success" | "warning" | "secondary" {
	if (label === "Playing") return "success";
	if (label === "Paused") return "warning";
	return "secondary";
}

const DEFAULT_HTML_BODY = "<p>Hello,</p><p> </p><p>Best,<br />Manage</p>";

// Mobile card fields (spec R3.2): display name is primary; username, activity
// badge, and email give the at-a-glance info for scanning users on a phone.
// See OpenSpec change `mobile-responsive-parity`, tasks slice 5.1.
const userCardFields: MobileCardField<UserStateItem>[] = [
	{ key: "name", label: "Name", render: (r) => userLabel(r), primary: true },
	{
		key: "username",
		label: "Username",
		render: (r) =>
			r.username && r.username !== r.display_name ? r.username : r.jellyfin_id,
	},
	{
		key: "activity",
		label: "Activity",
		render: (r) => (
			<Badge variant={activityBadgeVariant(r.activity_label)}>
				{r.activity_label}
			</Badge>
		),
	},
	{ key: "email", label: "Email", render: (r) => r.email || "—" },
];

export function UsersPage() {
	const { data, isError, error } = useUsers();
	const { data: activity } = useActivity();
	const queueStatusQuery = useUserMessageQueueStatus();
	const sendUserMessage = useSendUserMessage();
	const isComposeMobile = useComposeViewport();
	const isMobile = useIsMobile();
	const [search, setSearch] = useState("");
	const [searchParams, setSearchParams] = useSearchParams();
	const [selectedUserIds, setSelectedUserIds] = useState<string[]>([]);
	const [composeOpen, setComposeOpen] = useState(false);
	const [subject, setSubject] = useState("");
	const [htmlBody, setHtmlBody] = useState(DEFAULT_HTML_BODY);
	const [attachments, setAttachments] = useState<File[]>([]);
	const htmlBodyRef = useRef<HTMLTextAreaElement | null>(null);

	const baseRows = data?.items ?? [];
	const rows = useMemo(
		() => mergeUsersWithActivity(baseRows, activity ?? []),
		[baseRows, activity],
	);
	const filteredRows = useMemo(() => {
		const term = search.trim().toLowerCase();
		if (!term) {
			return rows;
		}
		return rows.filter((row) => {
			return [
				row.username,
				row.display_name,
				row.email,
				row.email_source,
				row.avatar_source,
				row.name_source,
				row.access_source,
				row.user_type_label,
				row.role,
				row.permissions_label,
				row.jellyseerr_username,
				row.activity_label,
				row.activity_summary,
				row.activity.primary_session?.title || "",
				String(row.jellyseerr_user_id ?? ""),
			].some((value) => value.toLowerCase().includes(term));
		});
	}, [rows, search]);

	const metrics = useMemo(() => {
		const total = baseRows.length;
		const contactable = rows.filter((row) => row.contactable).length;
		const enriched = rows.filter(
			(row) => row.jellyseerr_user_id !== null,
		).length;
		const admins = rows.filter((row) => row.role === "admin").length;
		return { total, contactable, enriched, admins };
	}, [baseRows]);

	const queueStatus = queueStatusQuery.data;
	const queueBanner = useMemo(() => {
		if (!queueStatus) {
			return null;
		}
		const activeCount = queueStatus.active_request_id ? 1 : 0;
		const totalCount = queueStatus.pending_count + activeCount;
		const countLabel =
			totalCount > 0
				? `${totalCount} item${totalCount === 1 ? "" : "s"} in queue (${queueStatus.pending_count} waiting${activeCount ? ", 1 processing" : ""})`
				: "0 items in queue";
		if (!queueStatus.worker_running) {
			return {
				severity: "warning" as const,
				message:
					queueStatus.last_error ||
					"Email queue worker is not running. New messages cannot be delivered until it restarts.",
				countLabel,
				subtext: `Sent ${queueStatus.sent_count}, failed ${queueStatus.failed_count}.`,
			};
		}
		if (queueStatus.state === "error") {
			return {
				severity: "error" as const,
				message: queueStatus.last_error || "The last email delivery failed.",
				countLabel,
				subtext: `Sent ${queueStatus.sent_count}, failed ${queueStatus.failed_count}.`,
			};
		}
		if (queueStatus.state === "busy") {
			const active = queueStatus.active_request_id
				? `processing ${queueStatus.active_request_id.slice(0, 8)}`
				: "processing a message";
			const waiting = queueStatus.pending_count
				? `${queueStatus.pending_count} waiting`
				: "no backlog";
			return {
				severity: "info" as const,
				message: `Email queue is busy: ${active}, ${waiting}.`,
				countLabel,
				subtext: `Sent ${queueStatus.sent_count}, failed ${queueStatus.failed_count}.`,
			};
		}
		return {
			severity: "success" as const,
			message: "Email queue is idle and empty.",
			countLabel,
			subtext: `Sent ${queueStatus.sent_count}, failed ${queueStatus.failed_count}.`,
		};
	}, [queueStatus]);

	const selectedIdSet = useMemo(
		() => new Set(selectedUserIds),
		[selectedUserIds],
	);
	const selectedRows = useMemo(
		() => rows.filter((row) => selectedIdSet.has(row.jellyfin_id)),
		[rows, selectedIdSet],
	);
	const selectedDeliverableRows = useMemo(
		() => selectedRows.filter((row) => row.contactable && row.email),
		[selectedRows],
	);
	const skippedRows = useMemo(
		() => selectedRows.filter((row) => !row.contactable || !row.email),
		[selectedRows],
	);
	const visibleSelectedRows = useMemo(
		() => filteredRows.filter((row) => selectedIdSet.has(row.jellyfin_id)),
		[filteredRows, selectedIdSet],
	);
	const allVisibleSelected =
		filteredRows.length > 0 &&
		visibleSelectedRows.length === filteredRows.length;

	const toggleUserSelected = (userId: string) => {
		setSelectedUserIds((current) =>
			current.includes(userId)
				? current.filter((id) => id !== userId)
				: [...current, userId],
		);
	};

	const toggleVisibleSelection = (checked: boolean) => {
		setSelectedUserIds((current) => {
			const next = new Set(current);
			filteredRows.forEach((row) => {
				if (checked) {
					next.add(row.jellyfin_id);
				} else {
					next.delete(row.jellyfin_id);
				}
			});
			return Array.from(next);
		});
	};

	const selectedUserParam = searchParams.get("user") || "";
	const selectedUser = useMemo(
		() =>
			selectedUserParam
				? (resolveUserSelection(
						rows,
						selectedUserParam,
					) as UserStateItem | null)
				: null,
		[rows, selectedUserParam],
	);
	const drawerModel = selectedUser ? buildUserDrawerModel(selectedUser) : null;

	const openCompose = () => {
		if (!selectedRows.length) {
			return;
		}
		sendUserMessage.reset();
		if (!subject.trim()) {
			setSubject(
				`Manage update for ${selectedDeliverableRows.length} user${selectedDeliverableRows.length === 1 ? "" : "s"}`,
			);
		}
		if (!htmlBody.trim()) {
			setHtmlBody(DEFAULT_HTML_BODY);
		}
		setComposeOpen(true);
	};

	const closeCompose = () => {
		setComposeOpen(false);
		sendUserMessage.reset();
	};

	const insertMarkup = (before: string, after = before) => {
		const textarea = htmlBodyRef.current;
		if (!textarea) {
			return;
		}
		const start = textarea.selectionStart ?? htmlBody.length;
		const end = textarea.selectionEnd ?? htmlBody.length;
		const selected = htmlBody.slice(start, end) || "text";
		const next =
			htmlBody.slice(0, start) +
			before +
			selected +
			after +
			htmlBody.slice(end);
		setHtmlBody(next);
		requestAnimationFrame(() => {
			textarea.focus();
			const cursorStart = start + before.length;
			const cursorEnd = cursorStart + selected.length;
			textarea.setSelectionRange(cursorStart, cursorEnd);
		});
	};

	const addLink = () => {
		const url = window.prompt("Link URL", "https://");
		if (!url) {
			return;
		}
		insertMarkup(`<a href="${url}">`, "</a>");
	};

	const handleAttachments = (event: ChangeEvent<HTMLInputElement>) => {
		const files = Array.from(event.target.files || []);
		if (files.length) {
			setAttachments((current) => [...current, ...files]);
		}
		event.target.value = "";
	};

	const removeAttachment = (index: number) => {
		setAttachments((current) => current.filter((_, idx) => idx !== index));
	};

	const handleSend = async () => {
		const allSelectedRows = selectedRows;
		if (!allSelectedRows.length) {
			return;
		}

		const formData = new FormData();
		formData.append(
			"recipient_ids",
			JSON.stringify(allSelectedRows.map((row) => row.jellyfin_id)),
		);
		formData.append("subject", subject);
		formData.append("html_body", htmlBody);
		attachments.forEach((file) => {
			formData.append("attachments", file, file.name);
		});

		try {
			await sendUserMessage.mutateAsync(formData);
			setComposeOpen(false);
			setAttachments([]);
			setSubject("");
			setHtmlBody(DEFAULT_HTML_BODY);
		} catch {
			// Mutation state is shown inline.
		}
	};

	// Sticky table-header base (opaque so rows don't bleed through on scroll).
	const thBase = "font-semibold sticky top-0 z-10 bg-card";

	return (
		<div className="flex flex-col gap-6">
			<div>
				<h1 className="text-2xl font-semibold">Users</h1>
				<p className="text-sm text-muted-foreground">
					Read-only Jellyfin users with optional Jellyseerr enrichment.
				</p>
			</div>

			{isError ? (
				<UIAlert variant="destructive">
					<AlertDescription>
						Unable to load users: {(error as Error)?.message || "Unknown error"}
					</AlertDescription>
				</UIAlert>
			) : null}

			{data && !data.jellyseerr_configured ? (
				<UIAlert>
					<AlertDescription>
						Jellyseerr is not configured in the backend yet. Check
						JELLYSEERR_URL and JELLYSEERR_API_KEY, then restart the API.
					</AlertDescription>
				</UIAlert>
			) : null}

			{data?.jellyseerr_error ? (
				<UIAlert>
					<AlertDescription>
						Jellyseerr enrichment is unavailable: {data.jellyseerr_error}
					</AlertDescription>
				</UIAlert>
			) : null}

			{data?.jellyseerr_configured &&
			!data.jellyseerr_error &&
			data.enriched_count === 0 ? (
				<UIAlert>
					<AlertDescription>
						Jellyseerr is connected, but no Jellyfin users were matched yet. The
						backend found {data.jellyseerr_jellyfin_user_count} Jellyfin-linked
						entries and {data.jellyseerr_user_count} Jellyseerr users.
					</AlertDescription>
				</UIAlert>
			) : null}

			{queueStatusQuery.isError ? (
				<UIAlert>
					<AlertDescription>
						Unable to load email queue status:{" "}
						{String(
							(queueStatusQuery.error as Error)?.message || "Unknown error",
						)}
					</AlertDescription>
				</UIAlert>
			) : queueBanner ? (
				<UIAlert
					variant={queueBanner.severity === "error" ? "destructive" : undefined}
				>
					<div className="flex flex-wrap items-center gap-2">
						<span className="text-sm font-semibold">{queueBanner.message}</span>
						<Badge variant="secondary">{queueBanner.countLabel}</Badge>
					</div>
					<AlertDescription>{queueBanner.subtext}</AlertDescription>
				</UIAlert>
			) : null}

			<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-4">
				<MetricCard label="Total users" value={String(metrics.total)} />
				<MetricCard label="Contactable" value={String(metrics.contactable)} />
				<MetricCard label="Enriched" value={String(metrics.enriched)} />
				<MetricCard label="Admins" value={String(metrics.admins)} />
			</div>

			<div className="rounded-lg border bg-card p-4">
				<div className="flex flex-col gap-4">
					<div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
						<div>
							<h2 className="text-base font-semibold">User list</h2>
							<p className="text-sm text-muted-foreground">
								{filteredRows.length} visible of {rows.length} total
							</p>
						</div>
						<div className="flex flex-col gap-2 sm:flex-row sm:items-center">
							<Badge variant="outline">{selectedRows.length} selected</Badge>
							<Badge
								variant={selectedDeliverableRows.length ? "success" : "outline"}
							>
								{selectedDeliverableRows.length} deliverable
							</Badge>
							<UiButton
								variant="default"
								disabled={!selectedDeliverableRows.length}
								onClick={openCompose}
							>
								<Mail />
								Message selected
							</UiButton>
							<UiButton
								variant="ghost"
								disabled={!selectedRows.length}
								onClick={() => setSelectedUserIds([])}
							>
								Clear selection
							</UiButton>
							<Input
								aria-label="Search"
								placeholder="Name, email, role, permission..."
								value={search}
								onChange={(event) => setSearch(event.target.value)}
								className="w-full sm:w-80"
							/>
						</div>
					</div>

					<div className="max-h-[660px] overflow-auto rounded-lg border">
						{isMobile ? (
							<div className="p-3">
								<MobileCardRow
									rows={filteredRows}
									fields={userCardFields}
									getRowId={(r) => r.jellyfin_id}
									onRowClick={(r) => setSearchParams({ user: r.jellyfin_id })}
									actions={(r) => {
										const checked = selectedIdSet.has(r.jellyfin_id);
										return (
											<Checkbox
												checked={checked}
												aria-label={`Select ${userLabel(r)}`}
												className="mobile-touch-target"
												onClick={(e) => e.stopPropagation()}
												onCheckedChange={() =>
													toggleUserSelected(r.jellyfin_id)
												}
											/>
										);
									}}
								/>
							</div>
						) : (
							<Table aria-label="Users table">
								<TableHeader>
									<TableRow>
										<TableHead className={cn(thBase, "w-14 p-2")}>
											<Checkbox
												checked={allVisibleSelected}
												aria-label="Select all visible users"
												onCheckedChange={(checked) =>
													toggleVisibleSelection(checked === true)
												}
											/>
										</TableHead>
										<TableHead className={thBase}>User</TableHead>
										<TableHead className={thBase}>Email</TableHead>
										<TableHead className={cn(thBase, "w-[132px] text-center")}>
											Activity
										</TableHead>
										<TableHead
											className={cn(
												thBase,
												"hidden w-[140px] text-center md:table-cell",
											)}
										>
											Type
										</TableHead>
										<TableHead className={cn(thBase, "w-[132px] text-center")}>
											Jellyseerr
										</TableHead>
										<TableHead
											className={cn(
												thBase,
												"hidden w-[120px] text-center md:table-cell",
											)}
										>
											Role
										</TableHead>
										<TableHead className={thBase}>Permissions</TableHead>
										<TableHead
											className={cn(
												thBase,
												"hidden w-24 text-center md:table-cell",
											)}
										>
											Reqs
										</TableHead>
										<TableHead
											className={cn(
												thBase,
												"hidden w-[120px] text-center md:table-cell",
											)}
										>
											Contact
										</TableHead>
									</TableRow>
								</TableHeader>
								<TableBody>
									{filteredRows.map((row) => {
										const linked =
											row.jellyseerr_user_id !== null &&
											row.jellyseerr_user_id !== undefined;
										const checked = selectedIdSet.has(row.jellyfin_id);
										return (
											<TableRow
												key={row.jellyfin_id}
												data-state={
													checked ||
													selectedUser?.jellyfin_id === row.jellyfin_id
														? "selected"
														: undefined
												}
												className="cursor-pointer"
												onClick={() =>
													setSearchParams({ user: row.jellyfin_id })
												}
											>
												<TableCell className="w-14 p-2">
													<Checkbox
														checked={checked}
														aria-label={`Select ${userLabel(row)}`}
														onClick={(event) => event.stopPropagation()}
														onCheckedChange={() =>
															toggleUserSelected(row.jellyfin_id)
														}
													/>
												</TableCell>
												<TableCell>
													<div className="flex items-center gap-3 min-w-0">
														<Avatar className="size-9">
															<AvatarImage
																src={row.avatar || undefined}
																alt={userLabel(row)}
															/>
															<AvatarFallback>
																{userLabel(row).charAt(0).toUpperCase()}
															</AvatarFallback>
														</Avatar>
														<div className="min-w-0">
															<div className="truncate font-semibold leading-tight">
																{userLabel(row)}
															</div>
															<div className="truncate text-xs text-muted-foreground">
																{row.username &&
																row.username !== row.display_name
																	? row.username
																	: row.jellyfin_id}
															</div>
														</div>
													</div>
												</TableCell>
												<TableCell>
													<div className="truncate font-medium">
														{row.email || "—"}
													</div>
												</TableCell>
												<TableCell className="text-center">
													<Badge
														variant={activityBadgeVariant(row.activity_label)}
													>
														{row.activity_label}
													</Badge>
												</TableCell>
												<TableCell className="hidden text-center md:table-cell">
													<Badge variant="outline">{row.user_type_label}</Badge>
												</TableCell>
												<TableCell className="text-center">
													<Badge variant={linked ? "success" : "secondary"}>
														{linked
															? `Linked #${row.jellyseerr_user_id}`
															: "Base only"}
													</Badge>
												</TableCell>
												<TableCell className="hidden text-center md:table-cell">
													<Badge variant="outline">{row.role}</Badge>
												</TableCell>
												<TableCell className="whitespace-normal">
													{row.permissions_label}
												</TableCell>
												<TableCell className="hidden text-center font-semibold md:table-cell">
													{row.request_count ?? "—"}
												</TableCell>
												<TableCell className="hidden text-center md:table-cell">
													<Badge
														variant={row.contactable ? "success" : "secondary"}
													>
														{row.contactable ? "Yes" : "No"}
													</Badge>
												</TableCell>
											</TableRow>
										);
									})}
								</TableBody>
							</Table>
						)}
					</div>
				</div>
			</div>

			<Sheet
				open={Boolean(drawerModel)}
				onOpenChange={(open) => {
					if (!open) {
						setSearchParams({});
					}
				}}
			>
				<SheetContent
					side="right"
					showCloseButton={false}
					className="w-full gap-6 overflow-y-auto p-6 sm:max-w-[440px]"
				>
					{selectedUser && drawerModel ? (
						<div className="flex flex-col gap-6">
							<div className="flex items-start gap-4">
								<Avatar className="size-14">
									<AvatarImage
										src={selectedUser.avatar || undefined}
										alt={drawerModel.title}
									/>
									<AvatarFallback>
										{drawerModel.title.charAt(0).toUpperCase()}
									</AvatarFallback>
								</Avatar>
								<div className="min-w-0 flex-1">
									<h2 className="truncate text-lg font-bold">
										{drawerModel.title}
									</h2>
									<p className="truncate text-sm text-muted-foreground">
										{drawerModel.subtitle}
									</p>
								</div>
								<Badge variant="secondary">
									{drawerModel.contactState.label}
								</Badge>
								<UiButton
									variant="ghost"
									aria-label="Close user details"
									onClick={() => setSearchParams({})}
								>
									<X />
									Close
								</UiButton>
							</div>

							<div className="flex flex-wrap gap-2">
								<Badge variant="outline">{selectedUser.user_type_label}</Badge>
								<Badge variant="default">{selectedUser.role}</Badge>
								<Badge variant="secondary">{drawerModel.syncStatus}</Badge>
							</div>

							<div className="rounded-lg border bg-card p-4">
								<h3 className="mb-2 text-sm font-semibold">Identity</h3>
								<div className="flex flex-col gap-1">
									{drawerModel.identity.map((field) => (
										<div key={field.label} className="flex gap-4">
											<span className="min-w-[120px] text-xs uppercase text-muted-foreground">
												{field.label}
											</span>
											<span className="break-words text-sm">{field.value}</span>
										</div>
									))}
								</div>
							</div>

							<div className="rounded-lg border bg-card p-4">
								<h3 className="mb-2 text-sm font-semibold">Activity</h3>
								<SessionActivityPanel
									sessions={selectedUser.activity.sessions}
									selectedUserLabel={
										selectedUser.display_name ||
										selectedUser.username ||
										selectedUser.jellyfin_id
									}
									emptyMessage="No live sessions matched to this user."
								/>
							</div>

							<div className="rounded-lg border bg-card p-4">
								<h3 className="mb-2 text-sm font-semibold">Contact actions</h3>
								<p className="mb-2 text-sm text-muted-foreground">
									{drawerModel.contactState.description}
								</p>
								<div className="flex flex-wrap gap-2">
									{drawerModel.contactActions.map((action) => (
										<UiButton
											key={action.label}
											variant="outline"
											disabled={!action.enabled}
										>
											{action.label}
										</UiButton>
									))}
								</div>
								<p className="mt-2 text-xs text-muted-foreground">
									{drawerModel.contactActions
										.map((action) => action.hint)
										.join(" ")}
								</p>
							</div>

							<div className="rounded-lg border bg-card p-4">
								<h3 className="mb-2 text-sm font-semibold">Permissions</h3>
								<div className="flex flex-wrap gap-1">
									{drawerModel.permissions.map((permission) => (
										<Badge key={permission} variant="secondary">
											{permission}
										</Badge>
									))}
								</div>
							</div>

							<Separator />

							<p className="text-xs text-muted-foreground">
								This panel is read-only for now. Communication actions will be
								added later without redesigning the list.
							</p>
						</div>
					) : null}
				</SheetContent>
			</Sheet>

			{/* Compose dialog: SheetForm below md, Dialog at md+ (spec R4.1) */}
			{(() => {
				const composeBody = (
					<>
						{sendUserMessage.isPending ? (
							<Progress value={100} className="animate-pulse" />
						) : null}
						<div className="flex flex-1 flex-col gap-4 overflow-y-auto px-4 py-4">
							{sendUserMessage.isError ? (
								<UIAlert variant="destructive">
									<AlertDescription>
										Unable to send message:{" "}
										{(sendUserMessage.error as Error)?.message ||
											"Unknown error"}
									</AlertDescription>
								</UIAlert>
							) : null}
							{sendUserMessage.isSuccess ? (
								<UIAlert>
									<AlertDescription>
										Queued for {sendUserMessage.data.recipient_count} recipients
										{sendUserMessage.data.attachment_count
											? ` with ${sendUserMessage.data.attachment_count} attachment${sendUserMessage.data.attachment_count === 1 ? "" : "s"}`
											: ""}
										{sendUserMessage.data.request_id
											? ` (request ${sendUserMessage.data.request_id.slice(0, 8)})`
											: ""}
										.
									</AlertDescription>
								</UIAlert>
							) : null}

							{queueBanner ? (
								<UIAlert
									variant={
										queueBanner.severity === "error" ? "destructive" : undefined
									}
								>
									<div className="flex flex-wrap items-center gap-2">
										<span className="text-sm font-semibold">
											{queueBanner.message}
										</span>
										<Badge variant="secondary">{queueBanner.countLabel}</Badge>
									</div>
								</UIAlert>
							) : null}

							<UIAlert>
								<AlertDescription>
									{selectedRows.length} selected,{" "}
									{selectedDeliverableRows.length} deliverable.
									{skippedRows.length
										? ` ${skippedRows.length} will be skipped because they do not have a deliverable email address.`
										: ""}
								</AlertDescription>
							</UIAlert>

							<div className="flex flex-wrap gap-1">
								{selectedDeliverableRows.map((row) => (
									<Badge key={row.jellyfin_id} variant="secondary">
										{`${userLabel(row)} <${row.email}>`}
									</Badge>
								))}
							</div>

							<div className="flex flex-col gap-1.5">
								<Label htmlFor="compose-subject">Subject</Label>
								<Input
									id="compose-subject"
									value={subject}
									onChange={(event) => setSubject(event.target.value)}
								/>
							</div>

							<div className="flex flex-wrap gap-1">
								<Tooltip>
									<TooltipTrigger asChild>
										<UiButton
											variant="ghost"
											size="icon"
											className="mobile-touch-target"
											onClick={() => insertMarkup("<strong>", "</strong>")}
											aria-label="Bold"
										>
											<Bold />
										</UiButton>
									</TooltipTrigger>
									<TooltipContent>Bold</TooltipContent>
								</Tooltip>
								<Tooltip>
									<TooltipTrigger asChild>
										<UiButton
											variant="ghost"
											size="icon"
											className="mobile-touch-target"
											onClick={() => insertMarkup("<em>", "</em>")}
											aria-label="Italic"
										>
											<Italic />
										</UiButton>
									</TooltipTrigger>
									<TooltipContent>Italic</TooltipContent>
								</Tooltip>
								<Tooltip>
									<TooltipTrigger asChild>
										<UiButton
											variant="ghost"
											size="icon"
											className="mobile-touch-target"
											onClick={addLink}
											aria-label="Link"
										>
											<Link />
										</UiButton>
									</TooltipTrigger>
									<TooltipContent>Link</TooltipContent>
								</Tooltip>
								<Tooltip>
									<TooltipTrigger asChild>
										<UiButton
											variant="ghost"
											size="icon"
											className="mobile-touch-target"
											onClick={() => insertMarkup("<ul><li>", "</li></ul>")}
											aria-label="Bullet list"
										>
											<List />
										</UiButton>
									</TooltipTrigger>
									<TooltipContent>Bullet list</TooltipContent>
								</Tooltip>
							</div>

							<div className="flex flex-col gap-1.5">
								<Label htmlFor="compose-body">HTML message body</Label>
								<Textarea
									id="compose-body"
									ref={htmlBodyRef}
									value={htmlBody}
									onChange={(event) => setHtmlBody(event.target.value)}
									className="min-h-[260px] font-mono"
								/>
								<p className="text-xs text-muted-foreground">
									Formatting is sent as HTML; a plain-text fallback is generated
									automatically.
								</p>
							</div>

							<div className="rounded-lg border bg-muted/40 p-4">
								<p className="mb-2 text-sm font-semibold">Preview</p>
								<div className="overflow-hidden rounded-md border bg-card">
									<iframe
										title="Email preview"
										sandbox=""
										srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>body{font-family:Roboto,Arial,sans-serif;padding:16px;margin:0;background:#fff;color:#111;line-height:1.5}</style></head><body>${htmlBody || "<p>(Empty)</p>"}</body></html>`}
										style={{ width: "100%", minHeight: 220, border: 0 }}
									/>
								</div>
							</div>

							<div className="flex flex-wrap items-center gap-1">
								<UiButton asChild variant="outline">
									<label className="cursor-pointer">
										<Paperclip />
										Add attachment
										<input
											hidden
											type="file"
											multiple
											onChange={handleAttachments}
										/>
									</label>
								</UiButton>
								{attachments.map((file, index) => (
									<Badge
										key={`${file.name}-${index}`}
										variant="secondary"
										className="gap-1 pr-1"
									>
										{file.name}
										<button
											type="button"
											aria-label={`Remove ${file.name}`}
											onClick={() => removeAttachment(index)}
											className="mobile-touch-target inline-flex items-center text-current [&>svg]:size-3"
										>
											<Trash2 />
										</button>
									</Badge>
								))}
							</div>
						</div>
					</>
				);

				if (isMobile) {
					return (
						<SheetForm
							open={composeOpen}
							onOpenChange={(open) => {
								if (!open) closeCompose();
							}}
							title="Message selected users"
							onSave={handleSend}
							onCancel={closeCompose}
							isPending={sendUserMessage.isPending}
							saveDisabled={!selectedDeliverableRows.length || !subject.trim()}
							saveLabel="Send message"
							isDirty={
								subject.trim() !== "" ||
								htmlBody.trim() !== DEFAULT_HTML_BODY.trim() ||
								attachments.length > 0
							}
						>
							<div className="flex flex-col gap-4">{composeBody}</div>
						</SheetForm>
					);
				}

				return (
					<Dialog
						open={composeOpen}
						onOpenChange={(open) => {
							if (!open) {
								closeCompose();
							}
						}}
					>
						<DialogContent
							className={cn(
								"flex max-h-[90dvh] flex-col gap-0 overflow-hidden p-0 sm:max-w-2xl",
								isComposeMobile &&
									"inset-0 max-h-none max-w-none translate-x-0 translate-y-0 rounded-none",
							)}
						>
							<DialogHeader className="gap-1 px-4 pt-4">
								<DialogTitle className="pr-8">
									Message selected users
								</DialogTitle>
								<DialogDescription className="sr-only">
									Compose a message to the selected deliverable users.
								</DialogDescription>
							</DialogHeader>
							{composeBody}
							<DialogFooter className="m-0 border-t p-4">
								<UiButton variant="ghost" onClick={closeCompose}>
									Cancel
								</UiButton>
								<UiButton
									variant="default"
									disabled={
										sendUserMessage.isPending ||
										!selectedDeliverableRows.length ||
										!subject.trim()
									}
									onClick={handleSend}
								>
									<Send />
									Send message
								</UiButton>
							</DialogFooter>
						</DialogContent>
					</Dialog>
				);
			})()}
		</div>
	);
}
