import { AgentMessage } from '@agent/components/messages/AgentMessage';
import { StatusMessage } from '@agent/components/messages/StatusMessage';
import { UserMessage } from '@agent/components/messages/UserMessage';
import { WorkflowComponent } from '@agent/components/messages/WorkflowComponent';
import { WorkflowMessage } from '@agent/components/messages/WorkflowMessage';
import { ScrollDownButton } from '@agent/components/ScrollDownButton';
import { ScrollIntoViewOnce } from '@agent/components/ScrollIntoViewOnce';
import { useWhenFinishedToolProps } from '@agent/hooks/useWhenFinishedToolProps';
import { useChatStore } from '@agent/state/chat';
import { useGlobalStore } from '@agent/state/global';
import { useWorkflowStore } from '@agent/state/workflows';
import {
	createElement,
	useEffect,
	useLayoutEffect,
	useRef,
	useState,
} from '@wordpress/element';

export const ChatMessages = () => {
	const { open } = useGlobalStore();
	const { messages } = useChatStore();
	const { getWorkflow } = useWorkflowStore();
	const workflow = getWorkflow();
	const whenFinishedToolProps = useWhenFinishedToolProps();
	const whenFinishedComponent = workflow?.whenFinished?.component;
	const [canScrollDown, setCanScrollDown] = useState(false);
	const containerRef = useRef(null);
	const isFreshPageLoad = useRef(true);
	const [ready, setReady] = useState(false);

	// If last message is a user message, move it to the top
	const isUserMessage =
		messages.filter(({ type }) => type !== 'status').at(-1)?.details?.role ===
		'user';

	useEffect(() => {
		if (!containerRef.current || !open) return;
		if (!isFreshPageLoad.current) return;
		isFreshPageLoad.current = false;
		// Scroll to the bottom of the chat container on load
		const c = containerRef.current;
		const last = c.querySelector(
			'#extendify-agent-chat-scroll-area > :last-child',
		);
		let id2;
		const id = requestAnimationFrame(() => {
			id2 = requestAnimationFrame(() => {
				last?.scrollIntoView({ behavior: 'auto', block: 'start' });
				setReady(true);
			});
		});
		return () => {
			cancelAnimationFrame(id);
			cancelAnimationFrame(id2);
			isFreshPageLoad.current = true;
			setReady(false);
		};
	}, [open]);

	// Handles scrolling to the top of the last user message
	// TODO: if the user sends in a long message, maybe we scroll to the bottom
	// of the message offset by 2-3 lines
	useEffect(() => {
		if (!containerRef.current) return;
		if (!isUserMessage) return;
		const c = containerRef.current;
		const messages = c.querySelectorAll('[data-agent-message-role="user"]');
		const last = messages[messages.length - 1];
		if (!last || messages.length < 2) return;
		const scrollArea = c.querySelector('#extendify-agent-chat-scroll-area');
		const lastRect = last.getBoundingClientRect();
		const innerHeight = Array.from(scrollArea.children).reduce(
			(sum, child) => sum + child.offsetHeight,
			0,
		);
		const minHeight = innerHeight + c.clientHeight - lastRect.height;
		scrollArea.style.minHeight = `${minHeight}px`;
		last.scrollIntoView({ behavior: 'smooth', block: 'start' });
	}, [isUserMessage, messages]);

	// Handles the scroll down button visibility
	useLayoutEffect(() => {
		const c = containerRef.current;
		if (!c) return;
		const last = c.querySelector(
			'#extendify-agent-chat-scroll-area > :last-child',
		);
		if (!last) return;

		const areWeAtBottom = c.scrollTop + c.clientHeight >= c.scrollHeight - 4;
		if (areWeAtBottom) return setCanScrollDown(false);

		const observer = new IntersectionObserver(
			([entry]) => {
				const last = c.querySelector(
					'#extendify-agent-chat-scroll-area > :last-child',
				);
				if (!last) return setCanScrollDown(false);
				const areWeAtBottom =
					c.scrollTop + c.clientHeight >= c.scrollHeight - 4;
				if (areWeAtBottom) return setCanScrollDown(false);
				setCanScrollDown(!entry.isIntersecting);
			},
			{ root: c, threshold: 0.1 },
		);

		observer.observe(last);
		return () => observer.disconnect();
	}, [messages]);

	return (
		<div
			ref={containerRef}
			style={{ overscrollBehavior: 'contain' }}
			className="relative grow overflow-y-auto overflow-x-hidden p-1 pb-0 text-sm text-gray-900 md:p-2"
		>
			<div
				id="extendify-agent-chat-scroll-area"
				className={ready ? '' : 'invisible pointer-events-none'}
			>
				{messages.map((message) => {
					const isLastMessage = messages.at(-1)?.id === message.id;
					const freshLoad = isFreshPageLoad.current;
					if (message.details?.role === 'user') {
						return <UserMessage key={message.id} message={message} />;
					}
					if (message.details?.role === 'assistant') {
						return (
							<AgentMessage
								key={message.id}
								animate={!freshLoad}
								message={message}
							/>
						);
					}
					if (message.type === 'workflow') {
						return <WorkflowMessage key={message.id} message={message} />;
					}
					if (message.type === 'workflow-component') {
						return <WorkflowComponent key={message.id} message={message} />;
					}
					if (
						message.type === 'status' &&
						// Only show the status if it's last, or a workflow-tool-completed message
						(isLastMessage ||
							['workflow-tool-completed', 'workflow-canceled'].includes(
								message.details?.type,
							))
					) {
						const isError = message.details?.type === 'error';
						return (
							<StatusMessage
								animate={!isError}
								key={message.id}
								status={message}
							/>
						);
					}
					return null;
				})}
				{!workflow?.needsRedirect?.() &&
				whenFinishedToolProps?.id &&
				whenFinishedComponent ? (
					<ScrollIntoViewOnce>
						{createElement(whenFinishedComponent, whenFinishedToolProps)}
					</ScrollIntoViewOnce>
				) : null}
				{workflow?.needsRedirect?.() ? <workflow.redirectComponent /> : null}
			</div>
			<ScrollDownButton
				canScrollDown={canScrollDown}
				onClick={() => {
					if (!containerRef.current) return;
					const c = containerRef.current;
					// Scroll the last message into view
					const last = c.querySelector(
						'#extendify-agent-chat-scroll-area > :last-child',
					);
					if (!last) return;
					last.scrollIntoView({ behavior: 'smooth', block: 'start' });
				}}
			/>
		</div>
	);
};
