import React, { useEffect, useRef } from 'react';
import { themedColor } from '../../theme';
import { Animated, Easing, Linking, Pressable, Text, View } from 'react-native';
import { Check, ChevronLeft, Copy, MoreHorizontal, Reply, Share2 } from 'lucide-react-native';

import { AgentSphereIcon } from '../branding/AgentSphereIcon';
import { getToolTypeIcon, type ThinkingPhase } from './agentPhase';
import { ConnectorBrandIcon, hasConnectorBrandIcon } from '../connectors/connectorBrandIcons';
import { ShimmerText } from '../../components/ShimmerText';
import { conversationStyles } from './conversationStyles';
import { MarkdownText } from './MarkdownText';
import { useAgentBi } from '../../analytics/mixpanelContext';
import { useClipboard } from '../../runtime/useClipboard';
import { getFileName, getMessageFiles, getMessageToolCalls, getReplyTo, getRequestedConnectors, getTextDirection, messageHasReplyableContent } from './messageUtils';
import { fileTypeFromUrl, type FileType } from '../attachments/mediaUtils';
import { isSafeUrl } from './markdownTextUtils';
import { FileAttachment } from '../attachments/FileAttachment';
import { ImageGallery, type GalleryImage } from '../../components/ImageGallery';
import { ToolCallSummary } from '../tools/ToolCallSummary';
import type {
  SuperagentAgent,
  SuperagentConnector,
  SuperagentConnectorActionInput,
  SuperagentConversation,
  SuperagentEditorTab,
  SuperagentMarkdownRenderer,
  SuperagentMessage,
  SuperagentToolRenderers,
} from '../../types';

export function ConversationHeader({
  agent,
  onBack,
  onOpenMenu,
  onOpenShare,
  onRenameRequest,
}: {
  activeTab: SuperagentEditorTab;
  agent: SuperagentAgent;
  onBack: () => void;
  onOpenMenu: () => void;
  onOpenShare?: () => void;
  onRenameRequest?: () => void;
}) {
  return (
    <View style={conversationStyles.header}>
      <Pressable accessibilityLabel="Back" accessibilityRole="button" onPress={onBack} style={conversationStyles.headerIconButton}>
        <ChevronLeft color={themedColor('#F4F4F5')} size={28} strokeWidth={2.1} />
      </Pressable>
      <Pressable
        accessibilityHint="Long press to rename this Superagent"
        accessibilityLabel="Superagent name"
        accessibilityRole="button"
        delayLongPress={320}
        disabled={!onRenameRequest}
        onLongPress={onRenameRequest}
        style={conversationStyles.headerTitleWrap}
      >
        <AgentSphereIcon agent={agent} size="header" />
        <View style={conversationStyles.headerTitleTextWrap}>
          <Text numberOfLines={1} style={conversationStyles.headerTitle}>{agent.name || 'Superagent'}</Text>
        </View>
      </Pressable>
      <View style={conversationStyles.headerActions}>
        {onOpenShare ? (
          <Pressable accessibilityLabel="Share Superagent chat" accessibilityRole="button" onPress={onOpenShare} style={conversationStyles.headerShareButton}>
            <Share2 color={themedColor('#F4F4F5')} size={18} strokeWidth={2.4} />
          </Pressable>
        ) : null}
        <Pressable accessibilityLabel="Open Superagent menu" accessibilityRole="button" onPress={onOpenMenu} style={conversationStyles.headerMenuButton}>
          <MoreHorizontal color={themedColor('#F4F4F5')} size={22} strokeWidth={2.4} />
        </Pressable>
      </View>
    </View>
  );
}

export function MessageBubble({
  agent,
  availableConnectors,
  conversationId,
  currentUserAvatarUrl,
  isLastAssistantMessage,
  message,
  onConnectConnector,
  onReplyMessage,
  renderMarkdown,
  showAvatar,
  showDebugPayloads,
  showTimestamp,
  submitToolCallInput,
  toolRenderers,
}: {
  agent: SuperagentAgent;
  availableConnectors?: SuperagentConnector[];
  conversationId: string | null;
  currentUserAvatarUrl?: string | null;
  isLastAssistantMessage: boolean;
  message: SuperagentMessage;
  onConnectConnector?: (input: SuperagentConnectorActionInput) => Promise<boolean | string | void> | boolean | string | void;
  onReplyMessage?: (message: SuperagentMessage) => void;
  renderMarkdown?: SuperagentMarkdownRenderer;
  showAvatar: boolean;
  showDebugPayloads?: boolean;
  showTimestamp: boolean;
  submitToolCallInput?: (
    toolCallId: string,
    approve: boolean,
    extraUserInput?: unknown,
    originRequestId?: string,
  ) => Promise<SuperagentConversation | null>;
  toolRenderers?: SuperagentToolRenderers;
}) {
  const bi = useAgentBi();
  const isUser = message.role === 'user';
  const files = getMessageFiles(message);
  const replyTo = getReplyTo(message);
  const toolCalls = getMessageToolCalls(message);
  const requestedConnectors = isUser
    ? getRequestedConnectors(message).filter(hasConnectorBrandIcon)
    : [];
  const senderName = isUser ? 'You' : agent.name || 'Superagent';
  const usesScrollableMarkdown = hasScrollableMarkdown(message.content);
  // Tool-step content and scrollable markdown need full width: the shrink-to-fit
  // bubble otherwise collapses around its shrinkable rows and crushes them.
  const breaksOutOfBubble = usesScrollableMarkdown || toolCalls.length > 0;
  const bubbleStyle = [
    conversationStyles.messageBubble,
    isUser ? conversationStyles.userBubble : conversationStyles.assistantBubble,
    !showTimestamp && (isUser ? conversationStyles.userBubbleStacked : conversationStyles.assistantBubbleStacked),
  ];
  const { canCopy: clipboardReady, copied, copy } = useClipboard();
  // Reply and copy are offered as visible icons under the agent's latest message
  // only (user messages don't get the affordance; earlier ones would just repeat it).
  const hasFooterActionContent = !isUser && isLastAssistantMessage && messageHasReplyableContent(message);
  const canReply = Boolean(onReplyMessage) && hasFooterActionContent;
  const canCopy = clipboardReady && hasFooterActionContent;
  const handleReply = () => {
    void bi.trackEditor('Message Reply');
    onReplyMessage?.(message);
  };
  const handleCopy = () => {
    void bi.trackEditor('Message Copy');
    void copy(message.content ?? '');
  };
  // Only the text/reply/file content rides inside the bubble. Tool-call widgets
  // (automation cards, artifacts, …) and the reply/timestamp footer render as
  // siblings instead (see toolCallContent / footerContent below).
  const textContent = (
    <>
      {requestedConnectors.length > 0 ? <ConnectorBadges connectorIds={requestedConnectors} /> : null}
      {replyTo ? <ReplyQuote content={replyTo.content} /> : null}
      {files.length > 0 ? <FileChips files={files} /> : null}
      {message.content ? (
        <MarkdownText
          content={message.content}
          isUser={isUser}
          message={message}
          renderMarkdown={renderMarkdown}
        />
      ) : null}
    </>
  );
  const toolCallContent = toolCalls.length > 0 ? (
    <ToolCallSummary
      agent={agent}
      availableConnectors={availableConnectors}
      conversationId={conversationId}
      isLastAssistantMessage={isLastAssistantMessage}
      message={message}
      onConnectConnector={onConnectConnector}
      showDebugPayloads={showDebugPayloads}
      submitToolCallInput={submitToolCallInput}
      toolCalls={toolCalls}
      toolRenderers={toolRenderers}
    />
  ) : null;
  // Reply icon and timestamp share one row, split by a thin separator (either can
  // be absent — stacked messages show no timestamp, user messages get no reply).
  const timeText = showTimestamp && message.createdAt ? (
    <Text style={conversationStyles.messageTime}>{formatTime(message.createdAt)}</Text>
  ) : null;
  const hasActions = canReply || canCopy;
  const footerContent = (hasActions || timeText) ? (
    <View style={conversationStyles.messageFooterRow}>
      {canReply ? (
        <Pressable
          accessibilityLabel="Reply"
          accessibilityRole="button"
          hitSlop={8}
          onPress={handleReply}
          style={conversationStyles.messageReplyButton}
        >
          <Reply color={themedColor('#A1A1AA')} size={16} strokeWidth={2.2} />
        </Pressable>
      ) : null}
      {canCopy ? (
        <Pressable
          accessibilityLabel={copied ? 'Copied' : 'Copy message'}
          accessibilityRole="button"
          hitSlop={8}
          onPress={handleCopy}
          style={conversationStyles.messageReplyButton}
        >
          {copied ? (
            <Check color={themedColor('#F4F4F5')} size={16} strokeWidth={2.4} />
          ) : (
            <Copy color={themedColor('#A1A1AA')} size={15} strokeWidth={2.2} />
          )}
        </Pressable>
      ) : null}
      {hasActions && timeText ? <View style={conversationStyles.messageFooterSeparator} /> : null}
      {timeText}
    </View>
  ) : null;

  return (
    <View style={[
      conversationStyles.messageGroup,
      isUser ? conversationStyles.userMessageGroup : conversationStyles.assistantMessageGroup,
      showAvatar && conversationStyles.messageGroupWithAvatar,
    ]}>
      {showAvatar ? (
        <View style={[
          conversationStyles.identityRow,
          isUser ? conversationStyles.userIdentityRow : conversationStyles.assistantIdentityRow,
        ]}>
          <AgentSphereIcon agent={agent} isUser={isUser} userAvatarUrl={isUser ? currentUserAvatarUrl : undefined} />
          <Text numberOfLines={1} style={[
            conversationStyles.identityName,
            isUser ? conversationStyles.userIdentityName : conversationStyles.assistantIdentityName,
          ]}>
            {senderName}
          </Text>
        </View>
      ) : null}
      <View style={[
        conversationStyles.messageBubbleWrap,
        breaksOutOfBubble && conversationStyles.scrollableMessageBubbleWrap,
        isUser ? conversationStyles.userMessageBubbleWrap : conversationStyles.assistantMessageBubbleWrap,
      ]}>
        {/* Tool-call widgets and the reply/timestamp footer render as siblings
            outside the bubble (see toolCallContent / footerContent). The full-bleed
            width breakout is applied on the wrap above via breaksOutOfBubble. */}
        <View style={bubbleStyle}>
          {textContent}
        </View>
        {toolCallContent}
        {footerContent}
      </View>
    </View>
  );
}

function hasScrollableMarkdown(content?: string) {
  if (!content) return false;
  // Backtick and tilde fences both render as a horizontal ScrollView, so the
  // bubble breaks out to full width for either (see breaksOutOfBubble).
  if (/```|~~~/.test(content)) return true;

  const lines = content.replace(/\r\n/g, '\n').split('\n');
  return lines.some((line, index) => (
    line.includes('|') &&
    Boolean(lines[index + 1]?.match(/^\s*\|?\s*:?-{3,}:?\s*(?:\|\s*:?-{3,}:?\s*)+\|?\s*$/))
  ));
}

function ReplyQuote({ content }: { content: string }) {
  // Align the quoted text by its own language so it matches the reply body below
  // it (see MarkdownText), rather than flipping against a Hebrew/Arabic reply.
  const direction = getTextDirection(content);
  return (
    <View style={conversationStyles.replyQuote}>
      <Text
        numberOfLines={2}
        style={[conversationStyles.replyQuoteText, { textAlign: direction === 'rtl' ? 'right' : 'left', writingDirection: direction }]}
      >
        {content}
      </Text>
    </View>
  );
}

function ConnectorBadges({ connectorIds }: { connectorIds: string[] }) {
  return (
    <View style={conversationStyles.connectorBadgesWrap}>
      {connectorIds.map((id) => (
        <View key={id} style={conversationStyles.connectorBadge}>
          <ConnectorBrandIcon connectorId={id} size={20} />
        </View>
      ))}
    </View>
  );
}

function FileChips({ files }: { files: string[] }) {
  const bi = useAgentBi();
  // Images render through the shared ImageGallery (inline / grid + lightbox);
  // every other file type stays a FileAttachment chip.
  const images: GalleryImage[] = [];
  const others: { url: string; fileType: FileType }[] = [];
  for (const file of files) {
    const fileType = fileTypeFromUrl(file);
    // Only safe http(s) images go to the gallery, which fetches them via <Image>;
    // anything else falls through to a non-opening chip (its onPress is guarded too).
    if (fileType === 'image' && isSafeUrl(file)) images.push({ key: file, url: file, fileName: getFileName(file) });
    else others.push({ url: file, fileType });
  }

  return (
    <>
      {images.length > 0 ? (
        <View style={conversationStyles.attachmentImagesWrap}>
          <ImageGallery
            images={images}
            onOpen={() => void bi.trackEditor('Chat File Preview Open', { file_type: 'image', source: 'inline_image' })}
          />
        </View>
      ) : null}
      {others.length > 0 ? (
        <View style={conversationStyles.fileChipsWrap}>
          {others.map((file) => (
            <FileAttachment
              key={file.url}
              variant="file"
              fileType={file.fileType}
              name={getFileName(file.url)}
              src={file.url}
              onPress={() => {
                if (!isSafeUrl(file.url)) return;
                void bi.trackEditor('Chat File Preview Open', { file_type: file.fileType, source: 'attachment' });
                void Linking.openURL(file.url).catch(() => {});
              }}
            />
          ))}
        </View>
      ) : null}
    </>
  );
}

export function TypingIndicator({ agent, phase }: { agent?: SuperagentAgent; phase?: ThinkingPhase | null }) {
  // Gentle "breathing" on the icon — the native analog of the web
  // `AgentLiveAvatar` thinking orb (round, living icon) next to shimmer text.
  const breathe = useRef(new Animated.Value(0)).current;

  useEffect(() => {
    const animation = Animated.loop(
      Animated.sequence([
        Animated.timing(breathe, {
          duration: 900,
          easing: Easing.inOut(Easing.quad),
          toValue: 1,
          useNativeDriver: true,
        }),
        Animated.timing(breathe, {
          duration: 900,
          easing: Easing.inOut(Easing.quad),
          toValue: 0,
          useNativeDriver: true,
        }),
      ]),
    );

    animation.start();
    return () => animation.stop();
  }, [breathe]);

  const scale = breathe.interpolate({
    inputRange: [0, 1],
    outputRange: [0.96, 1.06],
  });
  const opacity = breathe.interpolate({
    inputRange: [0, 1],
    outputRange: [0.8, 1],
  });

  // Tool-driven phase → show the tool's group label + icon (e.g. "Editing your
  // code..." with a code glyph). No active tool → fall back to the agent orb +
  // "Thinking...".
  const PhaseIcon = phase ? getToolTypeIcon(phase.toolName) : null;
  const label = phase?.label ?? 'Thinking...';

  return (
    <View style={conversationStyles.typingWrap}>
      <Animated.View style={{ opacity, transform: [{ scale }] }}>
        {PhaseIcon ? (
          <View style={conversationStyles.thinkingPhaseIcon}>
            <PhaseIcon color={themedColor('#A1A1AA')} size={16} strokeWidth={2.3} />
          </View>
        ) : (
          <AgentSphereIcon agent={agent} size="sm" />
        )}
      </Animated.View>
      <View style={conversationStyles.typingTextWrap}>
        <ShimmerText text={label} />
      </View>
    </View>
  );
}

function formatTime(value: string) {
  const date = new Date(value);
  if (Number.isNaN(date.getTime())) return '';

  return date.toLocaleTimeString([], {
    hour: '2-digit',
    minute: '2-digit',
  });
}
