"use client";

import { Slot } from "@radix-ui/react-slot";
import {
	FileArchiveIcon,
	FileAudioIcon,
	FileCodeIcon,
	FileCogIcon,
	FileIcon,
	FileTextIcon,
	FileVideoIcon,
} from "lucide-react";
import {
	type ChangeEvent,
	type ComponentPropsWithoutRef,
	createContext,
	type DragEvent,
	forwardRef,
	type KeyboardEvent,
	type MouseEvent,
	type ReactNode,
	type RefObject,
	useCallback,
	useContext,
	useEffect,
	useId,
	useLayoutEffect,
	useMemo,
	useRef,
	useSyncExternalStore,
} from "react";
import { cn } from "~/lib/utils";

const ROOT_NAME = "FileUpload";
const DROPZONE_NAME = "FileUploadDropzone";
const TRIGGER_NAME = "FileUploadTrigger";
const LIST_NAME = "FileUploadList";
const ITEM_NAME = "FileUploadItem";
const ITEM_PREVIEW_NAME = "FileUploadItemPreview";
const ITEM_METADATA_NAME = "FileUploadItemMetadata";
const ITEM_PROGRESS_NAME = "FileUploadItemProgress";
const ITEM_DELETE_NAME = "FileUploadItemDelete";
const CLEAR_NAME = "FileUploadClear";

const FILE_UPLOAD_ERRORS = {
	[ROOT_NAME]: `\`${ROOT_NAME}\` must be used as root component`,
	[DROPZONE_NAME]: `\`${DROPZONE_NAME}\` must be within \`${ROOT_NAME}\``,
	[TRIGGER_NAME]: `\`${TRIGGER_NAME}\` must be within \`${ROOT_NAME}\``,
	[LIST_NAME]: `\`${LIST_NAME}\` must be within \`${ROOT_NAME}\``,
	[ITEM_NAME]: `\`${ITEM_NAME}\` must be within \`${ROOT_NAME}\``,
	[ITEM_PREVIEW_NAME]: `\`${ITEM_PREVIEW_NAME}\` must be within \`${ITEM_NAME}\``,
	[ITEM_METADATA_NAME]: `\`${ITEM_METADATA_NAME}\` must be within \`${ITEM_NAME}\``,
	[ITEM_PROGRESS_NAME]: `\`${ITEM_PROGRESS_NAME}\` must be within \`${ITEM_NAME}\``,
	[ITEM_DELETE_NAME]: `\`${ITEM_DELETE_NAME}\` must be within \`${ITEM_NAME}\``,
	[CLEAR_NAME]: `\`${CLEAR_NAME}\` must be within \`${ROOT_NAME}\``,
} as const;

const useIsomorphicLayoutEffect =
	typeof window !== "undefined" ? useLayoutEffect : useEffect;

function useAsRef<T>(data: T) {
	const ref = useRef<T>(data);
	useIsomorphicLayoutEffect(() => {
		ref.current = data;
	});
	return ref;
}

function useLazyRef<T>(fn: () => T) {
	const ref = useRef<T | null>(null);
	if (ref.current === null) {
		ref.current = fn();
	}
	return ref as RefObject<T>;
}

type Direction = "ltr" | "rtl";

const DirectionContext = createContext<Direction | undefined>(undefined);

function useDirection(dirProp?: Direction): Direction {
	const contextDir = useContext(DirectionContext);
	return dirProp ?? contextDir ?? "ltr";
}

interface FileState {
	file: File;
	progress: number;
	error?: string;
	status: "idle" | "uploading" | "error" | "success";
}

interface StoreState {
	files: Map<File, FileState>;
	dragOver: boolean;
	invalid: boolean;
}

type StoreAction =
	| { variant: "ADD_FILES"; files: File[] }
	| { variant: "SET_FILES"; files: File[] }
	| { variant: "SET_PROGRESS"; file: File; progress: number }
	| { variant: "SET_SUCCESS"; file: File }
	| { variant: "SET_ERROR"; file: File; error: string }
	| { variant: "REMOVE_FILE"; file: File }
	| { variant: "SET_DRAG_OVER"; dragOver: boolean }
	| { variant: "SET_INVALID"; invalid: boolean }
	| { variant: "CLEAR" };

function createStore(
	listeners: Set<() => void>,
	files: Map<File, FileState>,
	onValueChange?: (files: File[]) => void,
	invalid?: boolean,
) {
	const initialState: StoreState = {
		dragOver: false,
		files,
		invalid: invalid ?? false,
	};

	let state = initialState;

	function reducer(state: StoreState, action: StoreAction): StoreState {
		switch (action.variant) {
			case "ADD_FILES": {
				for (const file of action.files) {
					files.set(file, {
						file,
						progress: 0,
						status: "idle",
					});
				}

				if (onValueChange) {
					const fileList = Array.from(files.values()).map(
						(fileState) => fileState.file,
					);
					onValueChange(fileList);
				}
				return { ...state, files };
			}

			case "SET_FILES": {
				const newFileSet = new Set(action.files);
				for (const existingFile of files.keys()) {
					if (!newFileSet.has(existingFile)) {
						files.delete(existingFile);
					}
				}

				for (const file of action.files) {
					const existingState = files.get(file);
					if (!existingState) {
						files.set(file, {
							file,
							progress: 0,
							status: "idle",
						});
					}
				}
				return { ...state, files };
			}

			case "SET_PROGRESS": {
				const fileState = files.get(action.file);
				if (fileState) {
					files.set(action.file, {
						...fileState,
						progress: action.progress,
						status: "uploading",
					});
				}
				return { ...state, files };
			}

			case "SET_SUCCESS": {
				const fileState = files.get(action.file);
				if (fileState) {
					files.set(action.file, {
						...fileState,
						progress: 100,
						status: "success",
					});
				}
				return { ...state, files };
			}

			case "SET_ERROR": {
				const fileState = files.get(action.file);
				if (fileState) {
					files.set(action.file, {
						...fileState,
						error: action.error,
						status: "error",
					});
				}
				return { ...state, files };
			}

			case "REMOVE_FILE": {
				files.delete(action.file);

				if (onValueChange) {
					const fileList = Array.from(files.values()).map(
						(fileState) => fileState.file,
					);
					onValueChange(fileList);
				}
				return { ...state, files };
			}

			case "SET_DRAG_OVER": {
				return { ...state, dragOver: action.dragOver };
			}

			case "SET_INVALID": {
				return { ...state, invalid: action.invalid };
			}

			case "CLEAR": {
				files.clear();
				if (onValueChange) {
					onValueChange([]);
				}
				return { ...state, files, invalid: false };
			}

			default:
				return state;
		}
	}

	function getState() {
		return state;
	}

	function dispatch(action: StoreAction) {
		state = reducer(state, action);
		for (const listener of listeners) {
			listener();
		}
	}

	function subscribe(listener: () => void) {
		listeners.add(listener);
		return () => listeners.delete(listener);
	}

	return { dispatch, getState, subscribe };
}

const StoreContext = createContext<ReturnType<typeof createStore> | null>(null);
StoreContext.displayName = ROOT_NAME;

function useStoreContext(name: keyof typeof FILE_UPLOAD_ERRORS) {
	const context = useContext(StoreContext);
	if (!context) {
		throw new Error(FILE_UPLOAD_ERRORS[name]);
	}
	return context;
}

function useStore<T>(selector: (state: StoreState) => T): T {
	const store = useStoreContext(ROOT_NAME);

	const lastValueRef = useLazyRef<{ value: T; state: StoreState } | null>(
		() => null,
	);

	const getSnapshot = useCallback(() => {
		const state = store.getState();
		const prevValue = lastValueRef.current;

		if (prevValue && prevValue.state === state) {
			return prevValue.value;
		}

		const nextValue = selector(state);
		lastValueRef.current = { state, value: nextValue };
		return nextValue;
	}, [store, selector, lastValueRef]);

	return useSyncExternalStore(store.subscribe, getSnapshot, getSnapshot);
}

interface FileUploadContextValue {
	inputId: string;
	dropzoneId: string;
	listId: string;
	labelId: string;
	disabled: boolean;
	dir: Direction;
	inputRef: RefObject<HTMLInputElement | null>;
}

const FileUploadContext = createContext<FileUploadContextValue | null>(null);

function useFileUploadContext(name: keyof typeof FILE_UPLOAD_ERRORS) {
	const context = useContext(FileUploadContext);
	if (!context) {
		throw new Error(FILE_UPLOAD_ERRORS[name]);
	}
	return context;
}

interface FileUploadRootProps
	extends Omit<ComponentPropsWithoutRef<"div">, "defaultValue" | "onChange"> {
	value?: File[];
	defaultValue?: File[];
	onValueChange?: (files: File[]) => void;
	onAccept?: (files: File[]) => void;
	onFileAccept?: (file: File) => void;
	onFileReject?: (file: File, message: string) => void;
	onFileValidate?: (file: File) => string | null | undefined;
	onUpload?: (
		files: File[],
		options: {
			onProgress: (file: File, progress: number) => void;
			onSuccess: (file: File) => void;
			onError: (file: File, error: Error) => void;
		},
	) => Promise<void> | void;
	accept?: string;
	maxFiles?: number;
	maxSize?: number;
	dir?: Direction;
	label?: string;
	name?: string;
	asChild?: boolean;
	disabled?: boolean;
	invalid?: boolean;
	multiple?: boolean;
	required?: boolean;
}

const FileUploadRoot = forwardRef<HTMLDivElement, FileUploadRootProps>(
	(props, forwardedRef) => {
		const {
			value,
			defaultValue,
			onValueChange,
			//onAccept,
			//onFileAccept,
			//onFileReject,
			//onFileValidate,
			//onUpload,
			accept,
			//maxFiles,
			//maxSize,
			dir: dirProp,
			label,
			name,
			asChild,
			disabled = false,
			invalid = false,
			multiple = false,
			required = false,
			children,
			className,
			...rootProps
		} = props;

		const inputId = useId();
		const dropzoneId = useId();
		const listId = useId();
		const labelId = useId();

		const dir = useDirection(dirProp);
		const propsRef = useAsRef(props);
		const listeners = useLazyRef(() => new Set<() => void>()).current;
		const files = useLazyRef<Map<File, FileState>>(() => new Map()).current;
		const inputRef = useRef<HTMLInputElement>(null);
		const isControlled = value !== undefined;

		const store = useMemo(
			() => createStore(listeners, files, onValueChange, invalid),
			[listeners, files, onValueChange, invalid],
		);

		const contextValue = useMemo<FileUploadContextValue>(
			() => ({
				dir,
				disabled,
				dropzoneId,
				inputId,
				inputRef,
				labelId,
				listId,
			}),
			[dropzoneId, inputId, listId, labelId, dir, disabled],
		);

		useEffect(() => {
			if (isControlled) {
				store.dispatch({ files: value, variant: "SET_FILES" });
			} else if (
				defaultValue &&
				defaultValue.length > 0 &&
				!store.getState().files.size
			) {
				store.dispatch({ files: defaultValue, variant: "SET_FILES" });
			}
		}, [value, defaultValue, isControlled, store]);

		const onFilesChange = useCallback(
			(originalFiles: File[]) => {
				if (propsRef.current.disabled) return;

				let filesToProcess = [...originalFiles];
				let invalid = false;

				if (propsRef.current.maxFiles) {
					const currentCount = store.getState().files.size;
					const remainingSlotCount = Math.max(
						0,
						propsRef.current.maxFiles - currentCount,
					);

					if (remainingSlotCount < filesToProcess.length) {
						const rejectedFiles = filesToProcess.slice(remainingSlotCount);
						invalid = true;

						filesToProcess = filesToProcess.slice(0, remainingSlotCount);

						for (const file of rejectedFiles) {
							let rejectionMessage = `Maximum ${propsRef.current.maxFiles} files allowed`;

							if (propsRef.current.onFileValidate) {
								const validationMessage = propsRef.current.onFileValidate(file);
								if (validationMessage) {
									rejectionMessage = validationMessage;
								}
							}

							propsRef.current.onFileReject?.(file, rejectionMessage);
						}
					}
				}

				const acceptedFiles: File[] = [];
				const rejectedFiles: { file: File; message: string }[] = [];

				for (const file of filesToProcess) {
					let rejected = false;
					let rejectionMessage = "";

					if (propsRef.current.onFileValidate) {
						const validationMessage = propsRef.current.onFileValidate(file);
						if (validationMessage) {
							rejectionMessage = validationMessage;
							propsRef.current.onFileReject?.(file, rejectionMessage);
							rejected = true;
							invalid = true;
							continue;
						}
					}

					if (propsRef.current.accept) {
						const acceptTypes = propsRef.current.accept
							.split(",")
							.map((t) => t.trim());
						const fileType = file.type;
						const fileExtension = `.${file.name.split(".").pop()}`;

						if (
							!acceptTypes.some(
								(type) =>
									type === fileType ||
									type === fileExtension ||
									(type.includes("/*") &&
										fileType.startsWith(type.replace("/*", "/"))),
							)
						) {
							rejectionMessage = "File type not accepted";
							propsRef.current.onFileReject?.(file, rejectionMessage);
							rejected = true;
							invalid = true;
						}
					}

					if (
						propsRef.current.maxSize &&
						file.size > propsRef.current.maxSize
					) {
						rejectionMessage = "File too large";
						propsRef.current.onFileReject?.(file, rejectionMessage);
						rejected = true;
						invalid = true;
					}

					if (!rejected) {
						acceptedFiles.push(file);
					} else {
						rejectedFiles.push({ file, message: rejectionMessage });
					}
				}

				if (invalid) {
					store.dispatch({ invalid, variant: "SET_INVALID" });
					setTimeout(() => {
						store.dispatch({ invalid: false, variant: "SET_INVALID" });
					}, 2000);
				}

				if (acceptedFiles.length > 0) {
					store.dispatch({ files: acceptedFiles, variant: "ADD_FILES" });

					if (isControlled && propsRef.current.onValueChange) {
						const currentFiles = Array.from(
							store.getState().files.values(),
						).map((f) => f.file);
						propsRef.current.onValueChange([...currentFiles]);
					}

					if (propsRef.current.onAccept) {
						propsRef.current.onAccept(acceptedFiles);
					}

					for (const file of acceptedFiles) {
						propsRef.current.onFileAccept?.(file);
					}

					if (propsRef.current.onUpload) {
						requestAnimationFrame(() => {
							onFilesUpload(acceptedFiles).catch((e) => {
								throw new Error((e as Error).message);
							});
						});
					}
				}
			},
			[store, isControlled, propsRef],
		);

		const onFilesUpload = useCallback(
			async (files: File[]) => {
				try {
					for (const file of files) {
						store.dispatch({ file, progress: 0, variant: "SET_PROGRESS" });
					}

					if (propsRef.current.onUpload) {
						await propsRef.current.onUpload(files, {
							onError: (file, error) => {
								store.dispatch({
									error: error.message ?? "Upload failed",
									file,
									variant: "SET_ERROR",
								});
							},
							onProgress: (file, progress) => {
								store.dispatch({
									file,
									progress: Math.min(Math.max(0, progress), 100),
									variant: "SET_PROGRESS",
								});
							},
							onSuccess: (file) => {
								store.dispatch({ file, variant: "SET_SUCCESS" });
							},
						});
					} else {
						for (const file of files) {
							store.dispatch({ file, variant: "SET_SUCCESS" });
						}
					}
				} catch (error) {
					const errorMessage =
						error instanceof Error ? error.message : "Upload failed";
					for (const file of files) {
						store.dispatch({
							error: errorMessage,
							file,
							variant: "SET_ERROR",
						});
					}
				}
			},
			[store, propsRef.current.onUpload],
		);

		const onInputChange = useCallback(
			(event: ChangeEvent<HTMLInputElement>) => {
				const files = Array.from(event.target.files ?? []);
				onFilesChange(files);
				event.target.value = "";
			},
			[onFilesChange],
		);

		const RootPrimitive = asChild ? Slot : "div";

		return (
			<DirectionContext.Provider value={dir}>
				<StoreContext.Provider value={store}>
					<FileUploadContext.Provider value={contextValue}>
						<RootPrimitive
							data-disabled={disabled ? "" : undefined}
							data-slot="file-upload"
							dir={dir}
							{...rootProps}
							className={cn("relative flex flex-col gap-2", className)}
							ref={forwardedRef}
						>
							{children}
							<input
								accept={accept}
								aria-describedby={dropzoneId}
								aria-labelledby={labelId}
								className="sr-only"
								disabled={disabled}
								id={inputId}
								multiple={multiple}
								name={name}
								onChange={onInputChange}
								ref={inputRef}
								required={required}
								tabIndex={-1}
								type="file"
							/>
							<span className="sr-only" id={labelId}>
								{label ?? "File upload"}
							</span>
						</RootPrimitive>
					</FileUploadContext.Provider>
				</StoreContext.Provider>
			</DirectionContext.Provider>
		);
	},
);
FileUploadRoot.displayName = ROOT_NAME;

interface FileUploadDropzoneProps extends ComponentPropsWithoutRef<"div"> {
	asChild?: boolean;
}

const FileUploadDropzone = forwardRef<HTMLDivElement, FileUploadDropzoneProps>(
	(props, forwardedRef) => {
		const { asChild, className, ...dropzoneProps } = props;

		const context = useFileUploadContext(DROPZONE_NAME);
		const store = useStoreContext(DROPZONE_NAME);
		const dragOver = useStore((state) => state.dragOver);
		const invalid = useStore((state) => state.invalid);
		const propsRef = useAsRef(dropzoneProps);

		const onClick = useCallback(
			(event: MouseEvent<HTMLDivElement>) => {
				propsRef.current?.onClick?.(event);

				if (event.defaultPrevented) return;

				const target = event.target;

				const isFromTrigger =
					target instanceof HTMLElement &&
					target.closest('[data-slot="file-upload-trigger"]');

				if (!isFromTrigger) {
					context.inputRef.current?.click();
				}
			},
			[context.inputRef, propsRef],
		);

		const onDragOver = useCallback(
			(event: DragEvent<HTMLDivElement>) => {
				propsRef.current?.onDragOver?.(event);

				if (event.defaultPrevented) return;

				event.preventDefault();
				store.dispatch({ dragOver: true, variant: "SET_DRAG_OVER" });
			},
			[store, propsRef.current.onDragOver],
		);

		const onDragEnter = useCallback(
			(event: DragEvent<HTMLDivElement>) => {
				propsRef.current?.onDragEnter?.(event);

				if (event.defaultPrevented) return;

				event.preventDefault();
				store.dispatch({ dragOver: true, variant: "SET_DRAG_OVER" });
			},
			[store, propsRef.current.onDragEnter],
		);

		const onDragLeave = useCallback(
			(event: DragEvent<HTMLDivElement>) => {
				propsRef.current?.onDragLeave?.(event);

				if (event.defaultPrevented) return;

				event.preventDefault();
				store.dispatch({ dragOver: false, variant: "SET_DRAG_OVER" });
			},
			[store, propsRef.current.onDragLeave],
		);

		const onDrop = useCallback(
			(event: DragEvent<HTMLDivElement>) => {
				propsRef.current?.onDrop?.(event);

				if (event.defaultPrevented) return;

				event.preventDefault();
				store.dispatch({ dragOver: false, variant: "SET_DRAG_OVER" });

				const files = Array.from(event.dataTransfer.files);
				const inputElement = context.inputRef.current;
				if (!inputElement) return;

				const dataTransfer = new DataTransfer();
				for (const file of files) {
					dataTransfer.items.add(file);
				}

				inputElement.files = dataTransfer.files;
				inputElement.dispatchEvent(new Event("change", { bubbles: true }));
			},
			[store, context.inputRef, propsRef.current.onDrop],
		);

		const onKeyDown = useCallback(
			(event: KeyboardEvent<HTMLDivElement>) => {
				propsRef.current?.onKeyDown?.(event);

				if (
					!event.defaultPrevented &&
					(event.key === "Enter" || event.key === " ")
				) {
					event.preventDefault();
					context.inputRef.current?.click();
				}
			},
			[context.inputRef, propsRef.current.onKeyDown],
		);

		const DropzonePrimitive = asChild ? Slot : "div";

		return (
			<DropzonePrimitive
				aria-controls={`${context.inputId} ${context.listId}`}
				data-disabled={context.disabled ? "" : undefined}
				data-dragging={dragOver ? "" : undefined}
				//aria-disabled={context.disabled}
				//aria-invalid={invalid}
				data-invalid={invalid ? "" : undefined}
				data-slot="file-upload-dropzone"
				dir={context.dir}
				id={context.dropzoneId}
				role="region"
				{...dropzoneProps}
				className={cn(
					"data-[dragging]:border-primary data-[invalid]:border-destructive data-[invalid]:ring-destructive/20 hover:bg-accent/30 focus-visible:border-ring/50 relative flex flex-col items-center justify-center gap-2 rounded-lg border-2 border-dashed p-6 outline-hidden transition-colors select-none data-[disabled]:pointer-events-none",
					className,
				)}
				onClick={onClick}
				onDragEnter={onDragEnter}
				onDragLeave={onDragLeave}
				onDragOver={onDragOver}
				onDrop={onDrop}
				onKeyDown={onKeyDown}
				ref={forwardedRef}
				tabIndex={context.disabled ? undefined : 0}
			/>
		);
	},
);
FileUploadDropzone.displayName = DROPZONE_NAME;

interface FileUploadTriggerProps extends ComponentPropsWithoutRef<"button"> {
	asChild?: boolean;
}

const FileUploadTrigger = forwardRef<HTMLButtonElement, FileUploadTriggerProps>(
	(props, forwardedRef) => {
		const { asChild, ...triggerProps } = props;
		const context = useFileUploadContext(TRIGGER_NAME);
		const propsRef = useAsRef(triggerProps);

		const onClick = useCallback(
			(event: MouseEvent<HTMLButtonElement>) => {
				propsRef.current?.onClick?.(event);

				if (event.defaultPrevented) return;

				context.inputRef.current?.click();
			},
			[context.inputRef, propsRef.current],
		);

		const TriggerPrimitive = asChild ? Slot : "button";

		return (
			<TriggerPrimitive
				aria-controls={context.inputId}
				data-disabled={context.disabled ? "" : undefined}
				data-slot="file-upload-trigger"
				type="button"
				{...triggerProps}
				disabled={context.disabled}
				onClick={onClick}
				ref={forwardedRef}
			/>
		);
	},
);
FileUploadTrigger.displayName = TRIGGER_NAME;

interface FileUploadListProps extends ComponentPropsWithoutRef<"div"> {
	orientation?: "horizontal" | "vertical";
	asChild?: boolean;
	forceMount?: boolean;
}

const FileUploadList = forwardRef<HTMLDivElement, FileUploadListProps>(
	(props, forwardedRef) => {
		const {
			className,
			orientation = "vertical",
			asChild,
			forceMount,
			...listProps
		} = props;

		const context = useFileUploadContext(LIST_NAME);

		const shouldRender =
			forceMount || useStore((state) => state.files.size > 0);

		if (!shouldRender) return null;

		const ListPrimitive = asChild ? Slot : "div";

		return (
			<ListPrimitive
				data-orientation={orientation}
				data-slot="file-upload-list"
				//aria-orientation={orientation}
				data-state={shouldRender ? "active" : "inactive"}
				dir={context.dir}
				id={context.listId}
				role="list"
				{...listProps}
				className={cn(
					"data-[state=active]:animate-in data-[state=inactive]:animate-out data-[state=active]:fade-in-0 data-[state=inactive]:fade-out-0 data-[state=active]:slide-in-from-top-2 data-[state=inactive]:slide-out-to-top-2 flex flex-col gap-2",
					orientation === "horizontal" && "flex-row overflow-x-auto p-1.5",
					className,
				)}
				ref={forwardedRef}
			/>
		);
	},
);
FileUploadList.displayName = LIST_NAME;

interface FileUploadItemContextValue {
	id: string;
	fileState: FileState | undefined;
	nameId: string;
	sizeId: string;
	statusId: string;
	messageId: string;
}

const FileUploadItemContext = createContext<FileUploadItemContextValue | null>(
	null,
);

function useFileUploadItemContext(name: keyof typeof FILE_UPLOAD_ERRORS) {
	const context = useContext(FileUploadItemContext);
	if (!context) {
		throw new Error(FILE_UPLOAD_ERRORS[name]);
	}
	return context;
}

interface FileUploadItemProps extends ComponentPropsWithoutRef<"div"> {
	value: File;
	asChild?: boolean;
}

const FileUploadItem = forwardRef<HTMLDivElement, FileUploadItemProps>(
	(props, forwardedRef) => {
		const { value, asChild, className, ...itemProps } = props;

		const id = useId();
		const statusId = `${id}-status`;
		const nameId = `${id}-name`;
		const sizeId = `${id}-size`;
		const messageId = `${id}-message`;

		const context = useFileUploadContext(ITEM_NAME);
		const fileState = useStore((state) => state.files.get(value));
		const fileCount = useStore((state) => state.files.size);
		const fileIndex = useStore((state) => {
			const files = Array.from(state.files.keys());
			return files.indexOf(value) + 1;
		});

		const itemContext = useMemo(
			() => ({
				fileState,
				id,
				messageId,
				nameId,
				sizeId,
				statusId,
			}),
			[id, fileState, statusId, nameId, sizeId, messageId],
		);

		if (!fileState) return null;

		const statusText = (() => {
			if (fileState.error) return `Error: ${fileState.error}`;
			if (fileState.status === "uploading")
				return `Uploading: ${fileState.progress}% complete`;
			if (fileState.status === "success") return "Upload complete";
			return "Ready to upload";
		})();

		const ItemPrimitive = asChild ? Slot : "div";

		return (
			<FileUploadItemContext.Provider value={itemContext}>
				<ItemPrimitive
					aria-describedby={`${nameId} ${sizeId} ${statusId} ${
						fileState.error ? messageId : ""
					}`}
					aria-labelledby={nameId}
					aria-posinset={fileIndex}
					aria-setsize={fileCount}
					data-slot="file-upload-item"
					dir={context.dir}
					id={id}
					role="listitem"
					{...itemProps}
					className={cn(
						"relative flex items-center gap-2.5 rounded-md border p-3 has-[_[data-slot=file-upload-progress]]:flex-col has-[_[data-slot=file-upload-progress]]:items-start",
						className,
					)}
					ref={forwardedRef}
				>
					{props.children}
					<span className="sr-only" id={statusId}>
						{statusText}
					</span>
				</ItemPrimitive>
			</FileUploadItemContext.Provider>
		);
	},
);
FileUploadItem.displayName = ITEM_NAME;

function formatBytes(bytes: number) {
	if (bytes === 0) return "0 B";
	const sizes = ["B", "KB", "MB", "GB", "TB"];
	const i = Math.floor(Math.log(bytes) / Math.log(1024));
	return `${(bytes / 1024 ** i).toFixed(i ? 1 : 0)} ${sizes[i]}`;
}

function getFileIcon(file: File) {
	const type = file.type;
	const extension = file.name.split(".").pop()?.toLowerCase() ?? "";

	if (type.startsWith("video/")) {
		return <FileVideoIcon />;
	}

	if (type.startsWith("audio/")) {
		return <FileAudioIcon />;
	}

	if (
		type.startsWith("text/") ||
		["txt", "md", "rtf", "pdf"].includes(extension)
	) {
		return <FileTextIcon />;
	}

	if (
		[
			"html",
			"css",
			"js",
			"jsx",
			"ts",
			"tsx",
			"json",
			"xml",
			"php",
			"py",
			"rb",
			"java",
			"c",
			"cpp",
			"cs",
		].includes(extension)
	) {
		return <FileCodeIcon />;
	}

	if (["zip", "rar", "7z", "tar", "gz", "bz2"].includes(extension)) {
		return <FileArchiveIcon />;
	}

	if (
		["exe", "msi", "app", "apk", "deb", "rpm"].includes(extension) ||
		type.startsWith("application/")
	) {
		return <FileCogIcon />;
	}

	return <FileIcon />;
}

interface FileUploadItemPreviewProps extends ComponentPropsWithoutRef<"div"> {
	render?: (file: File) => ReactNode;
	asChild?: boolean;
}

const FileUploadItemPreview = forwardRef<
	HTMLDivElement,
	FileUploadItemPreviewProps
>((props, forwardedRef) => {
	const { render, asChild, children, className, ...previewProps } = props;

	const itemContext = useFileUploadItemContext(ITEM_PREVIEW_NAME);

	const isImage = itemContext.fileState?.file.type.startsWith("image/");

	const onPreviewRender = useCallback(
		(file: File) => {
			if (render) return render(file);

			if (isImage) {
				return (
					// biome-ignore lint/a11y/noNoninteractiveElementInteractions: <intended>
					<img
						alt={file.name}
						className="size-full rounded-sm object-cover"
						onLoad={(event) => {
							if (!(event.target instanceof HTMLImageElement)) return;
							URL.revokeObjectURL(event.target.src);
						}}
						src={URL.createObjectURL(file)}
					/>
				);
			}

			return getFileIcon(file);
		},
		[isImage, render],
	);

	if (!itemContext.fileState) return null;

	const ItemPreviewPrimitive = asChild ? Slot : "div";

	return (
		<ItemPreviewPrimitive
			aria-labelledby={itemContext.nameId}
			data-slot="file-upload-preview"
			{...previewProps}
			className={cn(
				"relative flex size-10 shrink-0 items-center justify-center rounded-md",
				isImage ? "object-cover" : "bg-accent/50 [&>svg]:size-7",
				className,
			)}
			ref={forwardedRef}
		>
			{onPreviewRender(itemContext.fileState.file)}
			{children}
		</ItemPreviewPrimitive>
	);
});
FileUploadItemPreview.displayName = ITEM_PREVIEW_NAME;

interface FileUploadItemMetadataProps extends ComponentPropsWithoutRef<"div"> {
	asChild?: boolean;
}

const FileUploadItemMetadata = forwardRef<
	HTMLDivElement,
	FileUploadItemMetadataProps
>((props, forwardedRef) => {
	const { asChild, children, className, ...metadataProps } = props;

	const context = useFileUploadContext(ITEM_METADATA_NAME);
	const itemContext = useFileUploadItemContext(ITEM_METADATA_NAME);

	if (!itemContext.fileState) return null;

	const ItemMetadataPrimitive = asChild ? Slot : "div";

	return (
		<ItemMetadataPrimitive
			data-slot="file-upload-metadata"
			dir={context.dir}
			{...metadataProps}
			className={cn("flex min-w-0 flex-1 flex-col", className)}
			ref={forwardedRef}
		>
			{children ?? (
				<>
					<span
						className="truncate text-sm font-medium"
						id={itemContext.nameId}
					>
						{itemContext.fileState.file.name}
					</span>
					<span
						className="text-muted-foreground text-xs"
						id={itemContext.sizeId}
					>
						{formatBytes(itemContext.fileState.file.size)}
					</span>
					{itemContext.fileState.error && (
						<span
							className="text-destructive text-xs"
							id={itemContext.messageId}
						>
							{itemContext.fileState.error}
						</span>
					)}
				</>
			)}
		</ItemMetadataPrimitive>
	);
});
FileUploadItemMetadata.displayName = ITEM_METADATA_NAME;

interface FileUploadItemProgressProps extends ComponentPropsWithoutRef<"div"> {
	asChild?: boolean;
	circular?: boolean;
	size?: number;
}

const FileUploadItemProgress = forwardRef<
	HTMLDivElement,
	FileUploadItemProgressProps
>((props, forwardedRef) => {
	const { circular, size = 40, asChild, className, ...progressProps } = props;

	const itemContext = useFileUploadItemContext(ITEM_PROGRESS_NAME);

	if (!itemContext.fileState) return null;

	const ItemProgressPrimitive = asChild ? Slot : "div";

	if (circular) {
		if (itemContext.fileState.status === "success") return null;

		const circumference = 2 * Math.PI * ((size - 4) / 2);
		const strokeDashoffset =
			circumference - (itemContext.fileState.progress / 100) * circumference;

		return (
			<ItemProgressPrimitive
				aria-labelledby={itemContext.nameId}
				aria-valuemax={100}
				aria-valuemin={0}
				aria-valuenow={itemContext.fileState.progress}
				aria-valuetext={`${itemContext.fileState.progress}%`}
				data-slot="file-upload-progress"
				role="progressbar"
				{...progressProps}
				className={cn(
					"absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2",
					className,
				)}
				ref={forwardedRef}
			>
				<svg
					className="rotate-[-90deg] transform"
					fill="none"
					height={size}
					stroke="currentColor"
					viewBox={`0 0 ${size} ${size}`}
					width={size}
				>
					<title>Progress</title>
					<circle
						className="text-primary/20"
						cx={size / 2}
						cy={size / 2}
						r={(size - 4) / 2}
						strokeWidth="2"
					/>
					<circle
						className="text-primary transition-all"
						cx={size / 2}
						cy={size / 2}
						r={(size - 4) / 2}
						strokeDasharray={circumference}
						strokeDashoffset={strokeDashoffset}
						strokeLinecap="round"
						strokeWidth="2"
					/>
				</svg>
			</ItemProgressPrimitive>
		);
	}

	return (
		<ItemProgressPrimitive
			aria-labelledby={itemContext.nameId}
			aria-valuemax={100}
			aria-valuemin={0}
			aria-valuenow={itemContext.fileState.progress}
			aria-valuetext={`${itemContext.fileState.progress}%`}
			data-slot="file-upload-progress"
			role="progressbar"
			{...progressProps}
			className={cn(
				"bg-primary/20 relative h-1.5 w-full overflow-hidden rounded-full",
				className,
			)}
			ref={forwardedRef}
		>
			<div
				className="bg-primary h-full w-full flex-1 transition-all"
				style={{
					transform: `translateX(-${100 - itemContext.fileState.progress}%)`,
				}}
			/>
		</ItemProgressPrimitive>
	);
});
FileUploadItemProgress.displayName = ITEM_PROGRESS_NAME;

interface FileUploadItemDeleteProps extends ComponentPropsWithoutRef<"button"> {
	asChild?: boolean;
}

const FileUploadItemDelete = forwardRef<
	HTMLButtonElement,
	FileUploadItemDeleteProps
>((props, forwardedRef) => {
	const { asChild, ...deleteProps } = props;

	const store = useStoreContext(ITEM_DELETE_NAME);
	const itemContext = useFileUploadItemContext(ITEM_DELETE_NAME);
	const propsRef = useAsRef(deleteProps);

	const onClick = useCallback(
		(event: MouseEvent<HTMLButtonElement>) => {
			propsRef.current?.onClick?.(event);

			if (!itemContext.fileState || event.defaultPrevented) return;

			store.dispatch({
				file: itemContext.fileState.file,
				variant: "REMOVE_FILE",
			});
		},
		[store, itemContext.fileState, propsRef.current?.onClick],
	);

	if (!itemContext.fileState) return null;

	const ItemDeletePrimitive = asChild ? Slot : "button";

	return (
		<ItemDeletePrimitive
			aria-controls={itemContext.id}
			aria-describedby={itemContext.nameId}
			data-slot="file-upload-item-delete"
			type="button"
			{...deleteProps}
			onClick={onClick}
			ref={forwardedRef}
		/>
	);
});
FileUploadItemDelete.displayName = ITEM_DELETE_NAME;

interface FileUploadClearProps extends ComponentPropsWithoutRef<"button"> {
	forceMount?: boolean;
	asChild?: boolean;
}

const FileUploadClear = forwardRef<HTMLButtonElement, FileUploadClearProps>(
	(props, forwardedRef) => {
		const { asChild, forceMount, disabled, ...clearProps } = props;

		const context = useFileUploadContext(CLEAR_NAME);
		const store = useStoreContext(CLEAR_NAME);
		const propsRef = useAsRef(clearProps);

		const isDisabled = disabled || context.disabled;

		const onClick = useCallback(
			(event: MouseEvent<HTMLButtonElement>) => {
				propsRef.current?.onClick?.(event);

				if (event.defaultPrevented) return;

				store.dispatch({ variant: "CLEAR" });
			},
			[store, propsRef],
		);

		const shouldRender =
			forceMount || useStore((state) => state.files.size > 0);

		if (!shouldRender) return null;

		const ClearPrimitive = asChild ? Slot : "button";

		return (
			<ClearPrimitive
				aria-controls={context.listId}
				data-disabled={isDisabled ? "" : undefined}
				data-slot="file-upload-clear"
				type="button"
				{...clearProps}
				disabled={isDisabled}
				onClick={onClick}
				ref={forwardedRef}
			/>
		);
	},
);
FileUploadClear.displayName = CLEAR_NAME;

const FileUpload = FileUploadRoot;
// const Root = FileUploadRoot;
// const Trigger = FileUploadTrigger;
// const Dropzone = FileUploadDropzone;
// const List = FileUploadList;
// const Item = FileUploadItem;
// const ItemPreview = FileUploadItemPreview;
// const ItemMetadata = FileUploadItemMetadata;
// const ItemProgress = FileUploadItemProgress;
// const ItemDelete = FileUploadItemDelete;
// const Clear = FileUploadClear;

export {
	FileUpload,
	FileUploadDropzone,
	FileUploadTrigger,
	FileUploadList,
	FileUploadItem,
	FileUploadItemPreview,
	FileUploadItemMetadata,
	FileUploadItemProgress,
	FileUploadItemDelete,
	//FileUploadClear,
	/*
  Root,
  Dropzone,
  Trigger,
  List,
  Item,
  ItemPreview,
  ItemMetadata,
  ItemProgress,
  ItemDelete,
  Clear,
  
  useStore as useFileUpload,*/
};
