import React, { forwardRef, useState, useEffect, useRef, JSX } from 'react';
import { Box, Button, Tooltip, SxProps } from '@mui/material';
import { Send as SendIcon } from '@mui/icons-material';
import { useAuth } from 'hooks/AuthContext';
import {
  ChatMessage,
  ChatSession,
  ChatMessageUser,
  ChatMessageError,
  ChatMessageStreaming,
  ChatMessageStatus,
  ChatMessageMetaData,
  CandidateQuestion,
  Resume,
} from 'types/types';
import { ConversationHandle } from 'components/Conversation';
import { Message } from 'components/Message';
import { DeleteConfirmation } from 'components/DeleteConfirmation';
import { CandidateInfo } from 'components/ui/CandidateInfo';
import { useAppState, useSelectedCandidate, useSelectedJob } from 'hooks/GlobalContext';
import PropagateLoader from 'react-spinners/PropagateLoader';
import { BackstoryTextField, BackstoryTextFieldRef } from 'components/BackstoryTextField';
import { BackstoryQuery } from 'components/BackstoryQuery';
import { CandidatePicker } from 'components/ui/CandidatePicker';
import { Scrollable } from 'components/Scrollable';
import { useParams } from 'react-router-dom';
import ResumePreview from 'components/ui/ResumePreview';
import { formatDate } from 'utils/formatDate';

const emptyMetadata: ChatMessageMetaData = {
  model: 'qwen2.5',
  temperature: 0,
  maxTokens: 0,
  topP: 0,
  frequencyPenalty: 0,
  presencePenalty: 0,
  stopSequences: [],
  usage: {
    evalCount: 0,
    evalDuration: 0,
    promptEvalCount: 0,
    promptEvalDuration: 0,
  },
};

const defaultMessage: ChatMessage = {
  status: 'done',
  type: 'text',
  sessionId: '',
  timestamp: new Date(),
  content: '',
  role: 'user',
  metadata: emptyMetadata,
};

const defaultQuestion: CandidateQuestion = {
  question:
    'How well does the resume align with the job description? What are the three key strengths and two greatest weaknesses?',
};

interface CandidateChatPageProps {
  sx?: SxProps; // Optional styles for the component
}

const CandidateChatPage = forwardRef<ConversationHandle, CandidateChatPageProps>(
  (props: CandidateChatPageProps, ref): JSX.Element => {
    const { resumeId } = useParams<{ resumeId?: string }>();
    const { selectedJob, setSelectedJob } = useSelectedJob();
    const [resume, setResume] = useState<Resume | null>(null);
    const { sx } = props;
    const { apiClient } = useAuth();
    const { selectedCandidate, setSelectedCandidate } = useSelectedCandidate();
    const [processingMessage, setProcessingMessage] = useState<
      ChatMessageStatus | ChatMessageError | null
    >(null);
    const [streamingMessage, setStreamingMessage] = useState<ChatMessage | null>(null);
    const backstoryTextRef = useRef<BackstoryTextFieldRef>(null);

    const { setSnack } = useAppState();

    const [chatSession, setChatSession] = useState<ChatSession | null>(null);
    const [messages, setMessages] = useState<ChatMessage[]>([]);
    const [loading, setLoading] = useState<boolean>(false);
    const [streaming, setStreaming] = useState<boolean>(false);
    const messagesEndRef = useRef<HTMLDivElement>(null);

    useEffect(() => {
      if (!resumeId || resume) return;
      apiClient
        .getResume(resumeId)
        .then(resume => {
          setResume(resume);
          if (resume.candidate && resume.candidate !== selectedCandidate) {
            setSelectedCandidate(resume.candidate);
          }
          if (resume.job && resume.job !== selectedJob) {
            setSelectedJob(resume.job);
          }
        })
        .catch(error => {
          console.error('Failed to load resume:', error);
          setSnack('Failed to load resume', 'error');
        });
    }, [resumeId, resume, apiClient, setSnack]);

    const onDelete = async (session: ChatSession): Promise<void> => {
      if (!session.id) {
        return;
      }
      try {
        await apiClient.resetChatSession(session.id);
        // If we're deleting the currently selected session, clear it
        setMessages([]);
        setSnack('Session reset succeeded', 'success');
      } catch (error) {
        console.error('Failed to delete session:', error);
        setSnack('Failed to delete session', 'error');
      }
    };

    // Send message
    const sendMessage = async (message: string): Promise<void> => {
      if (!message.trim() || !chatSession?.id || streaming || !selectedCandidate) return;

      const messageContent = message;
      setStreaming(true);

      const chatMessage: ChatMessageUser = {
        sessionId: chatSession.id,
        role: 'user',
        content: messageContent,
        status: 'done',
        type: 'text',
        timestamp: new Date(),
        extraContext: {
          candidateId: resume?.candidate?.id,
          jobId: resume?.job?.id,
          resumeId: resume?.id,
        },
      };

      setProcessingMessage({
        ...defaultMessage,
        status: 'status',
        activity: 'info',
        content: `Establishing connection with ${selectedCandidate.firstName}'s chat session.`,
      });

      setMessages(prev => {
        const filtered = prev.filter(m => m.id !== chatMessage.id);
        return [...filtered, chatMessage] as ChatMessage[];
      });

      try {
        apiClient.sendMessageStream(chatMessage, {
          onMessage: (msg: ChatMessage): void => {
            setMessages(prev => {
              const filtered = prev.filter(m => m.id !== msg.id);
              return [...filtered, msg] as ChatMessage[];
            });
            setStreamingMessage(null);
            setProcessingMessage(null);
          },
          onError: (error: string | ChatMessageError): void => {
            console.log('onError:', error);
            // Type-guard to determine if this is a ChatMessageBase or a string
            if (typeof error === 'object' && error !== null && 'error' in error) {
              setSnack(`Error: ${error.error}`, 'error');
            } else if (typeof error === 'string') {
              setSnack(`Error: ${error}`, 'error');
            } else {
              setSnack(`An unknown error occurred: ${JSON.stringify(error)}`, 'error');
            }
            setStreaming(false);
          },
          onStreaming: (chunk: ChatMessageStreaming): void => {
            // console.log("onStreaming:", chunk);
            setStreamingMessage({
              ...chunk,
              role: 'assistant',
              metadata: emptyMetadata,
            });
          },
          onStatus: (status: ChatMessageStatus): void => {
            setProcessingMessage(status);
          },
          onComplete: (): void => {
            console.log('onComplete');
            setStreamingMessage(null);
            setProcessingMessage(null);
            setStreaming(false);
          },
        });
      } catch (error) {
        console.error('Failed to send message:', error);
        setStreaming(false);
      }
    };

    // Auto-scroll to bottom when new messages arrive
    useEffect(() => {
      messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
    }, [messages]);

    // Load sessions when username changes
    useEffect(() => {
      if (!selectedCandidate) return;
      try {
        setLoading(true);
        apiClient
          .getOrCreateChatSession(
            selectedCandidate,
            resumeId
              ? `Backstory Resume chat ${resumeId}`
              : `Backstory Chat with ${selectedCandidate.fullName}`,
            resumeId ? 'resume_chat' : 'candidate_chat'
          )
          .then(session => {
            setChatSession(session);
            setLoading(false);
          });
      } catch (error) {
        setSnack('Unable to load chat session', 'error');
      } finally {
        setLoading(false);
      }
    }, [selectedCandidate, apiClient, setSnack]);

    // Load messages when session changes
    useEffect(() => {
      const loadMessages = async (): Promise<void> => {
        if (!chatSession?.id) return;

        try {
          const result = await apiClient.getChatMessages(chatSession.id);
          const chatMessages: ChatMessage[] = result.data;
          setMessages(chatMessages);
          setProcessingMessage(null);
          setStreamingMessage(null);
          console.log(`getChatMessages returned ${chatMessages.length} messages.`, chatMessages);
        } catch (error) {
          console.error('Failed to load messages:', error);
        }
      };

      if (chatSession?.id) {
        loadMessages();
      }
    }, [chatSession, apiClient]);

    if (!selectedCandidate) {
      return <CandidatePicker />;
    }

    const welcomeMessage: ChatMessage = {
      sessionId: chatSession?.id || '',
      role: 'information',
      type: 'text',
      status: 'done',
      timestamp: new Date(),
      content:
        `Welcome to the Backstory Chat about ${selectedCandidate.fullName}` +
        (resume && ` and the ${resume.job?.title} position at ${resume.job?.company}`) +
        `. Enter any questions you have about ${selectedCandidate.firstName}'${
          selectedCandidate.firstName.slice(-1) !== 's' ? 's' : ''
        } resume or skills, or select from the available questions.`,
      metadata: emptyMetadata,
    };

    const handleSubmitQuestion = (question: CandidateQuestion): void => {
      sendMessage(question.question);
    };

    return (
      <Box
        ref={ref}
        sx={{
          display: 'flex',
          flexDirection: 'row',
          height: '100%' /* Restrict to main-container's height */,
          width: '100%',
          minHeight: 0 /* Prevent flex overflow */,
          maxHeight: '100%',
          '& > *:not(.Scrollable)': {
            flexShrink: 0 /* Prevent shrinking */,
          },
          ...sx,
          p: 0,
          m: 0,
          backgroundColor: '#D3CDBF' /* Warm Gray */,
        }}
      >
        {resume && (
          <Box
            sx={{
              display: 'flex',
              flexDirection: 'column',
              width: '50%',
              maxHeight: '100%',
              p: 0,
              m: 0,
              position: 'relative',
              backgroundColor: '#f5f5f5',
            }}
          >
            <Box
              sx={{
                position: 'relative',
                display: 'flex',
                justifyContent: 'center',
                p: 1,
              }}
            >
              <strong>{resume.job?.title}</strong>&nbsp;at&nbsp;
              <strong>{resume.job?.company}</strong>. Last updated{' '}
              {formatDate(resume.updatedAt, false, true)}.
            </Box>
            <Scrollable sx={{ m: 0 }}>
              <ResumePreview shadeMargins={false} resume={resume} />
            </Scrollable>
          </Box>
        )}

        <Box
          sx={{
            display: 'flex',
            flexDirection: 'column',
            width: '50%',
            p: 1,
            m: resume ? 0 : '0 auto',
            position: 'relative',
            backgroundColor: 'background.paper',
          }}
        >
          {!resume && (
            <CandidateInfo
              key={selectedCandidate.username}
              action={`Chat with Backstory about ${selectedCandidate.firstName}`}
              elevation={4}
              candidate={selectedCandidate}
              variant="normal"
              sx={{
                width: '100%',
                maxHeight: 0,
                minHeight: 'min-content',
              }} // Prevent header from shrinking
            />
          )}
          {/* Chat Interface */}
          {/* Scrollable Messages Area */}
          {chatSession && (
            <Scrollable
              sx={{
                width: '100%',
              }}
            >
              <Box sx={{ display: 'flex', flexGrow: 1 }}></Box>
              {messages.length === 0 && <Message {...{ chatSession, message: welcomeMessage }} />}
              {messages.map((message: ChatMessage) => (
                <Message key={message.id} {...{ chatSession, message }} />
              ))}
              {processingMessage !== null && (
                <Message {...{ chatSession, message: processingMessage }} />
              )}
              {streamingMessage !== null && (
                <Message {...{ chatSession, message: streamingMessage }} />
              )}
              {streaming && (
                <Box
                  sx={{
                    display: 'flex',
                    flexDirection: 'column',
                    alignItems: 'center',
                    justifyContent: 'center',
                    m: 1,
                  }}
                >
                  <PropagateLoader
                    size="10px"
                    loading={streaming}
                    aria-label="Loading Spinner"
                    data-testid="loader"
                  />
                </Box>
              )}
              <div ref={messagesEndRef} />
            </Scrollable>
          )}
          <Box sx={{ display: 'flex', flexDirection: 'row', gap: 1, p: 1, flex: 0 }}>
            {selectedCandidate.questions?.map((q, i) => (
              <BackstoryQuery key={i} question={q} submitQuery={handleSubmitQuestion} />
            ))}
            {resume && (
              <BackstoryQuery question={defaultQuestion} submitQuery={handleSubmitQuestion} />
            )}
          </Box>
          {/* Fixed Message Input */}
          <Box sx={{ display: 'flex', gap: 1 }}>
            <DeleteConfirmation
              onDelete={(): void => {
                chatSession && onDelete(chatSession);
              }}
              disabled={!chatSession}
              sx={{ minWidth: 'auto', px: 2, maxHeight: 'min-content' }}
              action="reset"
              label="chat session"
              title="Reset Chat Session"
              message={`Are you sure you want to reset the session? This action cannot be undone.`}
            />
            <BackstoryTextField
              placeholder="Type your message about the candidate..."
              ref={backstoryTextRef}
              onEnter={sendMessage}
              disabled={streaming || loading}
            />
            <Tooltip title="Send">
              <span
                style={{
                  minWidth: 'auto',
                  maxHeight: 'min-content',
                  alignSelf: 'center',
                }}
              >
                <Button
                  variant="contained"
                  onClick={(): void => {
                    sendMessage(
                      (backstoryTextRef.current && backstoryTextRef.current.getAndResetValue()) ||
                        ''
                    );
                  }}
                  disabled={streaming || loading}
                >
                  <SendIcon />
                </Button>
              </span>
            </Tooltip>
          </Box>
        </Box>
      </Box>
    );
  }
);
CandidateChatPage.displayName = 'CandidateChatPage';
export { CandidateChatPage };
