import React, { useCallback, useMemo } from 'react';
import Markdown from 'markdown-to-jsx';
import { Link } from 'react-router-dom';
import styled, { keyframes } from 'styled-components';
import { isRagFeatureNotAllowed, parseChatErrorPayload } from '../../utils/chatErrors';
import type { ActivityHistoryEntry, Citation } from '../../utils/wsFrames';
import type { PromptRating } from '../../utils/promptFeedback';
import CustomPreBlock from '../CustomPreBlock/CustomPreBlock';
import MessageActions from '../MessageActions/MessageActions';
import ActivityIndicator from '../ActivityIndicator/ActivityIndicator';

const fadeIn = keyframes`
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: translateY(0); }
`;

const MessageContainer = styled.div<{ $isUser: boolean }>`
  display: flex;
  flex-direction: column;
  align-items: ${(props) => (props.$isUser ? 'flex-end' : 'flex-start')};
  margin-bottom: 1.5rem;
  width: 100%;
  animation: ${fadeIn} 0.3s ease-out;

  &:hover [data-message-actions],
  &:focus-within [data-message-actions] {
    opacity: 1 !important;
    pointer-events: auto !important;
  }
`;

const Bubble = styled.div<{ $isUser: boolean }>`
  max-width: 80%;
  min-width: 0;
  overflow-wrap: anywhere;
  padding: 1rem 1.5rem;
  border-radius: 1.2rem;
  background: ${(props) =>
    props.$isUser
      ? `linear-gradient(135deg, ${props.theme.main} 0%, ${props.theme.focus} 100%)`
      : props.theme.darkMode
        ? 'rgba(255, 255, 255, 0.1)'
        : 'rgba(0, 0, 0, 0.06)'};
  color: ${(props) =>
    props.$isUser || props.theme.darkMode ? '#fff' : props.theme.colors.text};
  backdrop-filter: blur(10px);
  border: 1px solid ${(props) =>
    props.$isUser
      ? props.theme.darkMode
        ? 'rgba(255, 255, 255, 0.1)'
        : 'rgba(0, 0, 0, 0.1)'
      : props.theme.darkMode
        ? 'rgba(255, 255, 255, 0.1)'
        : 'rgba(0, 0, 0, 0.08)'};
  box-shadow: ${(props) =>
    props.$isUser || props.theme.darkMode
      ? '0 4px 15px rgba(0, 0, 0, 0.2)'
      : '0 2px 10px rgba(0, 0, 0, 0.08)'};
  font-size: 1rem;
  line-height: 1.6;
  border-bottom-right-radius: ${(props) => (props.$isUser ? '0.2rem' : '1.2rem')};
  border-bottom-left-radius: ${(props) => (props.$isUser ? '1.2rem' : '0.2rem')};

  & pre {
    background: ${(props) =>
      props.$isUser || props.theme.darkMode
        ? 'rgba(0, 0, 0, 0.3)'
        : 'rgba(0, 0, 0, 0.06)'};
    padding: 1rem;
    border-radius: 0.5rem;
    overflow-x: auto;
    max-width: 100%;
  }

  & code {
    font-family: 'Fira Code', monospace;
    font-size: 0.9em;
  }

  & a {
    color: ${(props) =>
      props.$isUser || props.theme.darkMode ? '#a0c4ff' : props.theme.main};
    text-decoration: underline;
  }

  & img,
  & table {
    max-width: 100%;
  }

  @media (max-width: 768px) {
    max-width: 92%;
    padding: 0.75rem 1rem;
    font-size: 0.95rem;
  }
`;

const UpgradeNotice = styled.div`
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.65rem;
`;

const UpgradeLink = styled(Link)`
  background: ${(props) => props.theme.main};
  color: #fff;
  text-decoration: none;
  font-weight: 600;
  font-size: 0.85rem;
  padding: 0.5rem 1rem;
  border-radius: 0.5rem;

  &:hover {
    opacity: 0.9;
  }
`;

const CitationButton = styled.button`
  display: inline;
  margin: 0 0.1rem;
  padding: 0 0.25rem;
  border: none;
  border-radius: 0.25rem;
  background: ${({ theme }) =>
    theme.darkMode ? 'rgba(160, 196, 255, 0.2)' : 'rgba(51, 102, 153, 0.12)'};
  color: ${({ theme }) => (theme.darkMode ? '#a0c4ff' : theme.main)};
  font: inherit;
  font-size: 0.85em;
  font-weight: 600;
  cursor: pointer;
  vertical-align: baseline;

  &:hover,
  &:focus-visible {
    outline: 2px solid ${({ theme }) => theme.main};
    outline-offset: 1px;
  }
`;

/** Linkify [n] / [1, 2] outside fenced code blocks. */
export function injectCitationMarkers(markdown: string): string {
  const parts = markdown.split(/(```[\s\S]*?```)/g);
  return parts
    .map((part) => {
      if (part.startsWith('```')) return part;
      return part.replace(
        /\[(\d+(?:\s*,\s*\d+)*)\]/g,
        (_match, nums: string) =>
          `<citation indices="${nums.replace(/\s+/g, '')}"></citation>`,
      );
    })
    .join('');
}

type ConversationDetailCardProps = {
  message: string;
  user_created: boolean;
  promptId?: number;
  citations?: Citation[];
  isStreaming?: boolean;
  isLast?: boolean;
  conversationTitle?: string;
  createdTimestamp?: Date | string | null;
  initialRating?: PromptRating | null;
  onRatingChange?: (rating: PromptRating | null) => void;
  /** Live activity status for the empty-message (streaming placeholder) bubble (#96). */
  activityStage?: string | null;
  activityLabel?: string | null;
  activityDetail?: string | null;
  activityHistory?: ActivityHistoryEntry[];
  activityInterrupted?: boolean;
};

const MyPlot = ({ format, image }: { format: string; image: string }) => {
  const imageSrc = `data:image/${format};base64,${image}`;
  return (
    <img
      src={imageSrc}
      style={{ maxWidth: '100%', height: 'auto', borderRadius: '8px', marginTop: '10px' }}
      alt="plot"
    />
  );
};

const MyError = ({ content }: { content: string }) => {
  return (
    <span style={{ color: '#ff6b6b', fontWeight: 'bold', display: 'block', marginTop: '0.5rem' }}>
      Error: {content}
    </span>
  );
};

const ConversationDetailCard = ({
  message,
  user_created,
  promptId,
  citations = [],
  isStreaming = false,
  isLast = false,
  conversationTitle = 'conversation',
  createdTimestamp = null,
  initialRating = null,
  onRatingChange,
  activityStage = null,
  activityLabel = null,
  activityDetail = null,
  activityHistory = [],
  activityInterrupted = false,
}: ConversationDetailCardProps): JSX.Element => {
  const CitationMark = useCallback(
    ({ indices }: { indices?: string }) => {
      const list = (indices || '')
        .split(',')
        .map((n) => Number(n.trim()))
        .filter((n) => !Number.isNaN(n));
      if (!list.length) return null;
      return (
        <>
          {list.map((index, i) => {
            const citation = citations.find((c) => c.index === index);
            return (
              <CitationButton
                key={`${index}-${i}`}
                type="button"
                aria-label={`Source ${index}`}
                title={citation?.title || `Source ${index}`}
                onClick={() => {
                  if (citation?.url) {
                    window.open(citation.url, '_blank', 'noopener,noreferrer');
                  }
                }}
              >
                [{index}]
              </CitationButton>
            );
          })}
        </>
      );
    },
    [citations],
  );

  const displayMarkdown = useMemo(() => {
    if (!message) return message;
    if (!citations.length || user_created) return message;
    return injectCitationMarkers(message);
  }, [message, citations, user_created]);

  if (message.length === 0) {
    return (
      <MessageContainer $isUser={false}>
        <Bubble $isUser={false}>
          <ActivityIndicator
            stage={activityStage}
            label={activityLabel}
            detail={activityDetail}
            history={activityHistory}
            interrupted={activityInterrupted}
          />
        </Bubble>
      </MessageContainer>
    );
  }

  const errorPayload = parseChatErrorPayload(message);
  if (errorPayload && isRagFeatureNotAllowed(errorPayload)) {
    return (
      <MessageContainer $isUser={false}>
        <Bubble $isUser={false}>
          <UpgradeNotice>
            <span>
              {errorPayload.content ||
                "Document search (RAG) isn't included in your current plan."}
            </span>
            <UpgradeLink to="/account/">Upgrade your plan</UpgradeLink>
          </UpgradeNotice>
        </Bubble>
      </MessageContainer>
    );
  }

  let contentToAdd = displayMarkdown;
  let rawForCopy = message;
  try {
    const parsedMessage = JSON.parse(message);
    if (
      parsedMessage &&
      typeof parsedMessage === 'object' &&
      parsedMessage.type
    ) {
      switch (parsedMessage.type) {
        case 'text':
          rawForCopy = parsedMessage.content;
          contentToAdd =
            citations.length && !user_created
              ? injectCitationMarkers(parsedMessage.content)
              : parsedMessage.content;
          break;
        case 'plot':
          contentToAdd = `<plot format="${parsedMessage.format}" image="${parsedMessage.image}"></plot>`;
          rawForCopy = message;
          break;
        case 'error':
          contentToAdd = `<error content="${parsedMessage.content}"></error>`;
          rawForCopy = message;
          break;
        default:
          break;
      }
    }
  } catch {
    /* plain markdown */
  }

  return (
    <MessageContainer $isUser={user_created}>
      <Bubble $isUser={user_created}>
        <Markdown
          className="display-linebreak"
          style={{ whiteSpace: 'pre-line' }}
          options={{
            overrides: {
              plot: {
                component: MyPlot,
              },
              error: {
                component: MyError,
              },
              pre: {
                component: CustomPreBlock,
              },
              citation: {
                component: CitationMark,
              },
            },
          }}
        >
          {contentToAdd}
        </Markdown>
      </Bubble>

      <MessageActions
        promptId={promptId}
        rawMarkdown={rawForCopy}
        userCreated={user_created}
        isStreaming={isStreaming}
        isLast={isLast}
        conversationTitle={conversationTitle}
        createdTimestamp={createdTimestamp}
        citations={citations}
        initialRating={initialRating}
        onRatingChange={onRatingChange}
      />
    </MessageContainer>
  );
};

export default ConversationDetailCard;
