import { useMutation, useQuery } from '@tanstack/react-query';
import { AxiosError } from 'axios';
import { nanoid } from 'nanoid';
import { useEffect, useRef, useState } from 'react';

import { ChatDrawerSource } from '@/app/builder/builder-hooks';
import { ChatInput, ChatMessage } from '@/components/ui/chat/chat-input';
import { ImageDialog } from '@/components/ui/chat/chat-message/image-dialog';
import {
  ChatMessageList,
  Messages,
} from '@/components/ui/chat/chat-message-list';
import { LoadingScreen } from '@/components/ui/loading-screen';
import { ChatIntro } from '@/features/chat/chat-intro';
import { humanInputApi } from '@/features/forms/lib/human-input-api';
import { cn } from '@/lib/utils';
import {
  ApErrorParams,
  ChatUIResponse,
  ErrorCode,
  isNil,
  HumanInputFormResultTypes,
} from '@activepieces/shared';

interface FlowChatProps {
  flowId: string;
  className?: string;
  showWelcomeMessage?: boolean;
  mode: ChatDrawerSource | null;
  onError?: (error: ApErrorParams | null) => void;
  onSendingMessage?: (message: ChatMessage) => void;
  closeChat?: () => void;
  messages?: Messages;
  chatSessionId?: string | null;
  onAddMessage?: (message: Messages[0]) => void;
  onSetSessionId?: (sessionId: string) => void;
}

export function FlowChat({
  flowId,
  className,
  showWelcomeMessage = true,
  mode,
  onError,
  onSendingMessage,
  closeChat,
  messages = [],
  chatSessionId,
  onAddMessage,
  onSetSessionId,
}: FlowChatProps) {
  const messagesRef = useRef<HTMLDivElement>(null);
  const chatInputRef = useRef<HTMLTextAreaElement>(null);

  const {
    data: chatUI,
    isLoading,
    isError: isLoadingError,
  } = useQuery<ChatUIResponse | null, Error>({
    queryKey: ['chat', flowId],
    queryFn: () =>
      humanInputApi.getChatUI(
        flowId,
        mode === ChatDrawerSource.TEST_FLOW ||
          mode === ChatDrawerSource.TEST_STEP
          ? true
          : false,
      ),
    enabled: !isNil(flowId),
    staleTime: Infinity,
    retry: false,
  });

  const scrollToBottom = () => {
    setTimeout(() => {
      const lastMessage = document.getElementById('last-message');
      if (lastMessage) {
        lastMessage.scrollIntoView({ behavior: 'smooth', block: 'start' });
      }
    }, 100);
  };

  // Initialize chat session ID if not set and we have the callback
  useEffect(() => {
    if (!chatSessionId && onSetSessionId) {
      onSetSessionId(nanoid());
    }
  }, [chatSessionId, onSetSessionId]);

  const previousInputRef = useRef('');
  const previousFilesRef = useRef<File[]>([]);
  const [sendingError, setSendingError] = useState<ApErrorParams | null>(null);
  const [selectedImage, setSelectedImage] = useState<string | null>(null);
  const [imageDialogOpen, setImageDialogOpen] = useState(false);

  const botName =
    chatUI?.props.botName ?? `${chatUI?.platformName ?? 'Activepieces'} Bot`;

  const { mutate: sendMessage, isPending: isSending } = useMutation({
    mutationFn: async ({
      isRetrying,
      message,
    }: {
      isRetrying: boolean;
      message?: ChatMessage;
    }) => {
      if (!flowId || !chatSessionId) return null;

      const savedInput = isRetrying
        ? previousInputRef.current
        : message?.textContent || '';
      const savedFiles = isRetrying
        ? previousFilesRef.current
        : message?.files || [];

      previousInputRef.current = savedInput;
      previousFilesRef.current = savedFiles;

      if (!isRetrying && message && onAddMessage) {
        onAddMessage({
          role: 'user',
          textContent: savedInput,
          files: savedFiles.map((file) => ({
            url: URL.createObjectURL(file),
            mimeType: file.type,
          })),
        });
      }

      scrollToBottom();
      const isDraft = mode === ChatDrawerSource.TEST_FLOW;
      const isTestStep = mode === ChatDrawerSource.TEST_STEP;
      return humanInputApi.sendMessage({
        flowId,
        chatId: chatSessionId,
        message: savedInput,
        files: savedFiles,
        mode: isDraft ? 'draft' : isTestStep ? 'test' : 'locked',
      });
    },

    onSuccess: (result) => {
      console.log(result);
      if (mode === ChatDrawerSource.TEST_STEP) {
        closeChat?.();
      }
      if (!result) {
        const error: ApErrorParams = {
          code: ErrorCode.NO_CHAT_RESPONSE,
          params: {},
        };
        setSendingError(error);
        onError?.(error);
        return;
      }

      if ('type' in result && onAddMessage) {
        setSendingError(null);
        onError?.(null);

        switch (result.type) {
          case HumanInputFormResultTypes.FILE: {
            if ('url' in result.value) {
              onAddMessage({
                role: 'bot',
                files: [
                  {
                    url: result.value.url,
                    mimeType: result.value.mimeType,
                  },
                ],
              });
            }
            break;
          }

          case HumanInputFormResultTypes.MARKDOWN: {
            const validFiles = (result.files ?? []).filter(
              (file) => 'url' in file && 'mimeType' in file,
            );

            onAddMessage({
              role: 'bot',
              textContent: result.value,
              files: validFiles.length > 0 ? validFiles : undefined,
            });
            break;
          }
        }
      }

      scrollToBottom();

      setTimeout(() => {
        if (chatInputRef.current) {
          chatInputRef.current.focus();
        }
      }, 100);
    },

    onError: (error: AxiosError) => {
      const errorData = error.response?.data as ApErrorParams;
      setSendingError(errorData);
      onError?.(errorData);
      scrollToBottom();
    },
  });

  useEffect(scrollToBottom, [messages, isSending]);

  const handleSendMessage = (message: ChatMessage) => {
    onSendingMessage?.(message);

    sendMessage({ isRetrying: false, message });
  };

  if (isLoadingError) {
    return null;
  }

  if (isLoading) return <LoadingScreen />;

  const toggleImageDialog = (imageUrl: string | null) => {
    setImageDialogOpen(!!imageUrl);
    setSelectedImage(imageUrl);
  };

  return (
    <main
      className={cn(
        'flex w-full flex-col items-center justify-center pb-6',
        messages.length > 0 ? 'h-screen' : 'h-screen',
        className,
      )}
    >
      {messages.length > 0 ? (
        <>
          <ChatMessageList
            messagesRef={messagesRef}
            messages={messages}
            chatUI={chatUI}
            sendingError={sendingError}
            isSending={isSending}
            flowId={flowId}
            sendMessage={sendMessage}
            setSelectedImage={toggleImageDialog}
          />
          <div className="w-full px-4 max-w-3xl">
            <ChatInput
              ref={chatInputRef}
              onSendMessage={handleSendMessage}
              disabled={isSending}
              placeholder="Type your message here..."
            />
          </div>
        </>
      ) : (
        <>
          {showWelcomeMessage && (
            <ChatIntro chatUI={chatUI} botName={botName} />
          )}
          <div className="w-full px-4 max-w-3xl absolute bottom-6">
            <ChatInput
              ref={chatInputRef}
              onSendMessage={handleSendMessage}
              disabled={isSending}
              placeholder="Type your message here..."
            />
          </div>
        </>
      )}
      <ImageDialog
        open={imageDialogOpen}
        onOpenChange={(open) => {
          setImageDialogOpen(open);
          if (!open) setSelectedImage(null);
        }}
        imageUrl={selectedImage}
      />
    </main>
  );
}
