import React, { useContext, useEffect, useRef, useState } from "react";
import styled, { ThemeContext } from "styled-components";
import { Formik, Form, Field } from "formik";
import * as Yup from "yup";
import { AttachFile, Delete, Send, Close, FileDownload } from "@mui/icons-material";
import { Tooltip, Menu, MenuItem, IconButton as MuiIconButton } from "@mui/material";
import Markdown from "markdown-to-jsx";

import {
  ConversationPrompt,
} from "../../data";
import { ConversationContext } from "../../contexts/ConversationContext";
import ConversationDetailCard from "../../components/ConversationDetailCard/ConversationDetailCard";
import { WebSocketContext, ConnectionStatus } from "../../contexts/WebSocketContext";
import { MessageContext } from "../../contexts/MessageContext";
import ParticleBackground from "../../components/ParticleBackground/ParticleBackground";

import Header2 from "../../components/Header2/Header2";
import { AnalyticsEvents, trackEvent } from "../../utils/analytics";
import { exportChat, type ExportFormat } from "../../utils/export/exportChat";
import { showToast } from "../../utils/toastBus";
import type { PromptRating } from "../../utils/promptFeedback";

// Styled Components
const PageContainer = styled.div`
  position: relative;
  width: 100%;
  max-width: 100%;
  height: 100vh;
  /* Mobile URL bars change the viewport height; dvh keeps the input row reachable. */
  height: 100dvh;
  overflow: hidden;
  display: flex;
  color: ${({ theme }) => theme.colors.text};
  font-family: 'Inter', sans-serif;
  /* background-color: ${({ theme }) => theme.colors.background}; Removed to show particles */
`;

const Sidebar = styled.div<{ $isOpen: boolean }>`
  width: 280px;
  flex-shrink: 0;
  box-sizing: border-box;
  height: 100%;
  background: ${({ theme }) => theme.darkMode ? 'rgba(0, 0, 0, 0.6)' : 'rgba(255, 255, 255, 0.6)'};
  backdrop-filter: blur(10px);
  border-right: 1px solid ${({ theme }) => theme.colors.cardBorder};
  display: flex;
  flex-direction: column;
  padding: 1rem;
  padding-top: 5rem; // Account for header
  z-index: 20;
  transition: transform 0.3s ease;

  @media (max-width: 768px) {
    position: fixed;
    top: 0;
    left: 0;
    width: min(85vw, 320px);
    height: 100dvh;
    padding-top: calc(4.5rem + env(safe-area-inset-top, 0px));
    padding-bottom: calc(1rem + env(safe-area-inset-bottom, 0px));
    transform: ${({ $isOpen }) => $isOpen ? 'translateX(0)' : 'translateX(-100%)'};
    background: ${({ theme }) => theme.darkMode ? 'rgba(0, 0, 0, 0.95)' : 'rgba(255, 255, 255, 0.95)'};
    box-shadow: ${({ $isOpen }) => $isOpen ? '0 0 20px rgba(0,0,0,0.5)' : 'none'};
    z-index: 120;
  }
`;

const Overlay = styled.div<{ $isOpen: boolean }>`
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
  backdrop-filter: blur(2px);
  z-index: 110;
  opacity: ${({ $isOpen }) => $isOpen ? 1 : 0};
  pointer-events: ${({ $isOpen }) => $isOpen ? 'auto' : 'none'};
  transition: opacity 0.3s ease;

  @media (max-width: 768px) {
    display: block;
  }
`;

const MobileSidebarHeader = styled.div`
  display: none;

  @media (max-width: 768px) {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 1rem;
    color: ${({ theme }) => theme.colors.text};
    font-weight: 700;
    font-size: 1rem;
  }
`;

const MobileSidebarCloseButton = styled.button`
  display: none;

  @media (max-width: 768px) {
    display: flex;
    align-items: center;
    justify-content: center;
    border: none;
    background: transparent;
    color: ${({ theme }) => theme.colors.text};
    cursor: pointer;
    border-radius: 999px;
    padding: 0.3rem;
  }
`;

const MainContent = styled.div`
  flex: 1;
  min-width: 0;
  height: 100%;
  display: flex;
  flex-direction: column;
  position: relative;
  z-index: 5;
  padding-top: calc(4rem + env(safe-area-inset-top, 0px)); // Account for header

  @media (max-width: 768px) {
    padding-top: calc(3.75rem + env(safe-area-inset-top, 0px));
  }
`;

const ChatArea = styled.div`
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  padding: 2rem;
  display: flex;
  flex-direction: column;
  scroll-behavior: smooth;

  @media (max-width: 768px) {
    padding: 1rem 0.85rem;
  }

  &::-webkit-scrollbar {
    width: 8px;
  }
  &::-webkit-scrollbar-track {
    background: transparent;
  }
  &::-webkit-scrollbar-thumb {
    background: rgba(255, 255, 255, 0.2);
    border-radius: 4px;
  }
`;

const ChatToolbar = styled.div`
  display: flex;
  justify-content: flex-end;
  align-items: center;
  padding: 0.25rem 2rem 0;
  flex-shrink: 0;

  @media (max-width: 768px) {
    padding: 0.25rem 0.85rem 0;
  }
`;

const InputArea = styled.div`
  flex-shrink: 0;
  box-sizing: border-box;
  padding: 1.5rem 2rem;
  background: ${({ theme }) => theme.darkMode ? 'rgba(0, 0, 0, 0.4)' : 'rgba(255, 255, 255, 0.4)'};
  backdrop-filter: blur(5px);
  border-top: 1px solid ${({ theme }) => theme.colors.cardBorder};

  @media (max-width: 768px) {
    padding: 0.75rem 0.75rem calc(0.75rem + env(safe-area-inset-bottom, 0px));
  }
`;

const StyledInputContainer = styled.div`
  position: relative;
  box-sizing: border-box;
  width: 100%;
  max-width: 900px;
  margin: 0 auto;
  background: ${({ theme }) => theme.darkMode ? 'rgba(255, 255, 255, 0.05)' : 'rgba(0, 0, 0, 0.05)'};
  border: 1px solid ${({ theme }) => theme.colors.cardBorder};
  border-radius: 1.5rem;
  padding: 0.5rem 1rem;
  display: flex;
  align-items: center;
  gap: 0.25rem;
  transition: all 0.3s ease;

  @media (max-width: 768px) {
    padding: 0.35rem 0.5rem;
    border-radius: 1.25rem;
  }

  &:focus-within {
    background: ${({ theme }) => theme.darkMode ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)'};
    border-color: ${({ theme }) => theme.main};
    box-shadow: 0 0 15px ${({ theme }) => theme.main}33;
  }
`;

const StyledInput = styled.textarea`
  flex: 1;
  min-width: 0;
  background: transparent;
  border: none;
  color: ${({ theme }) => theme.colors.text};
  /* Below 16px iOS/Android browsers zoom the page on focus. */
  font-size: 1rem;
  padding: 0.8rem;
  outline: none;
  resize: none;
  overflow-y: auto;
  max-height: 150px; /* Approx 5 lines */
  font-family: inherit;
  line-height: 1.5;

  &::placeholder {
    color: ${({ theme }) => theme.darkMode ? 'rgba(255, 255, 255, 0.4)' : 'rgba(0, 0, 0, 0.4)'};
  }
`;

const IconButton = styled.button`
  background: transparent;
  border: none;
  flex-shrink: 0;
  color: ${({ theme }) => theme.darkMode ? 'rgba(255, 255, 255, 0.6)' : 'rgba(0, 0, 0, 0.6)'};
  cursor: pointer;
  padding: 0.5rem;
  border-radius: 50%;
  transition: all 0.2s ease;
  display: flex;
  align-items: center;
  justify-content: center;

  &:hover {
    color: ${({ theme }) => theme.colors.text};
    background: ${({ theme }) => theme.darkMode ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)'};
  }

  &:disabled {
    opacity: 0.5;
    cursor: not-allowed;
  }
`;

const ConversationItem = styled.div<{ $active: boolean }>`
  padding: 0.8rem 1rem;
  margin-bottom: 0.5rem;
  border-radius: 0.5rem;
  cursor: pointer;
  background: ${(props) => (props.$active ? props.theme.main + "33" : "transparent")};
  color: ${(props) => (props.$active ? props.theme.colors.text : props.theme.darkMode ? "rgba(255, 255, 255, 0.7)" : "rgba(0, 0, 0, 0.7)")};
  transition: all 0.2s ease;
  border: 1px solid ${(props) => (props.$active ? props.theme.main + "66" : "transparent")};
  display: flex;
  justify-content: space-between;
  align-items: center;

  &:hover {
    background: ${({ theme }) => theme.darkMode ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)'};
    color: ${({ theme }) => theme.colors.text};
  }
`;

const ConversationTitle = styled.span`
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  flex: 1;
  min-width: 0;
  margin-right: 8px;
`;

const NewChatButton = styled.button`
  width: 100%;
  padding: 0.8rem;
  margin-bottom: 1rem;
  background: ${({ theme }) => theme.main};
  border: none;
  border-radius: 0.5rem;
  color: #fff;
  font-weight: 600;
  cursor: pointer;
  transition: transform 0.2s ease, box-shadow 0.2s ease;

  &:hover {
    transform: translateY(-2px);
    box-shadow: 0 4px 12px ${({ theme }) => theme.main}66;
  }
`;

const VisuallyHiddenInput = styled.input`
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  height: 1px;
  overflow: hidden;
  position: absolute;
  bottom: 0;
  left: 0;
  white-space: nowrap;
  width: 1px;
`;

const validationSchema = Yup.object().shape({
  prompt: Yup.string()
    .trim()
    .min(1, "Need to have at least one character")
    .required("This is required"),
});

/** Fixed until product re-exposes a model picker (#106). */
const DEFAULT_MODEL_NAME = "THINKING";

type PromptValues = {
  prompt: string;
  file: Blob | null;
  fileType: string | null;
};

const AlwaysScrollToBottom = ({ trigger }: { trigger: string | number }): JSX.Element => {
  const elementRef = useRef<HTMLDivElement>(null);
  useEffect(() => {
    elementRef.current?.scrollIntoView({ behavior: "smooth", block: "end" });
  }, [trigger]);
  return <div ref={elementRef} />;
};

const AsyncDashboardInner = (): JSX.Element => {
  const [, , , sendMessage, isConnected, connectionStatus] =
    useContext(WebSocketContext);

  const { conversations, selectedConversation, setSelectedConversation, deleteConversation } =
    useContext(ConversationContext);

  const {
    conversationDetails,
    setConversationDetails,
    stateMessage,
    streamInterrupted,
    activityStage,
    activityLabel,
    activityDetail,
    activityHistory,
    clearActivity,
  } = useContext(MessageContext);

  const conversationRef = useRef(conversationDetails);
  const theme = useContext(ThemeContext);
  const textareaRef = useRef<HTMLTextAreaElement>(null);
  const [isSidebarOpen, setIsSidebarOpen] = useState(false);
  const [exportAnchor, setExportAnchor] = useState<null | HTMLElement>(null);

  const selectedTitle =
    conversations.find((c) => c.id === selectedConversation)?.title ||
    'conversation';

  const handleConversationExport = async (format: ExportFormat) => {
    setExportAnchor(null);
    const turns = conversationDetails
      .filter((d) => d.message.length > 0)
      .map((d) => ({
        role: (d.user_created ? 'user' : 'assistant') as 'user' | 'assistant',
        message: d.message,
        timestamp: d.created_timestamp,
        citations: d.citations,
      }));
    if (!turns.length) {
      showToast('Nothing to export yet', 'info');
      return;
    }
    try {
      await exportChat({
        format,
        scope: 'conversation',
        title: selectedTitle,
        turns,
      });
      trackEvent(AnalyticsEvents.MESSAGE_EXPORTED, {
        format,
        scope: 'conversation',
      });
    } catch (err) {
      console.error(err);
      showToast('Export failed', 'error');
    }
  };

  const updatePromptRating = (promptId: number | undefined, rating: PromptRating | null) => {
    if (!promptId) return;
    const next = conversationDetails.map((detail) => {
      if (detail.id !== promptId) return detail;
      return new ConversationPrompt({
        ...detail,
        feedback: rating
          ? { rating, reason: detail.feedback?.reason, comment: detail.feedback?.comment }
          : null,
      });
    });
    conversationRef.current = next;
    setConversationDetails(next);
  };

  const connectionBanner = (() => {
    if (connectionStatus === ConnectionStatus.CONNECTED || isConnected) {
      return null;
    }
    if (connectionStatus === ConnectionStatus.CONNECTING) {
      return { text: "Connecting to chat…", color: "#f0a500" };
    }
    if (connectionStatus === ConnectionStatus.RECONNECTING) {
      return { text: "Reconnecting…", color: "#f0a500" };
    }
    return { text: "Connection to the LLM is not active", color: "#ff4444" };
  })();

  const handlePromptSubmit = async (
    { prompt, file, fileType }: PromptValues,
    { resetForm }: any,
  ): Promise<void> => {
    const trimmedPrompt = (prompt ?? "").trim();
    if (!trimmedPrompt) {
      return;
    }
    try {
      clearActivity();
      const tempConversations: ConversationPrompt[] = [
        ...conversationDetails,
        new ConversationPrompt({ message: trimmedPrompt, user_created: true }),
        new ConversationPrompt({ message: "", user_created: false }),
      ];

      conversationRef.current = tempConversations;
      setConversationDetails(tempConversations);
      sendMessage(trimmedPrompt, selectedConversation, file, fileType, DEFAULT_MODEL_NAME);
      trackEvent(AnalyticsEvents.MESSAGE_SENT, {
        hasConversation: Boolean(selectedConversation),
        hasAttachment: Boolean(file),
      });
      resetForm({
        values: {
          prompt: "",
          file: null,
          fileType: null,
        }
      });

      // Reset textarea height
      if (textareaRef.current) {
        textareaRef.current.style.height = 'auto';
      }
    } catch (e) {
      console.log(`error ${e}`);
    }
  };

  const adjustHeight = () => {
    const textarea = textareaRef.current;
    if (textarea) {
      textarea.style.height = 'auto';
      textarea.style.height = `${textarea.scrollHeight}px`;
    }
  };

  return (
    <PageContainer>
      <ParticleBackground />
      <Header2 onOpenConversations={() => setIsSidebarOpen(true)} />

      <Overlay $isOpen={isSidebarOpen} onClick={() => setIsSidebarOpen(false)} />

      <Sidebar $isOpen={isSidebarOpen}>
        <MobileSidebarHeader>
          <span>Conversations</span>
          <MobileSidebarCloseButton onClick={() => setIsSidebarOpen(false)} aria-label="Close conversations drawer">
            <Close fontSize="small" />
          </MobileSidebarCloseButton>
        </MobileSidebarHeader>
        <NewChatButton onClick={() => {
          setSelectedConversation(undefined);
          setIsSidebarOpen(false);
        }}>
          + New Chat
        </NewChatButton>
        <div style={{ overflowY: 'auto', flex: 1 }}>
          {conversations.length === 0 ? (
            <div style={{
              color: theme?.darkMode ? 'rgba(255,255,255,0.55)' : 'rgba(0,0,0,0.55)',
              fontSize: '0.9rem',
              lineHeight: 1.5,
              padding: '0.25rem 0.5rem'
            }}>
              No conversations yet. Start with <strong>+ New Chat</strong>.
            </div>
          ) : (
            conversations.map((convo) => (
              <ConversationItem
                key={convo.id}
                $active={convo.id === selectedConversation}
                onClick={() => {
                  setSelectedConversation(convo.id);
                  setIsSidebarOpen(false);
                }}
              >
                <ConversationTitle>{convo.title || "New Conversation"}</ConversationTitle>
                {convo.id === selectedConversation && (
                  <IconButton
                    as="div"
                    onClick={(e) => {
                      e.stopPropagation();
                      deleteConversation(convo.id);
                    }}
                    style={{ padding: 4, width: 'auto', height: 'auto' }}
                  >
                    <Delete fontSize="small" />
                  </IconButton>
                )}
              </ConversationItem>
            ))
          )}
        </div>
      </Sidebar>

      <MainContent>
        {conversationDetails.length > 0 && (
          <ChatToolbar>
            <Tooltip title="Export conversation">
              <MuiIconButton
                aria-label="Export conversation"
                size="small"
                onClick={(e) => setExportAnchor(e.currentTarget)}
                sx={{ color: theme?.colors?.text }}
              >
                <FileDownload fontSize="small" />
              </MuiIconButton>
            </Tooltip>
            <Menu
              anchorEl={exportAnchor}
              open={Boolean(exportAnchor)}
              onClose={() => setExportAnchor(null)}
            >
              {(
                [
                  ['pdf', 'PDF'],
                  ['csv', 'CSV'],
                  ['xlsx', 'Excel (.xlsx)'],
                  ['txt', 'Plain text'],
                ] as [ExportFormat, string][]
              ).map(([format, label]) => (
                <MenuItem key={format} onClick={() => void handleConversationExport(format)}>
                  {label}
                </MenuItem>
              ))}
            </Menu>
          </ChatToolbar>
        )}
        <ChatArea>
          {conversationDetails.length > 0 ? (
            conversationDetails.map((convo_detail, index) => {
              const isLast = index === conversationDetails.length - 1;
              const isLiveStream =
                !convo_detail.user_created && convo_detail.message.length === 0;
              const displayMessage = isLiveStream
                ? stateMessage
                : convo_detail.message;
              return (
                <ConversationDetailCard
                  key={convo_detail.id || index}
                  message={displayMessage}
                  user_created={convo_detail.user_created}
                  promptId={convo_detail.id}
                  citations={convo_detail.citations}
                  isStreaming={isLiveStream}
                  isLast={isLast}
                  conversationTitle={selectedTitle}
                  createdTimestamp={convo_detail.created_timestamp}
                  initialRating={convo_detail.feedback?.rating ?? null}
                  onRatingChange={(rating) =>
                    updatePromptRating(convo_detail.id, rating)
                  }
                  activityStage={isLiveStream ? activityStage : null}
                  activityLabel={isLiveStream ? activityLabel : null}
                  activityDetail={isLiveStream ? activityDetail : null}
                  activityHistory={isLiveStream ? activityHistory : []}
                  activityInterrupted={isLiveStream && streamInterrupted}
                />
              );
            })
          ) : (
            <div style={{
              display: 'flex',
              justifyContent: 'center',
              alignItems: 'center',
              height: '100%',
              color: theme?.darkMode ? 'rgba(255,255,255,0.5)' : 'rgba(0,0,0,0.5)',
              fontSize: '1.2rem'
            }}>
              <Markdown>Select a conversation or start a new one.</Markdown>
            </div>
          )}
          <AlwaysScrollToBottom trigger={`${conversationDetails.length}:${stateMessage.length}`} />
        </ChatArea>

        <InputArea>
          <Formik
            initialValues={{
              prompt: "",
              file: null,
              fileType: null,
            }}
            validationSchema={validationSchema}
            onSubmit={handlePromptSubmit}
          >
            {(formik) => (
              <Form>
                {connectionBanner && (
                  <div style={{
                    color: connectionBanner.color,
                    textAlign: 'center',
                    marginBottom: '0.5rem',
                    fontSize: '0.9rem',
                    fontWeight: 500
                  }}>
                    {connectionBanner.text}
                  </div>
                )}
                {streamInterrupted && isConnected && (
                  <div style={{
                    color: '#f0a500',
                    textAlign: 'center',
                    marginBottom: '0.5rem',
                    fontSize: '0.85rem',
                    fontWeight: 500
                  }}>
                    Last response was interrupted. You can re-send your message.
                  </div>
                )}
                <StyledInputContainer>
                  <label htmlFor="file-upload" style={{ cursor: 'pointer', display: 'flex' }}>
                    <IconButton as="span">
                      <AttachFile />
                    </IconButton>
                  </label>
                  <VisuallyHiddenInput
                    id="file-upload"
                    type="file"
                    accept=".csv,.xlsx,.txt,.pdf,.PDF"
                    onChange={(event) => {
                      const file = event.target.files?.[0];
                      if (file) {
                        formik.setFieldValue("file", file);
                        formik.setFieldValue("fileType", file.type);
                      } else {
                        formik.setFieldValue("file", null);
                        formik.setFieldValue("fileType", null);
                      }
                    }}
                  />

                  <Field name="prompt">
                    {({ field }: any) => (
                      <StyledInput
                        {...field}
                        ref={textareaRef}
                        placeholder="Type a message..."
                        rows={1}
                        onKeyDown={(e: React.KeyboardEvent) => {
                          if (e.key === 'Enter' && !e.shiftKey) {
                            e.preventDefault();
                            if (formik.isValid && formik.dirty) {
                              formik.submitForm();
                            }
                          }
                        }}
                        onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => {
                          field.onChange(e);
                          adjustHeight();
                        }}
                      />
                    )}
                  </Field>

                  <Tooltip title={!isConnected ? "Connection to the LLM is not active" : "Send message"}>
                    <span>
                      <IconButton
                        type="submit"
                        disabled={!formik.isValid || !formik.dirty || !isConnected}
                        style={{
                          color: (formik.isValid && formik.dirty && isConnected) ? '#fff' : undefined,
                          background: (formik.isValid && formik.dirty && isConnected) ? theme?.main : undefined
                        }}
                      >
                        <Send />
                      </IconButton>
                    </span>
                  </Tooltip>
                </StyledInputContainer>
                {formik.values.file && (
                  <div style={{
                    marginTop: '0.5rem',
                    fontSize: '0.8rem',
                    color: theme?.darkMode ? 'rgba(255,255,255,0.6)' : 'rgba(0,0,0,0.6)',
                    textAlign: 'center'
                  }}>
                    Attached: {(formik.values.file as File).name}
                  </div>
                )}
              </Form>
            )}
          </Formik>
        </InputArea>
      </MainContent>
    </PageContainer>
  );
};

const AsyncDashboard2 = (): JSX.Element => {
  return <AsyncDashboardInner />;
};

export default AsyncDashboard2;
