"use client";

import * as DialogPrimitive from "@radix-ui/react-dialog";
import { X } from "lucide-react";
import {
	type ComponentPropsWithoutRef,
	createContext,
	type Dispatch,
	type ElementRef,
	forwardRef,
	type HTMLAttributes,
	type PointerEvent,
	type ReactNode,
	type SetStateAction,
	useContext,
	useEffect,
	useRef,
	useState,
} from "react";
import { cn } from "~/lib/utils";

interface DialogContextValue {
	innerOpen: boolean;
	setInnerOpen: Dispatch<SetStateAction<boolean>>;
}

const DialogContext = createContext<DialogContextValue | undefined>(undefined);

function Dialog({ children }: { children: ReactNode }) {
	const [outerOpen, setOuterOpen] = useState(false);
	const [innerOpen, setInnerOpen] = useState(false);

	return (
		<DialogContext.Provider value={{ innerOpen, setInnerOpen }}>
			<DialogPrimitive.Root open={outerOpen} onOpenChange={setOuterOpen}>
				{children}
			</DialogPrimitive.Root>
		</DialogContext.Provider>
	);
}

const DialogTrigger = DialogPrimitive.Trigger;
const DialogPortal = DialogPrimitive.Portal;
const DialogClose = DialogPrimitive.Close;

const DialogOverlay = forwardRef<
	ElementRef<typeof DialogPrimitive.Overlay>,
	ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
>(({ className, ...props }, ref) => (
	<DialogPrimitive.Overlay
		ref={ref}
		className={cn(
			"bg-background/40 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 backdrop-blur-sm",
			className,
		)}
		{...props}
	/>
));
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;

const DialogContent = forwardRef<
	ElementRef<typeof DialogPrimitive.Content>,
	ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
>(({ className, children, ...props }, ref) => {
	const context = useContext(DialogContext);
	if (!context) throw new Error("DialogContent must be used within a Dialog");

	return (
		<DialogPortal>
			<DialogOverlay />
			<DialogPrimitive.Content
				ref={ref}
				className={cn(
					"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] fixed top-[50%] left-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:rounded-lg",
					context.innerOpen && "translate-y-[-55%] scale-[0.97]",
					className,
				)}
				{...props}
			>
				{children}
				<DialogClose className="ring-offset-background data-[state=open]:bg-accent data-[state=open]:text-muted-foreground focus:ring-ring absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none">
					<X className="size-4" />
					<span className="sr-only">Close</span>
				</DialogClose>
			</DialogPrimitive.Content>
		</DialogPortal>
	);
});
DialogContent.displayName = DialogPrimitive.Content.displayName;

function InnerDialog({ children }: { children: ReactNode }) {
	const context = useContext(DialogContext);
	if (!context) throw new Error("InnerDialog must be used within a Dialog");

	useEffect(() => {
		const handleEscapeKeyDown = (event: KeyboardEvent) => {
			if (event.key === "Escape" && context.innerOpen) {
				context.setInnerOpen(false);
				event.stopPropagation();
			}
		};

		document.addEventListener("keydown", handleEscapeKeyDown);
		return () => {
			document.removeEventListener("keydown", handleEscapeKeyDown);
		};
	}, [context.innerOpen, context.setInnerOpen]);

	return (
		<DialogPrimitive.Root
			open={context.innerOpen}
			onOpenChange={context.setInnerOpen}
		>
			{children}
		</DialogPrimitive.Root>
	);
}

const InnerDialogTrigger = DialogPrimitive.Trigger;
const InnerDialogClose = DialogPrimitive.Close;

interface InnerDialogContentProps
	extends ComponentPropsWithoutRef<typeof DialogPrimitive.Content> {
	position?: "default" | "bottom" | "top" | "left" | "right";
	draggable?: boolean;
}

const InnerDialogContent = forwardRef<
	ElementRef<typeof DialogPrimitive.Content>,
	InnerDialogContentProps
>(
	(
		{ className, children, position = "default", draggable = false, ...props },
		ref,
	) => {
		const context = useContext(DialogContext);
		if (!context)
			throw new Error("InnerDialogContent must be used within a Dialog");

		const [isDragging, setIsDragging] = useState(false);
		const [startY, setStartY] = useState(0);
		const [currentY, setCurrentY] = useState(0);
		const [isClosingByDrag, setIsClosingByDrag] = useState(false);
		const contentRef = useRef<HTMLDivElement>(null);

		useEffect(() => {
			if (context.innerOpen) {
				setCurrentY(0);
				setIsClosingByDrag(false);
			}
		}, [context.innerOpen]);

		const handlePointerDown = (e: PointerEvent<HTMLDivElement>) => {
			if (!draggable) return;
			setIsDragging(true);
			setStartY(e.clientY - currentY);
			e.currentTarget.setPointerCapture(e.pointerId);
		};

		const handlePointerMove = (e: PointerEvent<HTMLDivElement>) => {
			if (!isDragging || !draggable) return;
			const newY = e.clientY - startY;
			setCurrentY(newY > 0 ? newY : 0);
		};

		const handlePointerUp = () => {
			if (!draggable) return;
			setIsDragging(false);
			if (currentY > (contentRef.current?.offsetHeight || 0) / 2) {
				setIsClosingByDrag(true);
				context.setInnerOpen(false);
			} else {
				setCurrentY(0);
			}
		};

		return (
			<DialogPortal>
				<DialogPrimitive.Content
					ref={ref}
					onPointerDown={handlePointerDown}
					onPointerMove={handlePointerMove}
					onPointerUp={handlePointerUp}
					style={{
						transform: `translate(-50%, calc(-50% + ${currentY}px))`,
						transition: isDragging ? "none" : "transform 0.3s ease-out",
					}}
					className={cn(
						"bg-background fixed top-[50%] left-[50%] z-[60] grid w-full max-w-lg translate-x-[-50%] translate-y-[-45%] gap-4 rounded-lg border p-6 shadow-lg duration-200",
						isClosingByDrag
							? "data-[state=closed]:fade-out-0 data-[state=closed]:animate-none"
							: "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%]",
						position === "default" &&
							"data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%]",
						position === "bottom" &&
							"data-[state=closed]:slide-out-to-bottom-full data-[state=open]:slide-in-from-bottom-full",
						position === "top" &&
							"data-[state=closed]:slide-out-to-top-full data-[state=open]:slide-in-from-top-full",
						position === "left" &&
							"data-[state=closed]:slide-out-to-left-full data-[state=open]:slide-in-from-left-full",
						position === "right" &&
							"data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-right-full",
						draggable && "",
						className,
					)}
					{...props}
				>
					<div ref={contentRef}>{children}</div>
					<InnerDialogClose className="ring-offset-background data-[state=open]:bg-accent data-[state=open]:text-muted-foreground focus:ring-ring absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none">
						<X className="size-4" />
						<span className="sr-only">Close</span>
					</InnerDialogClose>
				</DialogPrimitive.Content>
			</DialogPortal>
		);
	},
);
InnerDialogContent.displayName = "InnerDialogContent";

const InnerDialogHeader = ({
	className,
	...props
}: HTMLAttributes<HTMLDivElement>) => (
	<div
		className={cn(
			"flex flex-col space-y-1.5 text-center sm:text-left",
			className,
		)}
		{...props}
	/>
);
InnerDialogHeader.displayName = "InnerDialogHeader";

const InnerDialogFooter = ({
	className,
	...props
}: HTMLAttributes<HTMLDivElement>) => (
	<div
		className={cn("flex flex-col-reverse sm:flex-row sm:space-x-2", className)}
		{...props}
	/>
);
InnerDialogFooter.displayName = "InnerDialogFooter";

const InnerDialogTitle = forwardRef<
	ElementRef<typeof DialogPrimitive.Title>,
	ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
>(({ className, ...props }, ref) => (
	<DialogPrimitive.Title
		ref={ref}
		className={cn(
			"text-lg leading-none font-semibold tracking-tight",
			className,
		)}
		{...props}
	/>
));
InnerDialogTitle.displayName = "InnerDialogTitle";

const InnerDialogDescription = forwardRef<
	ElementRef<typeof DialogPrimitive.Description>,
	ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
>(({ className, ...props }, ref) => (
	<DialogPrimitive.Description
		ref={ref}
		className={cn("text-muted-foreground text-sm", className)}
		{...props}
	/>
));
InnerDialogDescription.displayName = "InnerDialogDescription";

const DialogHeader = ({
	className,
	...props
}: HTMLAttributes<HTMLDivElement>) => (
	<div
		className={cn(
			"flex flex-col space-y-1.5 text-center sm:text-left",
			className,
		)}
		{...props}
	/>
);
DialogHeader.displayName = "DialogHeader";

const DialogFooter = ({
	className,
	...props
}: HTMLAttributes<HTMLDivElement>) => (
	<div
		className={cn("flex flex-col-reverse sm:flex-row sm:space-x-2", className)}
		{...props}
	/>
);
DialogFooter.displayName = "DialogFooter";

const DialogTitle = forwardRef<
	ElementRef<typeof DialogPrimitive.Title>,
	ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
>(({ className, ...props }, ref) => (
	<DialogPrimitive.Title
		ref={ref}
		className={cn(
			"text-lg leading-none font-semibold tracking-tight",
			className,
		)}
		{...props}
	/>
));
DialogTitle.displayName = DialogPrimitive.Title.displayName;

const DialogDescription = forwardRef<
	ElementRef<typeof DialogPrimitive.Description>,
	ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
>(({ className, ...props }, ref) => (
	<DialogPrimitive.Description
		ref={ref}
		className={cn("text-muted-foreground text-sm", className)}
		{...props}
	/>
));
DialogDescription.displayName = DialogPrimitive.Description.displayName;

export type { InnerDialogContentProps };
export {
	Dialog,
	DialogTrigger,
	DialogContent,
	DialogHeader,
	DialogFooter,
	DialogTitle,
	DialogDescription,
	DialogClose,
	InnerDialog,
	InnerDialogTrigger,
	InnerDialogContent,
	InnerDialogHeader,
	InnerDialogFooter,
	InnerDialogTitle,
	InnerDialogDescription,
	InnerDialogClose,
	DialogPortal,
	DialogOverlay,
};
