import { AnimatePresence, motion } from "framer-motion";
import { Pencil, Trash2 } from "lucide-react";
import {
	type FormEvent,
	useCallback,
	useEffect,
	useRef,
	useState,
} from "react";
import toast from "react-hot-toast";
import {
	ChatBubble,
	ChatBubbleMessage,
	ChatBubbleReadStatus,
	ChatBubbleTimestamp,
} from "~/components/chat/chat-bubble";
import LoadingButton from "~/components/custom_ui/loading-button";
import {
	AlertDialog,
	AlertDialogAction,
	AlertDialogCancel,
	AlertDialogContent,
	AlertDialogDescription,
	AlertDialogFooter,
	AlertDialogHeader,
	AlertDialogTitle,
} from "~/components/ui/alert-dialog";
import { Button } from "~/components/ui/button";
import {
	ContextMenu,
	ContextMenuContent,
	ContextMenuItem,
	ContextMenuTrigger,
} from "~/components/ui/context-menu";
import {
	Dialog,
	DialogContent,
	DialogDescription,
	DialogHeader,
	DialogTitle,
} from "~/components/ui/dialog";
import Input from "~/components/ui/input";
import { Textarea } from "~/components/ui/textarea";
import { getUserColorV2, UserAvatar } from "~/components/user_avatar";
import type { LiveChat } from "~/hooks/chatHooks";
import { cn, getInitials } from "~/lib/utils";
import { useTranslation } from "~/providers/I18nProvider";
import type { ChatsChatid } from "~/schemas/public/Chats";
import type { MessagesMessageid } from "~/schemas/public/Messages";
import type { UsersId } from "~/schemas/public/Users";
import type { Message, TypingData } from "~/server/api/routers/chat_sse";
import type { Session } from "~/server/api/trpc";
import { api } from "~/utils/api";

type ChatListProps = {
	chatId: ChatsChatid;
	messages: Message[];
	userData: Session;
	query: LiveChat["query"];
	typingStatus: TypingData;
};

export const ChatList = ({
	chatId,
	messages,
	userData,
	query,
	typingStatus,
}: ChatListProps) => {
	const messagesContainerRef = useRef<HTMLDivElement>(null);
	const [editModalOpen, setEditModalOpen] = useState(false);
	const [msgSelected, setMsgSelected] = useState<Message | null>(null);
	const [deleteModalOpen, setDeleteModalOpen] = useState(false);

	const handleEditOpen = (status: boolean) => {
		setEditModalOpen(status);
	};
	const handleDeleteOpen = (status: boolean) => {
		setDeleteModalOpen(status);
	};

	const { mutate: deleteMutation } = api.chatSSE.deleteMessage.useMutation({
		onError: () => {
			toast.error("Error deleting message", { id: "deleteMessage" });
		},
		onMutate: () => {
			toast("Deleting message...", { icon: "🗑️", id: "deleteMessage" });
		},
		onSuccess: () => {
			toast("Message deleted", { icon: "🗑️", id: "deleteMessage" });
		},
	});
	const { mutate: editMutation } = api.chatSSE.editMessage.useMutation({
		onError: () => {
			toast.error("Error editing message", { id: "editMessage" });
		},
		onMutate: () => {
			toast("Editing message...", { icon: "📝", id: "editMessage" });
		},
		onSuccess: () => {
			toast("Message edited", { icon: "📝", id: "editMessage" });
		},
	});

	const handleEditSubmit = useCallback(
		(e: FormEvent<HTMLFormElement>) => {
			e.preventDefault();
			const form = e.currentTarget;
			const formData = new FormData(form);

			const raw_messageId = formData.get("message-id");
			const raw_message = formData.get("message-edit");

			if (!raw_message || !raw_messageId) {
				return;
			}
			const messageId = raw_messageId.toString() as MessagesMessageid;
			const message = raw_message.toString();
			editMutation({
				chatId,
				message,
				messageId,
			});
			setEditModalOpen(false);
		},
		[editMutation],
	);
	const [locked, setLocked] = useState(false);

	useEffect(() => {
		if (
			messagesContainerRef.current &&
			!locked &&
			!query.isLoading &&
			!query.isFetching
		) {
			messagesContainerRef.current.scrollTop =
				messagesContainerRef.current.scrollHeight;
		}
		setLocked(false);
	}, [messages, typingStatus]);

	return (
		<div className="flex h-full w-full flex-col overflow-y-auto">
			<div
				className={cn("flex h-full w-full flex-col overflow-y-auto p-4")}
				ref={messagesContainerRef}
			>
				<AnimatePresence key="chat-messages">
					{query.hasNextPage && (
						<LoadingButton
							aria-label="Load More Messages"
							className="mb-3"
							disabled={!query.hasNextPage || query.isFetchingNextPage}
							key="load-more"
							loading={query.isFetchingNextPage}
							onClick={async () => {
								setLocked(true);
								await query.fetchNextPage();
							}}
							variant="secondary"
						>
							{query.isFetchingNextPage
								? "Caricamento..."
								: "Carica messaggi precedenti"}
						</LoadingButton>
					)}

					{messages.map((message, index) => {
						const variant = getMessageVariant(message.sender, userData.id);

						const isNextSameSender =
							index + 1 < messages.length &&
							messages[index + 1]?.sender === message.sender;

						const isPreviousSameSender =
							index - 1 >= 0 && messages[index - 1]?.sender === message.sender;

						const isPrevSameDay =
							index - 1 >= 0 &&
							// biome-ignore lint/style/noNonNullAssertion: <non lo so>
							new Date(messages[index - 1]!.time).toDateString() ===
								new Date(message.time).toDateString();

						const userColor = getUserColorV2(
							`${getInitials(message.sender_nome)}${message.sender}`,
						);

						return (
							<motion.div
								animate={{ opacity: 1, scale: 1, x: 0, y: 0 }}
								className={cn(
									"flex flex-col gap-2 pb-4",
									isNextSameSender && "pb-0.5",
								)}
								exit={{ opacity: 0, scale: 1, x: 0, y: 1 }}
								initial={{ opacity: 0, scale: 1, x: 0, y: 50 }}
								key={message.messageid}
								layout
								style={{ originX: 0.5, originY: 0.5 }}
								transition={{
									layout: {
										bounce: 0.3,
										duration: 0.3,
										type: "spring",
									},
									opacity: { duration: 0.1 },
								}}
							>
								{!isPrevSameDay && (
									<span className="mx-auto rounded-md bg-muted px-2 py-1 text-center text-muted-foreground text-xs">
										{new Date(message.time).toLocaleDateString("it", {
											day: "2-digit",
											month: "2-digit",
											year: "2-digit",
										})}
									</span>
								)}

								<ChatBubble variant={variant}>
									{isNextSameSender ? (
										<div className="size-6 shrink-0"></div>
									) : (
										<UserAvatar
											className="size-6 shrink-0"
											showInitials={false}
											userId={message.sender}
											username={message.sender_nome}
										/>
									)}
									<ContextMenu>
										<ContextMenuTrigger asChild disabled={!userData.isAdmin}>
											<div className="flex flex-col gap-1">
												{!isPreviousSameSender && (
													<p
														className={cn(
															"font-semibold text-sm",
															variant === "sent" && "text-right",
														)}
														style={{
															color: userColor,
														}}
													>
														{message.sender_nome}
													</p>
												)}
												<ChatBubbleMessage
													className={cn(isNextSameSender && "rounded-lg", "")}
													isLoading={false}
												>
													<span className="flex shrink break-all">
														{message.message}
													</span>
													<div className="flex items-center gap-2">
														{userData.id === message.sender && (
															<ChatBubbleReadStatus isread={message.isread} />
														)}
														{message.time && (
															<ChatBubbleTimestamp
																timestamp={new Date(
																	message.time,
																).toLocaleString("it", {
																	day: "2-digit",
																	hour: "2-digit",
																	minute: "2-digit",
																	month: "short",
																})}
															/>
														)}
													</div>
												</ChatBubbleMessage>
											</div>
										</ContextMenuTrigger>
										<ContextMenuContent>
											<ContextMenuItem
												className="flex items-center gap-2"
												onClick={() => {
													setMsgSelected(message);
													handleDeleteOpen(true);
												}}
											>
												<Trash2 />
												Cancella
											</ContextMenuItem>
											<ContextMenuItem
												className="flex items-center gap-2"
												onClick={() => {
													setMsgSelected(message);
													handleEditOpen(true);
												}}
											>
												<Pencil />
												Modifica
											</ContextMenuItem>
										</ContextMenuContent>
									</ContextMenu>
								</ChatBubble>
							</motion.div>
						);
					})}

					<Typers
						currentTypers={typingStatus}
						messageLenght={messages.length}
						userId={userData.id}
					/>
				</AnimatePresence>
			</div>
			<EditMessageDialog
				handleEditSubmit={handleEditSubmit}
				msgEdit={msgSelected}
				onOpenChange={handleEditOpen}
				open={editModalOpen}
			/>
			<DeleteMessageDialog
				handleDeleteSubmit={(messageId) => {
					deleteMutation({ chatId, messageId });
				}}
				msgDelete={msgSelected}
				onOpenChange={handleDeleteOpen}
				open={deleteModalOpen}
			/>
		</div>
	);
};

const Typers = ({
	currentTypers,
	messageLenght,
	userId,
}: {
	currentTypers: TypingData;
	messageLenght: number;
	userId: UsersId;
}) => {
	return (
		<>
			{currentTypers.map((typer) => {
				if (typer.userId === userId) return null;
				const variant = "received";

				return (
					<motion.div
						animate={{ opacity: 1, scale: 1, x: 0, y: 0 }}
						className="flex flex-col gap-2 py-2"
						exit={{ opacity: 0, scale: 1, x: 0, y: 1 }}
						initial={{ opacity: 0, scale: 1, x: 0, y: 50 }}
						key={`typers${messageLenght}${typer.userId}`}
						layout
						style={{ originX: 0.5, originY: 0.5 }}
						transition={{
							layout: {
								bounce: 0.3,
								duration: 0.3,
								type: "spring",
							},
							opacity: { duration: 0.1 },
						}}
					>
						<ChatBubble variant={variant}>
							<UserAvatar
								className="size-6"
								showInitials={false}
								userId={typer.userId}
								username={typer.username}
							/>

							<ChatBubbleMessage isLoading={true}></ChatBubbleMessage>
						</ChatBubble>
					</motion.div>
				);
			})}
		</>
	);
};

const EditMessageDialog = ({
	open,
	onOpenChange,
	msgEdit,
	handleEditSubmit,
}: {
	open: boolean;
	onOpenChange: (status: boolean) => void;
	msgEdit: Message | null;
	handleEditSubmit: (e: FormEvent<HTMLFormElement>) => void;
}) => {
	const { t } = useTranslation();
	return (
		<Dialog onOpenChange={onOpenChange} open={open}>
			<DialogContent>
				<DialogHeader>
					<DialogTitle>{t.modifica}</DialogTitle>
					<DialogDescription className="sr-only">Edit</DialogDescription>
				</DialogHeader>
				{msgEdit && (
					<form method="post" onSubmit={handleEditSubmit}>
						<div className="flex items-center gap-3">
							<label className="sr-only" htmlFor="id">
								ID
							</label>
							<Input
								className="hidden"
								id="id"
								name="message-id"
								readOnly
								type="text"
								value={msgEdit.messageid}
							/>
							<label className="sr-only" htmlFor="message-edit">
								Modifica messaggio
							</label>
							<Textarea
								className="mt-0 min-h-64"
								defaultValue={msgEdit.message || undefined}
								id="message-edit"
								name="message-edit"
								placeholder="Modifica messaggio"
							/>

							<Button type="submit">{t.salva}</Button>
						</div>
					</form>
				)}
			</DialogContent>
		</Dialog>
	);
};

const DeleteMessageDialog = ({
	open,
	onOpenChange,
	msgDelete,
	handleDeleteSubmit,
}: {
	open: boolean;
	onOpenChange: (status: boolean) => void;
	msgDelete: Message | null;
	handleDeleteSubmit: (msgId: MessagesMessageid) => void;
}) => {
	const { t } = useTranslation();
	return (
		<>
			{msgDelete && (
				<AlertDialog onOpenChange={onOpenChange} open={open}>
					<AlertDialogContent>
						<AlertDialogHeader>
							<AlertDialogTitle>Vuoi eliminare il messaggio?</AlertDialogTitle>
							<AlertDialogDescription>
								Questa azione non può essere annullata.
							</AlertDialogDescription>
						</AlertDialogHeader>
						<AlertDialogFooter>
							<AlertDialogCancel>{t.annulla}</AlertDialogCancel>
							<AlertDialogAction
								aria-label="Delete Message"
								onClick={() => handleDeleteSubmit(msgDelete.messageid)}
							>
								{t.procedi}
							</AlertDialogAction>
						</AlertDialogFooter>
					</AlertDialogContent>
				</AlertDialog>
			)}
		</>
	);
};

const getMessageVariant = (messageName: string, selectedUserName: string) =>
	messageName !== selectedUserName ? "received" : "sent";
