import { CloudUpload, UploadIcon, X } from "lucide-react";
import { useCallback, useState } from "react";
import toast from "react-hot-toast";
import {
	Credenza,
	CredenzaBody,
	CredenzaContent,
	CredenzaDescription,
	CredenzaHeader,
	CredenzaTitle,
	CredenzaTrigger,
} from "~/components/custom_ui/credenza";
import {
	FileUpload,
	FileUploadDropzone,
	FileUploadItem,
	FileUploadItemDelete,
	FileUploadItemMetadata,
	FileUploadItemPreview,
	FileUploadItemProgress,
	FileUploadList,
	FileUploadTrigger,
} from "~/components/custom_ui/fileUpload";
import { Button } from "~/components/ui/button";
import { uploadHandler } from "~/hooks/storageClienSideHooks";
import { useTranslation } from "~/providers/I18nProvider";
import type { UsersId } from "~/schemas/public/Users";
import type { UsersStorageUserStorageId } from "~/schemas/public/UsersStorage";
import { api } from "~/utils/api";

export type UploadCallback = (args: {
	storageIds: string[];
	userStorageIds: UsersStorageUserStorageId[];
}) => void;

type UploadModalProps = {
	cb_onUpload?: UploadCallback;
	isAdmin?: boolean;
	userId?: UsersId;
};

export const UploadModal = ({
	cb_onUpload,
	isAdmin,
	userId,
}: UploadModalProps) => {
	const { t } = useTranslation();

	return (
		<Credenza>
			<CredenzaTrigger asChild>
				<Button className="flex w-fit gap-2">
					<CloudUpload className="size-6" /> {t.allegati.carica_nuovi}
				</Button>
			</CredenzaTrigger>
			<CredenzaContent className="max-h-[90vh]">
				<CredenzaHeader>
					<CredenzaTitle className="text-center font-semibold text-2xl">
						{t.allegati.carica_nuovi}
					</CredenzaTitle>
					<CredenzaDescription className="sr-only">
						Allegati modal
					</CredenzaDescription>
				</CredenzaHeader>
				<CredenzaBody className="max-h-[80vh] overflow-auto pb-5">
					<UploadComponent
						cb_onUpload={cb_onUpload}
						isAdmin={isAdmin}
						userId={userId}
					/>
				</CredenzaBody>
			</CredenzaContent>
		</Credenza>
	);
};

type UploadComponentProps = {
	cb_onUpload?: UploadCallback;
	isAdmin?: boolean;
	userId?: UsersId;
	maxMB?: number;
	maxFiles?: number;
};

export const UploadComponent = ({
	cb_onUpload,
	isAdmin,
	userId,
	maxMB = 5, // Default max size is 5MB
	maxFiles,
}: UploadComponentProps) => {
	const { t } = useTranslation();
	const [files, setFiles] = useState<File[]>([]);
	const [isUploading, setIsUploading] = useState(false);

	const { mutateAsync: setUserStorage } =
		api.storage.addUserStorageEntry.useMutation({
			onMutate: () => {
				toast(t.allegati.allegato_caricato, {
					icon: "📤",
					toasterId: "uploading-toast",
				});
			},
			onSettled: async () => {
				await utils.storage.getAll.invalidate();
				await utils.storage.retrieveUserFileData.invalidate({
					userId,
				});
			},
		});

	const acceptedMimeTypes = [
		"image/jpeg", // jpg, jpeg
		"image/png", // png
		"image/webp", // webp
		"text/plain", // txt
		"application/pdf", // pdf
		"application/msword", // doc
		"application/vnd.openxmlformats-officedocument.wordprocessingml.document", // docx
		"application/vnd.ms-excel", // xls
		"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", // xlsx
		"application/vnd.oasis.opendocument.text", // odt
		"application/vnd.oasis.opendocument.spreadsheet", // ods
	];

	const utils = api.useUtils();

	const onFileReject = useCallback((file: File, message: string) => {
		toast(
			`${message}\n\n"${file.name.length > 15 ? `${file.name.slice(0, 15)}...` : file.name}" ${t.allegati.file_rejected}`,
		);
	}, []);

	const onFileValidate = useCallback(
		(file: File): string | null => {
			if (maxFiles) {
				// Validate max files
				if (files.length >= maxFiles) {
					return `${t.allegati.file_too_many} ${maxFiles}`;
				}
			}

			// Validate file type
			if (!acceptedMimeTypes.includes(file.type)) {
				return t.allegati.invalid_file_type;
			}
			if (maxMB) {
				// Validate file size
				const MAX_SIZE = maxMB * 1024 * 1024;
				if (file.size > MAX_SIZE) {
					return `${t.allegati.file_too_large} ${MAX_SIZE / (1024 * 1024)}MB`;
				}
			}

			return null;
		},
		[files],
	);

	const onUpload = useCallback(
		async (
			files: File[],
			{
				onProgress,
			}: {
				onProgress: (file: File, progress: number) => void;
			},
		) => {
			if (files.length === 0) {
				return;
			}
			let storageIds: string[] = [];
			const userStorageIds: UsersStorageUserStorageId[] = [];
			try {
				const from_admin = isAdmin ?? true;
				setIsUploading(true);

				const { status, attachments } = await uploadHandler({
					files,
					from_admin,
				});
				if (!status) {
					toast.error(t.allegati.errore_caricamento, {
						icon: "❌",
						toasterId: "uploading-toast",
					});
					setIsUploading(false);
					return;
				}
				storageIds = attachments;

				if (userId) {
					for (const storageId of attachments) {
						const res = await setUserStorage({
							storageId,
							userId,
							from_admin,
						});
						userStorageIds.push(res.user_storage_id);
					}
				}

				files.forEach((file) => {
					const randomDelay = Math.random() * 1000 + 500; // Random delay between 500ms and 1500ms
					let progress = 0;

					const interval = setInterval(() => {
						progress += Math.random() * 10; // Increment progress randomly
						if (progress >= 100) {
							progress = 100;
							clearInterval(interval);
						}
						onProgress(file, progress);
					}, randomDelay / 1); // Adjust interval based on random delay
				});

				toast.success(t.allegati.success_caricamento, {
					icon: "✅",
					toasterId: "uploading-toast",
				});
			} catch (error) {
				setIsUploading(false);

				toast.error(
					error instanceof Error
						? error.message
						: t.allegati.errore_sconosciuto,
					{ icon: "❌", toasterId: "uploading-toast" },
				);
			} finally {
				setIsUploading(false);
				setFiles([]);
				cb_onUpload?.({ storageIds, userStorageIds });
				await utils.storage.getAll.invalidate();
			}
		},
		[],
	);

	return (
		<FileUpload
			className="w-full"
			disabled={isUploading}
			multiple
			onFileReject={onFileReject}
			onFileValidate={onFileValidate}
			onUpload={onUpload}
			onValueChange={setFiles}
			value={files}
		>
			<FileUploadDropzone>
				<div className="flex flex-col items-center gap-1">
					<div className="flex items-center justify-center rounded-full border p-2.5">
						<UploadIcon className="size-6 text-muted-foreground" />
					</div>
					<p className="font-medium text-sm">{t.allegati.drag_drop}</p>
					<p className="text-muted-foreground text-xs">{t.allegati.oppure}</p>
				</div>
				<FileUploadTrigger asChild>
					<Button className="mt-2 w-fit" size="sm" variant="outline">
						{t.allegati.seleziona}
					</Button>
				</FileUploadTrigger>
			</FileUploadDropzone>
			<FileUploadList>
				{files.map((file) => (
					<FileUploadItem key={file.name} value={file}>
						<FileUploadItemPreview />
						<FileUploadItemMetadata />
						<FileUploadItemDelete asChild>
							<Button className="size-7" size="icon" variant="ghost">
								<X />
							</Button>
						</FileUploadItemDelete>
						<FileUploadItemProgress />
					</FileUploadItem>
				))}
			</FileUploadList>
		</FileUpload>
	);
};
