import React, { useEffect, useCallback } from 'react';
import styled from 'styled-components';

import type { JSX } from 'react';
import type {
  AiSearchConversationItem,
  SearchAiMessageResource,
  FeedbackType,
  ToolCall,
  ContentSegment,
} from '@redocly/theme/core/types';

import { useThemeConfig, useThemeHooks } from '@redocly/theme/core/hooks';
import { Button } from '@redocly/theme/components/Button/Button';
import { SearchAiConversationInput } from '@redocly/theme/components/Search/SearchAiConversationInput';
import {
  AiSearchError,
  AI_SEARCH_ERROR_CONFIG as ERROR_CONFIG,
  AiSearchConversationRole,
} from '@redocly/theme/core/constants';
import { SearchAiMessage } from '@redocly/theme/components/Search/SearchAiMessage';
import { Admonition } from '@redocly/theme/components/Admonition/Admonition';
import { AiStarsIcon } from '@redocly/theme/icons/AiStarsIcon/AiStarsIcon';

export type SearchAiDialogProps = {
  response: string | undefined;
  isGeneratingResponse: boolean;
  error: AiSearchError | null;
  resources: SearchAiMessageResource[];
  initialMessage?: string;
  className?: string;
  conversation: AiSearchConversationItem[];
  setConversation: React.Dispatch<React.SetStateAction<AiSearchConversationItem[]>>;
  onMessageSent: (
    message: string,
    history?: AiSearchConversationItem[],
    messageId?: string,
  ) => void;
  toolCalls?: ToolCall[];
  contentSegments?: ContentSegment[];
};

export function SearchAiDialog({
  isGeneratingResponse,
  response,
  initialMessage,
  error,
  resources,
  onMessageSent,
  className,
  conversation,
  setConversation,
  toolCalls = [],
  contentSegments,
}: SearchAiDialogProps): JSX.Element {
  const { useTranslate } = useThemeHooks();
  const { aiAssistant } = useThemeConfig();
  const { translate } = useTranslate();

  const conversationEndRef = React.useRef<HTMLDivElement>(null);

  const suggestions = aiAssistant?.suggestions;

  const placeholder = isGeneratingResponse
    ? translate('search.ai.generatingResponse', 'Generating response...')
    : conversation.length > 0
      ? translate('search.ai.followUpQuestion', 'Ask a follow up question?')
      : translate('search.ai.placeholder', 'Ask a question...');

  const scrollToBottom = useCallback(() => {
    conversationEndRef.current?.scrollIntoView({ block: 'end' });
  }, []);

  const handleOnMessageSent = useCallback(
    (message: string) => {
      if (!message.trim()) {
        return;
      }
      const mappedHistory = conversation.map(({ role, content }) => ({
        role,
        content,
      }));

      onMessageSent(message, mappedHistory);
      setConversation((prev) => [
        ...prev,
        { role: AiSearchConversationRole.USER, content: message },
      ]);
    },
    [conversation, onMessageSent, setConversation],
  );

  useEffect(() => {
    if (!initialMessage?.trim().length) {
      return;
    }
    setConversation((prev) => [
      ...prev,
      { role: AiSearchConversationRole.USER, content: initialMessage },
    ]);
  }, [initialMessage, setConversation]);

  useEffect(() => {
    if (response === undefined || conversation.length === 0 || error) {
      return;
    }

    setConversation((prev) => {
      const lastMessage = prev[prev.length - 1];
      const content: string = response || '';

      if (lastMessage && lastMessage.role === AiSearchConversationRole.ASSISTANT) {
        return [
          ...prev.slice(0, -1),
          {
            role: AiSearchConversationRole.ASSISTANT,
            content,
            resources,
            messageId: lastMessage.messageId,
            toolCalls,
            contentSegments,
          },
        ];
      }

      return [
        ...prev,
        {
          role: AiSearchConversationRole.ASSISTANT,
          content,
          resources,
          toolCalls,
          contentSegments,
        },
      ];
    });
  }, [
    response,
    conversation.length,
    error,
    resources,
    toolCalls,
    contentSegments,
    setConversation,
  ]);

  useEffect(() => {
    if (error) {
      setConversation((prev) => prev.slice(0, -1));
    }
  }, [error, setConversation]);

  useEffect(() => {
    scrollToBottom();
  }, [conversation, isGeneratingResponse, scrollToBottom]);

  const handleFeedbackChange = useCallback(
    (messageId: string, feedback: FeedbackType | undefined) => {
      setConversation((prev) =>
        prev.map((item) => (item.messageId === messageId ? { ...item, feedback } : item)),
      );
    },
    [setConversation],
  );

  return (
    <SearchAiDialogWrapper data-component-name="Search/SearchAiDialog" className={className}>
      {!conversation.length && (
        <WelcomeWrapper>
          <AiStarsIcon
            color="var(--search-ai-icon-color)"
            size="32px"
            background="var(--search-ai-icon-bg-color)"
            borderRadius="var(--border-radius-lg)"
            margin="0 var(--spacing-xs) 0 0"
          />
          {translate(
            'search.ai.welcomeText',
            'Welcome to AI search! Feel free to ask me anything. How can I help you? ',
          )}
        </WelcomeWrapper>
      )}

      <ConversationWrapper>
        {conversation.map((item, index) => (
          <SearchAiMessage
            key={`search-ai-message-${index}`}
            role={item.role}
            content={item.content}
            isThinking={
              item.role === AiSearchConversationRole.ASSISTANT &&
              isGeneratingResponse &&
              index === conversation.length - 1
            }
            resources={item.resources}
            messageId={item.messageId}
            feedback={item.feedback}
            onFeedbackChange={handleFeedbackChange}
            toolCalls={
              item.toolCalls || (index === conversation.length - 1 ? toolCalls : undefined)
            }
            contentSegments={item.contentSegments}
          />
        ))}

        {error && (
          <Admonition
            type="danger"
            name={translate(ERROR_CONFIG[error].headerKey, ERROR_CONFIG[error].headerDefault)}
          >
            {translate(ERROR_CONFIG[error].messageKey, ERROR_CONFIG[error].messageDefault)}
          </Admonition>
        )}

        {!conversation.length && !error && (
          <SuggestionsWrapper>
            {suggestions?.map((suggestion) => (
              <Button
                key={suggestion}
                variant="outlined"
                onClick={() => handleOnMessageSent(suggestion)}
              >
                {suggestion}
              </Button>
            ))}
          </SuggestionsWrapper>
        )}

        <div ref={conversationEndRef} />
      </ConversationWrapper>

      <ConversationInputWrapper>
        <SearchAiConversationInput
          onMessageSent={handleOnMessageSent}
          isGeneratingResponse={isGeneratingResponse}
          placeholder={placeholder}
        />
      </ConversationInputWrapper>
    </SearchAiDialogWrapper>
  );
}

const SearchAiDialogWrapper = styled.div`
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  width: 100%;
  min-width: 0;
  min-height: 0;
  position: relative;
  background: var(--search-ai-dialog-bg-color);
  overflow: hidden;
`;

const ConversationWrapper = styled.div`
  flex: 1 1 auto;
  overflow-y: auto;
  overflow-x: hidden;
  padding: var(--search-ai-dialog-body-padding);
  padding-bottom: 0;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--search-ai-dialog-body-gap);
  min-height: 0;

  > :first-child {
    margin-top: auto;
  }
  > :last-child {
    margin-bottom: 0;
    gap: 0;
  }
`;

const SuggestionsWrapper = styled.div`
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  gap: var(--search-ai-suggestions-gap);
  align-items: center;
  justify-content: center;
`;

const ConversationInputWrapper = styled.div`
  padding: var(--search-ai-dialog-input-padding);
  border-top: var(--search-ai-dialog-input-border);
  background: var(--search-ai-dialog-input-bg-color);
`;

const WelcomeWrapper = styled.div`
  display: flex;
  flex-direction: row;
  align-items: center;
  width: auto;
  margin: var(--search-ai-welcome-margin);
  position: relative;
`;
