import { createContext, type ReactNode, useContext, useEffect, useState } from 'react';

import type { AnyProfileView, ChatBskyConvoDefs } from '@atcute/bluesky';

import { useQueryClient } from '@tanstack/react-query';

import { useConvoActive } from '#/state/messages/convo';
import { unstableCacheProfileView } from '#/state/queries/unstable-profile-cache';

import * as Dialog from '#/components/Dialog';
import { AfterReportDialog } from '#/components/dms/AfterReportDialog';
import { ReactionsDialog } from '#/components/dms/ReactionsDialog';
import { ReportDialog } from '#/components/moderation/ReportDialog';
import * as Prompt from '#/components/Prompt';
import * as Toast from '#/components/Toast';

import { m } from '#/paraglide/messages';

type MessageDialogsContextType = {
	openDeleteMessage: (message: ChatBskyConvoDefs.MessageView) => void;
	openReportMessage: (
		message: ChatBskyConvoDefs.MessageView,
		senderProfile: AnyProfileView | undefined,
	) => void;
	/** Handle for the reactions dialog, so a detached `Dialog.Trigger` can open it with a message payload. */
	reactionsHandle: Dialog.DialogHandle<ChatBskyConvoDefs.MessageView>;
};

const Context = createContext<MessageDialogsContextType | null>(null);

export function useMessageDialogs() {
	const ctx = useContext(Context);
	if (!ctx) {
		throw new Error('useMessageDialogs must be used within a MessageOverlays');
	}
	return ctx;
}

export function MessageOverlays({ children }: { children: ReactNode }) {
	const queryClient = useQueryClient();
	const convo = useConvoActive();

	const deleteControl = Prompt.usePromptHandle();
	const reportHandle = Dialog.useDialogHandle();
	const afterReportHandle = Dialog.useDialogHandle();
	const reactionsHandle = Dialog.useDialogHandle<ChatBskyConvoDefs.MessageView>();

	const [deleteTarget, setDeleteTarget] = useState<ChatBskyConvoDefs.MessageView | null>(null);
	const [reportTarget, setReportTarget] = useState<{
		message: ChatBskyConvoDefs.MessageView;
		senderProfile: AnyProfileView | undefined;
	} | null>(null);
	const [afterReportTarget, setAfterReportTarget] = useState<ChatBskyConvoDefs.MessageView | null>(null);

	const openDeleteMessage = (message: ChatBskyConvoDefs.MessageView) => {
		setDeleteTarget(message);
		deleteControl.open(null);
	};

	const openReportMessage = (
		message: ChatBskyConvoDefs.MessageView,
		senderProfile: AnyProfileView | undefined,
	) => {
		setReportTarget({ message, senderProfile });
		reportHandle.open(null);
	};

	// AfterReportDialog is conditionally mounted, so we can't open it in the same tick that we set its
	// target - the handle isn't attached yet. Open in an effect after the dialog has mounted.
	useEffect(() => {
		if (afterReportTarget) {
			afterReportHandle.open(null);
		}
	}, [afterReportTarget, afterReportHandle]);

	const onConfirmDelete = () => {
		if (!deleteTarget) {
			return;
		}
		convo
			.deleteMessage(deleteTarget.id)
			.then(() => Toast.show(m['components.dms.delete.messageDeleted']()))
			.catch(() => Toast.show(m['components.dms.delete.error.message']()));
	};

	const onAfterReportSubmit = () => {
		if (!reportTarget) {
			return;
		}
		if (reportTarget.senderProfile) {
			unstableCacheProfileView(queryClient, reportTarget.senderProfile);
		}
		setAfterReportTarget(reportTarget.message);
	};

	const ctx: MessageDialogsContextType = {
		openDeleteMessage,
		openReportMessage,
		reactionsHandle,
	};

	const reportSubject = reportTarget
		? ({
				view: 'message',
				convoId: convo.convo.view.id,
				message: reportTarget.message,
			} as const)
		: undefined;

	return (
		<Context.Provider value={ctx}>
			{children}
			<ReportDialog
				handle={reportHandle}
				subject={reportSubject}
				onAfterSubmit={onAfterReportSubmit}
				onClose={() => setReportTarget(null)}
			/>
			{afterReportTarget && (
				<AfterReportDialog
					handle={afterReportHandle}
					currentScreen="conversation"
					params={{
						convoId: convo.convo.view.id,
						did: afterReportTarget.sender.did,
					}}
					onClose={() => setAfterReportTarget(null)}
				/>
			)}
			<ReactionsDialog handle={reactionsHandle} />
			<Prompt.Basic
				handle={deleteControl}
				title={m['components.dms.delete.action.message']()}
				description={m['components.dms.delete.messagePrompt']()}
				confirmButtonCta={m['common.action.delete']()}
				confirmButtonColor="negative"
				onConfirm={onConfirmDelete}
			/>
		</Context.Provider>
	);
}
