import * as React from "react";
import { Loader2, XIcon } from "lucide-react";

import { cn } from "@/lib/utils";
import { Button } from "@/components/ui/button";
import { Sheet, SheetContent, SheetTitle } from "@/components/ui/sheet";
import { ConfirmDialog } from "@/components/ConfirmDialog";

export interface SheetFormProps {
	open: boolean;
	onOpenChange: (open: boolean) => void;
	title: string;
	onSave: () => void;
	onCancel: () => void;
	/** Disable Save and show a pending spinner. */
	isPending?: boolean;
	/** Override the Save button label (default "Save"). */
	saveLabel?: string;
	/** Disable the Save button (e.g. when required fields are empty). */
	saveDisabled?: boolean;
	/**
	 * When true, any close attempt (Cancel button, header X, overlay click,
	 * Escape) prompts a discard-confirmation instead of immediately closing.
	 * Spec R4.5.
	 */
	isDirty?: boolean;
	children: React.ReactNode;
	/** Optional className applied to the scrolling body. */
	bodyClassName?: string;
}

/**
 * Full-height form host for the mobile (`< md`) breakpoint.
 *
 * Wraps the shadcn `Sheet` primitive with a fixed header (title + close) and a
 * fixed footer (Cancel + Save). The body scrolls between them. Laid out as a
 * flex column (NOT `position: sticky`) because Radix `Sheet` uses transforms,
 * which break sticky positioning — see OpenSpec change
 * `mobile-responsive-parity`, design §`SheetForm` / risks.
 *
 * Uses `h-[100dvh]` (not `h-screen`) to avoid the iOS Safari URL-bar resize
 * jump. Consumers choose this host vs the desktop `Dialog` via `useIsMobile()`.
 */
export function SheetForm({
	open,
	onOpenChange,
	title,
	onSave,
	onCancel,
	isPending = false,
	saveDisabled = false,
	saveLabel = "Save",
	isDirty = false,
	children,
	bodyClassName,
}: SheetFormProps) {
	const [confirmDiscardOpen, setConfirmDiscardOpen] = React.useState(false);

	// Route every close path (Cancel, header X, Radix overlay/Escape) through one
	// guard so the dirty-confirm is applied uniformly (spec R4.5).
	const attemptClose = React.useCallback(() => {
		if (isDirty) {
			setConfirmDiscardOpen(true);
		} else {
			onCancel();
		}
	}, [isDirty, onCancel]);

	const handleOpenChange = React.useCallback(
		(next: boolean) => {
			if (!next) {
				attemptClose();
			} else {
				onOpenChange(next);
			}
		},
		[attemptClose, onOpenChange],
	);

	return (
		<Sheet open={open} onOpenChange={handleOpenChange}>
			<SheetContent
				side="bottom"
				showCloseButton={false}
				className="flex h-[100dvh] w-full flex-col gap-0 p-0 data-[side=bottom]:h-[100dvh] sm:max-w-full"
				onEscapeKeyDown={(e) => {
					// Prevent Radix's default Escape close so our guard runs instead.
					if (isDirty) {
						e.preventDefault();
						attemptClose();
					}
				}}
				onPointerDownOutside={(e) => {
					// Prevent overlay-click close so our guard runs instead.
					if (isDirty) {
						e.preventDefault();
						attemptClose();
					}
				}}
			>
				{/* Header — fixed at top */}
				<div className="flex h-14 shrink-0 items-center justify-between border-b border-border px-4">
					<SheetTitle className="font-heading text-base font-medium">
						{title}
					</SheetTitle>
					<Button
						variant="ghost"
						size="icon-sm"
						aria-label="Close"
						onClick={attemptClose}
					>
						<XIcon />
					</Button>
				</div>

				{/* Body — scrolls */}
				<div className={cn("flex-1 overflow-y-auto p-4", bodyClassName)}>
					{children}
				</div>

				{/* Footer — fixed at bottom */}
				<div className="flex shrink-0 items-center justify-end gap-2 border-t border-border bg-muted/50 p-4">
					<Button variant="outline" onClick={attemptClose} disabled={isPending}>
						Cancel
					</Button>
					<Button onClick={onSave} disabled={isPending || saveDisabled}>
						{isPending ? (
							<>
								<Loader2 className="animate-spin" />
								Saving…
							</>
						) : (
							saveLabel
						)}
					</Button>
				</div>
			</SheetContent>

			<ConfirmDialog
				open={confirmDiscardOpen}
				title="Discard changes?"
				message="You have unsaved changes. Discard them and close?"
				confirmLabel="Discard"
				onCancel={() => setConfirmDiscardOpen(false)}
				onConfirm={() => {
					setConfirmDiscardOpen(false);
					onCancel();
				}}
			/>
		</Sheet>
	);
}
