import { format, isSameDay } from "date-fns";
import { AnimatePresence, motion } from "framer-motion";
import { Copy, Forward, Pencil, Reply, Trash2 } from "lucide-react";
import { useRouter } from "next/router";
import {
	type FormEvent,
	useCallback,
	useEffect,
	useRef,
	useState,
} from "react";
import toast from "react-hot-toast";
import {
	ChatBubble,
	ChatBubbleMessage,
	ChatBubbleReadStatus,
} from "~/components/chat/chat-bubble";
import LoadingButton from "~/components/custom_ui/loading-button";
import { LoadingPage } from "~/components/loading";
import {
	AlertDialog,
	AlertDialogAction,
	AlertDialogCancel,
	AlertDialogContent,
	AlertDialogDescription,
	AlertDialogFooter,
	AlertDialogHeader,
	AlertDialogTitle,
} from "~/components/ui/alert-dialog";
import { Button } from "~/components/ui/button";
import {
	CommandDialog,
	CommandEmpty,
	CommandGroup,
	CommandInput,
	CommandItem,
	CommandList,
} from "~/components/ui/command";
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 { cn } from "~/lib/utils";
import { useChatContext } from "~/providers/ChatProvider";
import { useTranslation } from "~/providers/I18nProvider";
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 { api } from "~/utils/api";

export const ChatList = () => {
	const {
		chatId,
		session,
		liveChat,
		inputRef,
		setReplyTrg,
		messagesContainerRef,
	} = useChatContext();
	const { messages, query, typingStatus } = liveChat;

	const [editModalOpen, setEditModalOpen] = useState(false);
	const [msgSelected, setMsgSelected] = useState<Message | null>(null);
	const [deleteModalOpen, setDeleteModalOpen] = useState(false);
	const [inoltraModalOpen, setInoltraModalOpen] = 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);
	const focusInputOnCloseRef = useRef(false);
	useEffect(() => {
		if (
			messagesContainerRef.current &&
			!locked &&
			!query.isLoading &&
			!query.isFetching
		) {
			messagesContainerRef.current.scrollTo({
				top: messagesContainerRef.current.scrollHeight,
				behavior: "smooth",
			});
		}
		setLocked(false);
	}, [messages, typingStatus]);

	const handleFetchPrevious = useCallback(async () => {
		if (query.hasNextPage && !query.isFetchingNextPage) {
			setLocked(true);
			await query.fetchNextPage();
		}
	}, [query]);

	const utils = api.useUtils();

	const handleScrollTo = (messageId: MessagesMessageid): boolean => {
		const el = document.getElementById(messageId);
		if (el) {
			el.scrollIntoView({
				behavior: "smooth",
				block: "center",
			});
			el.classList.add("bg-primary/50");
			setTimeout(() => {
				el.classList.remove("bg-primary/50");
			}, 2000);
			return true;
		}
		console.warn("Element not found for messageId:", messageId);
		return false;
	};

	const handleJumpToMessage = async (messageId: MessagesMessageid) => {
		const scroll = handleScrollTo(messageId);
		if (scroll) return;

		const context = await utils.chatSSE.getChat.fetch({
			chatId,
			targetMsgId: messageId,
		});

		utils.chatSSE.getChat.setInfiniteData({ chatId }, () => {
			return {
				pages: [context],
				pageParams: [null] as (MessagesMessageid | null)[],
			};
		});
		setTimeout(() => {
			handleScrollTo(messageId);
		}, 100);
		console.log(query.data);
	};

	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-1")}
				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={handleFetchPrevious}
							variant="secondary"
						>
							{query.isFetchingNextPage
								? "Caricamento..."
								: "Carica messaggi precedenti"}
						</LoadingButton>
					)}

					{messages.map((message, index) => {
						const isNextSameSender =
							index + 1 < messages.length &&
							messages[index + 1]?.sender === message.sender;

						const isPrevSameDay = (() => {
							const prevMsg = messages[index - 1];
							if (!prevMsg) return false;
							return isSameDay(new Date(prevMsg.time), new Date(message.time));
						})();
						const isPreviousSameSender =
							index - 1 >= 0 && messages[index - 1]?.sender === message.sender;

						const isSender = message.sender === session.id;

						const userColor = getUserColorV2(message.color_str);
						const reply_userColor = message.reply_source
							? getUserColorV2(message.reply_source.color_str)
							: undefined;

						return (
							<div
								className={cn(
									"flex flex-col gap-0.5 pb-4",
									isNextSameSender && "pb-0.5",
								)}
								key={message.messageid}
							>
								{!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>
								)}
								{!isPreviousSameSender && (
									<p
										className={cn(
											"px-1.5 font-semibold text-sm",
											isSender && "text-right",
										)}
										style={{
											color: userColor,
										}}
									>
										{message.sender_nome}
									</p>
								)}
								<ContextMenu>
									<ContextMenuTrigger asChild>
										<motion.div
											animate={{ opacity: 1, scale: 1, x: 0, y: 0 }}
											className={cn(
												"flex flex-col gap-2 rounded-md px-1",
												"data-[state=open]:bg-muted",
											)}
											exit={{ opacity: 0, scale: 1, x: 0, y: 1 }}
											id={message.messageid}
											initial={{ opacity: 0, scale: 1, x: 0, y: 25 }}
											layout
											style={{ originX: 0.25, originY: 0.25 }}
											transition={{
												layout: {
													bounce: 0.2,
													duration: 0.2,
													type: "spring",
												},
												opacity: { duration: 0.1 },
											}}
										>
											<ChatBubble variant={isSender ? "sent" : "received"}>
												<ChatBubbleMessage
													className={cn(
														"flex flex-wrap items-end justify-end gap-x-2 gap-y-1",
														isNextSameSender && "rounded-lg",
														"",
													)}
													variant={isSender ? "sent" : "received"}
												>
													{message.reply_source && (
														<button
															className="group block w-full cursor-pointer text-left"
															onClick={async () => {
																if (!message.reply_source) return;
																await handleJumpToMessage(
																	message.reply_source.messageid,
																);
															}}
															type="button"
														>
															<div
																className="mt-1 w-full rounded-md border-l-4 bg-secondary px-2 py-0.5 text-foreground group-hover:bg-secondary/90"
																style={{ borderColor: reply_userColor }}
															>
																<span
																	className="font-semibold text-xs"
																	style={{
																		color: reply_userColor,
																	}}
																>
																	{message.reply_source.sender === session.id
																		? "Tu"
																		: message.reply_source.nome}
																</span>
																<span className="line-clamp-2 block break-all text-xs italic">
																	{message.reply_source.message}
																</span>
															</div>
														</button>
													)}
													<span className="flex shrink break-all">
														{message.message}
													</span>
													<div
														className={cn(
															"flex items-center gap-1",
															isSender && "justify-end",
														)}
													>
														{message.time && (
															<div
																className={cn(
																	"text-[0.70rem]",
																	isSender
																		? "text-muted"
																		: "text-muted-foreground",
																)}
															>
																{format(new Date(message.time), "HH:mm")}
															</div>
														)}
														{isSender && (
															<ChatBubbleReadStatus isread={message.isread} />
														)}
													</div>
												</ChatBubbleMessage>
											</ChatBubble>
										</motion.div>
									</ContextMenuTrigger>
									<ContextMenuContent
										onCloseAutoFocus={(e) => {
											if (focusInputOnCloseRef.current) {
												e.preventDefault();
												inputRef.current?.textArea.focus();
												focusInputOnCloseRef.current = false;
												if (messagesContainerRef.current) {
													messagesContainerRef.current.scrollTo({
														top: messagesContainerRef.current.scrollHeight,
														behavior: "smooth",
													});
												}
											}
										}}
									>
										<ContextMenuItem
											className="flex items-center gap-2"
											onClick={() => {
												navigator.clipboard.writeText(message.message);
												toast.success("Messaggio copiato negli appunti", {
													id: "copyMessage",
												});
											}}
										>
											<Copy />
											Copia
										</ContextMenuItem>
										<ContextMenuItem
											className="flex items-center gap-2"
											onClick={() => {
												setReplyTrg(message);

												focusInputOnCloseRef.current = true;
											}}
										>
											<Reply />
											Rispondi
										</ContextMenuItem>
										{session.isAdmin && (
											<ContextMenuItem
												className="flex items-center gap-2"
												onClick={() => {
													setMsgSelected(message);
													setInoltraModalOpen(true);
												}}
											>
												<Forward />
												Inoltra
											</ContextMenuItem>
										)}

										{(isSender || session.isAdmin) && (
											<ContextMenuItem
												className="flex items-center gap-2"
												onClick={() => {
													setMsgSelected(message);
													handleEditOpen(true);
												}}
											>
												<Pencil />
												Modifica
											</ContextMenuItem>
										)}
										{session.isAdmin && (
											<>
												<ContextMenuItem
													className="flex items-center gap-2"
													onClick={() => {
														setMsgSelected(message);
														handleDeleteOpen(true);
													}}
												>
													<Trash2 />
													Cancella
												</ContextMenuItem>{" "}
											</>
										)}
									</ContextMenuContent>
								</ContextMenu>
							</div>
						);
					})}

					<Typers
						currentTypers={typingStatus}
						messageLenght={messages.length}
						userId={session.id}
					/>
				</AnimatePresence>
			</div>
			{session.isAdmin && (
				<InoltraDialog
					isOpen={inoltraModalOpen}
					msgSelected={msgSelected}
					openChange={(status) => setInoltraModalOpen(status)}
				/>
			)}
			<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 px-1 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}>
							<ChatBubbleMessage className="text-muted-foreground text-xs">
								✏️ {typer.username} sta scrivendo...
							</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 InoltraDialog = ({
	isOpen,
	openChange,
	msgSelected,
}: {
	isOpen: boolean;
	openChange: (status: boolean) => void;
	msgSelected: Message | null;
}) => {
	const router = useRouter();
	const { chatId, session } = useChatContext();
	const { data: activeChats, isLoading } = api.chat.getActiveChats.useQuery(
		undefined,
		{
			enabled: isOpen,
		},
	);
	const { mutate } = api.chatSSE.sendMessage.useMutation({
		onError: () => {
			toast.error("Errore nell'inoltro del messaggio", {
				id: "forwardMessage",
			});
		},
		onMutate: () => {
			toast("Inoltrando messaggio...", { icon: "📨", id: "forwardMessage" });
		},
		onSuccess: () => {
			toast("Messaggio inoltrato", { icon: "📨", id: "forwardMessage" });
		},
	});
	return (
		<CommandDialog onOpenChange={openChange} open={isOpen}>
			<CommandInput
				className="border-none ring-0 focus:ring-0 focus-visible:ring-0"
				placeholder="Digita il nome utente..."
			/>
			<CommandList>
				<CommandEmpty>Nessun risultato</CommandEmpty>
				{isLoading && <LoadingPage />}
				<CommandGroup className="pb-2" heading={"Suggerimenti"}>
					{activeChats
						?.filter((chat) => chat.chatid !== chatId)
						.map((chat) => (
							<CommandItem
								className="cursor-pointer rounded-md"
								key={chat.id}
								onSelect={() => {
									if (!msgSelected) return;
									mutate({
										chatId: chat.chatid,
										message: msgSelected.message,
										sender: session.id,
										is_forwarded: true,
									});
									router.push(`/area-riservata/admin/chats/${chat.chatid}`);
								}}
							>
								<span className="flex items-center gap-2">
									<UserAvatar
										className="size-8"
										userId={chat.id}
										username={chat.username}
									/>

									<span>
										<span className="block font-medium">{chat.username}</span>
									</span>
								</span>
							</CommandItem>
						))}
				</CommandGroup>
			</CommandList>
		</CommandDialog>
	);
};
