"use client";

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,
	ChatBubbleAction,
	ChatBubbleActionWrapper,
	ChatBubbleMessage,
	ChatBubbleReadStatus,
	ChatBubbleTimestamp,
} from "~/components/chat/chat-bubble";
import { ChatMessageList } from "~/components/chat/chat-message-list";
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 {
	Dialog,
	DialogContent,
	DialogDescription,
	DialogHeader,
	DialogTitle,
} from "~/components/ui/dialog";
import Input from "~/components/ui/input";
import { getUserColor, UserAvatar } from "~/components/user_avatar";
import type { LiveChat } from "~/hooks/chatHooks";
import { cn } 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 { Session } from "~/server/api/trpc";
import type { ChatMessage } from "~/server/services/messages.service";
import type { WhoIsTyping } from "~/server/sse";
import { api } from "~/utils/api";

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

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

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

	const { mutate: deleteMutation } = api.messagesSSE.delete.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.messagesSSE.edit.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);
			if (!formData.get("message-edit") || !formData.get("message-id")) {
				return;
			}
			editMutation({
				chatId,
				message: formData.get("message-edit") as string,
				messageId: formData.get("message-id") as MessagesMessageid,
			});
			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">
			<ChatMessageList 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();

						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"></div>
									) : (
										<UserAvatar className="size-6" userId={message.sender} />
									)}

									<div className="flex flex-col gap-1">
										{!isPreviousSameSender && (
											<p
												className={cn(
													"font-semibold text-sm",
													variant === "sent" && "text-right",
												)}
												style={{
													color: getUserColor(message.sender),
												}}
											>
												{message.sender_nome}
											</p>
										)}
										<ChatBubbleMessage
											className={cn(isNextSameSender && "rounded-lg")}
											isLoading={false}
										>
											{message.message}
											<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>
									<ChatBubbleActionWrapper show={userData.isAdmin}>
										<ChatBubbleAction
											className="size-7 hover:bg-red-50 hover:text-red-500"
											icon={<Trash2 className="size-4" />}
											key={"delete"}
											onClick={() => {
												setMsgSelected(message);
												handleDeleteOpen(true);
											}}
										/>
										<ChatBubbleAction
											className="size-7 hover:bg-blue-50 hover:text-blue-500"
											icon={<Pencil className="size-4" />}
											key={"edit"}
											onClick={() => {
												setMsgSelected(message);
												handleEditOpen(true);
											}}
										/>
									</ChatBubbleActionWrapper>
								</ChatBubble>
							</motion.div>
						);
					})}

					<Typers
						currentTypers={typingStatus}
						messageLenght={messages.length}
						userId={userData.id}
					/>
				</AnimatePresence>
			</ChatMessageList>
			<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: WhoIsTyping[];
	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" userId={typer.userId} />

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

const EditMessageDialog = ({
	open,
	onOpenChange,
	msgEdit,
	handleEditSubmit,
}: {
	open: boolean;
	onOpenChange: (status: boolean) => void;
	msgEdit: ChatMessage | 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>
							<Input
								className="mt-0"
								defaultValue={msgEdit.message || undefined}
								id="message-edit"
								name="message-edit"
								placeholder="Modifica messaggio"
								type="text"
							/>
							<Button type="submit">{t.salva}</Button>
						</div>
					</form>
				)}
			</DialogContent>
		</Dialog>
	);
};

const DeleteMessageDialog = ({
	open,
	onOpenChange,
	msgDelete,
	handleDeleteSubmit,
}: {
	open: boolean;
	onOpenChange: (status: boolean) => void;
	msgDelete: ChatMessage | 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";
