import React, {
  createContext,
  PropsWithChildren,
  useCallback,
  useContext,
  useEffect,
  useMemo,
  useState,
} from 'react';
import {View, ViewProps, ViewStyle} from 'react-native';
import {useTranslation} from 'react-i18next';
import {Pressable} from 'react-native-gesture-handler';
import {IconArrowForwardUp, IconPinned} from '@tabler/icons-react-native';
import {useNavigation} from '@react-navigation/native';
import Animated, {
  useSharedValue,
  useAnimatedStyle,
  withTiming,
  withSequence,
  withDelay,
  runOnJS,
  interpolateColor,
} from 'react-native-reanimated';
import {EaseView} from 'react-native-ease';
import {Avatar, Image, Typography} from '@components';
import {Navigation} from '@interfaces/navigation';
import {
  BlockMessageReplied,
  BlockType,
  ChatUserStatus,
  DeliverStatus,
  ExtendedMessage,
  ExtendedMessageType,
  MessageStatus,
} from '@modules/chats/interfaces';
import {
  CHATS_SPACING,
  MESSAGE_SENDER_AVATAR_SIZE,
  getChatImage,
  getDateFromULID,
  getEstimatedMessageSize,
  getMessageMargins,
  getPreviewAsset,
  getTextWithHighlights,
} from '@modules/chats/utils';
import {IMAGE_SIZE_TINY_PX} from '@modules/chats/constants';
import {useGroups} from '@modules/chats/store/zustand/useGroups';
import {usePrefixIconAndText} from '@modules/chats/hooks';
import {MessageStatusDisplayer} from '@modules/chats/components/MessageStatusDisplayer';
import {useIsPinned} from '@modules/chats/store';
import {IconPlayVideo} from '@modules/chats/components';
import {usersMapper} from '@modules/chats/instances/UsersMapper';
import IconAiSummary from '@modules/chats/assets/icons/IconAiSummary.svg';
import {useAiMessageDialog} from '@modules/chats/screens/ChatMessages/components/DialogAIMessageProvider';
import {ICON_SIZES, useTheme} from '@shared/theme';
import {getCompleteName, getTime} from '@shared/utils';
import {Screens, windowDimensions} from '@shared/constants';
import {useFeatureFlag} from '@stores/featureFlags';

import {styles} from './styles';

const MessageItemContext = createContext<{
  isChannel: boolean;
  channel: string;
  username: Nullable<string>;
  showSenderInfo: boolean;
  avatar: Nullable<string>;
  isSentByCurrentUser: boolean;
  bubbleHeight: number;
  edited: boolean;
  ts: string;
  status: Nullable<MessageStatus>;
  isTargetMessage: boolean;
  extendedType: ExtendedMessageType;
  hasReply: boolean;
  deliver_status?: DeliverStatus;
  userId?: Nullable<number>;
  isAi?: boolean;
}>({
  isChannel: false,
  channel: '',
  username: null,
  showSenderInfo: false,
  avatar: null,
  isSentByCurrentUser: false,
  bubbleHeight: 0,
  edited: false,
  ts: '',
  status: null,
  isTargetMessage: false,
  extendedType: ExtendedMessageType.Default,
  hasReply: false,
  deliver_status: {
    read_at: null,
    received_at: null,
  },
  userId: null,
  isAi: false,
});

const useMessageItemContext = () => useContext(MessageItemContext);

export function MessageItem({
  item,
  children,
  ...props
}: ViewProps & {item: ExtendedMessage}) {
  const targetMessageId = useGroups(state => state.targetMessageId);

  const isTargetMessage = targetMessageId === item.ts;

  const providerValue = useMemo(() => {
    const isSentByCurrentUser =
      'isSentByCurrentUser' in item ? item.isSentByCurrentUser : false;
    const avatar =
      'sender_profile_picture' in item.data
        ? item.data.sender_profile_picture || null
        : null;
    const username = 'username' in item.data ? item.data.username : null;
    const edited = 'edited' in item.data ? item.data.edited || false : false;
    const ts = 'ts' in item.data ? item.data.ts : '';
    const status = 'status' in item.data ? item.data.status || null : null;
    const showSenderInfo =
      'showSenderInfo' in item ? item.showSenderInfo : false;
    const extendedType =
      'extendedType' in item ? item.extendedType : ExtendedMessageType.Default;
    const hasReply =
      'blocks' in item.data
        ? !!item.data.blocks?.some(
            block => block.type === BlockType.MessageReplied,
          )
        : false;
    const height = getEstimatedMessageSize(item, 0);
    const {marginTop, marginBottom} = getMessageMargins(item);
    const deliver_status =
      'deliver_status' in item.data ? item.data.deliver_status : undefined;
    const isError =
      'status' in item.data ? item.data.status === MessageStatus.Error : false;
    const userId = 'user' in item.data ? Number(item.data.user) : null;
    const channel = 'channel' in item.data ? (item.data.channel as string) : '';
    return {
      isChannel: item.isChannel,
      channel,
      username,
      showSenderInfo,
      avatar,
      isSentByCurrentUser,
      bubbleHeight:
        height -
        marginTop -
        marginBottom -
        (isError ? CHATS_SPACING.errorMessageHeight + CHATS_SPACING.gap : 0),
      edited,
      ts,
      status,
      height,
      marginTop,
      isTargetMessage,
      extendedType,
      hasReply,
      marginBottom,
      deliver_status,
      userId,
      isAi: 'generated_by' in item.data ? !!item.data.generated_by : false,
    };
  }, [item, isTargetMessage]);

  const containerStyle: ViewStyle = {
    marginTop: providerValue.marginTop,
    marginBottom: providerValue.marginBottom,
  };

  return (
    <MessageItemContext.Provider value={providerValue}>
      <View {...props} style={[styles.container, containerStyle]}>
        {children}
      </View>
    </MessageItemContext.Provider>
  );
}

function MessageAvatar() {
  const {
    isSentByCurrentUser,
    isChannel,
    avatar,
    username,
    showSenderInfo,
    userId,
  } = useMessageItemContext();
  const keepWidth = !isSentByCurrentUser && isChannel;
  const navigation =
    useNavigation<Navigation<Screens.CHAT_MESSAGES>['navigation']>();
  // Prefer the live `usersMapper` value over the stale `sender_profile_picture`
  // snapshot, since the message may have been mapped before `usersMapper.init()`.
  const mappedUser = userId ? usersMapper.get(userId) : undefined;
  const resolvedAvatar = mappedUser?.profile_picture || avatar || null;
  const avatarUrl = getChatImage.avatar(resolvedAvatar, {
    enabled: keepWidth && showSenderInfo,
    avatarSize: MESSAGE_SENDER_AVATAR_SIZE,
  });

  const onAvatarPress = useCallback(() => {
    const user = userId ? usersMapper.get(userId) : undefined;
    const userUnavailable =
      !user ||
      user?.status === ChatUserStatus.Deactivated ||
      !!user?.deleted_at;
    if (userUnavailable) {
      return;
    }
    navigation.navigate(Screens.PROFILE, {userId: userId!});
  }, [userId, navigation]);
  return keepWidth ? (
    <View style={styles.avatarContainer}>
      {showSenderInfo && (
        <Pressable onPress={onAvatarPress}>
          <Avatar
            url={avatarUrl}
            name={username || undefined}
            size="sm"
            variant="primary"
            enableCache
            loaderType="skeleton"
            withAuthHeader
            priority="high"
            recyclingKey={userId ? `user-${userId}` : undefined}
          />
        </Pressable>
      )}
    </View>
  ) : null;
}

function ReplyBubble({
  replyBlock,
  scrollToMessage,
}: {
  replyBlock: BlockMessageReplied;
  scrollToMessage?: (ts: string) => void;
}) {
  const {theme, iconSizes} = useTheme();
  const {isSentByCurrentUser} = useMessageItemContext();
  const replyBubbleColor = isSentByCurrentUser
    ? theme.brand[100]
    : theme.background.layout.default;
  const {PrefixIcon, defaultText} = usePrefixIconAndText({
    assets: replyBlock.assets,
  });
  // No messageId/channel — getPreviewAsset reads everything from the asset.
  const {showPreview, attachment, hasAttachment} = getPreviewAsset({
    assets: replyBlock.assets,
  });
  const hasMentions = replyBlock.highlighted_spans?.length;
  // formattedText is undefined initially to avoid showing the default text
  const [formattedText, setFormattedText] = useState<string | undefined>(
    hasMentions ? undefined : replyBlock.text || defaultText,
  );

  const senderName = useMemo(
    () =>
      getCompleteName({
        firstName:
          replyBlock.sender_membership?.user.hu_data.member.first_name || '',
        lastName:
          replyBlock.sender_membership?.user.hu_data.member.last_name || '',
      }),
    [replyBlock.sender_membership],
  );

  const onPress = useCallback(() => {
    scrollToMessage?.(replyBlock.message_replied_id);
  }, [scrollToMessage, replyBlock.message_replied_id]);

  /**
   * Get the formatted text only if the message has mentions
   */
  useEffect(() => {
    if (hasMentions) {
      getTextWithHighlights({
        message: replyBlock.text,
        highlightedSpans: replyBlock.highlighted_spans!,
      }).then(setFormattedText);
    }
  }, [hasMentions, replyBlock]);

  return (
    <Pressable onPress={onPress}>
      <View
        style={[
          styles.replyMessageContainer,
          !!hasAttachment && styles.replyWithAttachment,
          {backgroundColor: replyBubbleColor},
        ]}>
        <View style={styles.flex}>
          <Typography
            variant="xxs"
            weight="semiBold"
            color={theme.text.neutral.default}
            numberOfLines={1}>
            {senderName}
          </Typography>
          <View style={styles.replyMessageIconContainer}>
            {PrefixIcon && (
              <PrefixIcon
                size={iconSizes.x3}
                color={theme.text.neutral.lighter}
              />
            )}
            {!!formattedText && (
              <Typography
                variant="xxs"
                color={theme.text.neutral.default}
                numberOfLines={1}
                style={styles.flex}>
                {formattedText}
              </Typography>
            )}
          </View>
        </View>
        {showPreview && (
          <View style={styles.previewContainer}>
            <Image
              // Both image and video reply previews need auth headers —
              // the URL points at our authenticated asset endpoints. The
              // Image wrapper internally only injects for http URIs.
              withAuthHeader
              enableCache
              timeout={15_000}
              source={{
                uri: getChatImage.addSize(
                  attachment.isVideo ? attachment.thumbnailUrl : attachment.url,
                  IMAGE_SIZE_TINY_PX,
                ),
              }}
              style={[
                styles.replyMessageImage,
                {backgroundColor: theme.text.neutral.default},
              ]}
            />
            {attachment.isVideo && <IconPlayVideo size="sm" />}
          </View>
        )}
      </View>
    </Pressable>
  );
}

function ForwardedLabel() {
  const {t} = useTranslation();
  const {theme} = useTheme();

  return (
    <View style={styles.fowardLabelContainer}>
      <IconArrowForwardUp
        size={ICON_SIZES.x4}
        color={theme.text.neutral.disabled}
      />
      <Typography
        variant="xxxs"
        color={theme.text.neutral.lighter}
        fontStyle="italic">
        {t('chat.messages.forwarded')}
      </Typography>
    </View>
  );
}

const HIGHLIGHT_FADE_IN_DURATION = 200;
const HIGHLIGHT_VISIBLE_DURATION = 850;
const HIGHLIGHT_FADE_OUT_DURATION = 300;

interface MessageBubbleHighlightWrapperProps extends PropsWithChildren {
  baseBubbleStyle: ViewStyle[];
  normalBackgroundColor: string;
  targetBackgroundColor: string;
}

function MessageBubbleHighlightWrapper({
  children,
  baseBubbleStyle,
  normalBackgroundColor,
  targetBackgroundColor,
}: MessageBubbleHighlightWrapperProps) {
  const setTargetMessageId = useGroups(state => state.setTargetMessageId);

  const highlightProgress = useSharedValue(0);

  const onHighlightEnd = useCallback(() => {
    setTargetMessageId(null);
  }, [setTargetMessageId]);

  useEffect(() => {
    highlightProgress.value = withSequence(
      withTiming(1, {duration: HIGHLIGHT_FADE_IN_DURATION}),
      withDelay(
        HIGHLIGHT_VISIBLE_DURATION,
        withTiming(0, {duration: HIGHLIGHT_FADE_OUT_DURATION}, finished => {
          if (finished) {
            runOnJS(onHighlightEnd)();
          }
        }),
      ),
    );
  }, [highlightProgress, onHighlightEnd]);

  const animatedBubbleStyle = useAnimatedStyle(() => ({
    backgroundColor: interpolateColor(
      highlightProgress.value,
      [0, 1],
      [normalBackgroundColor, targetBackgroundColor],
    ),
  }));

  return (
    <Animated.View
      style={[...baseBubbleStyle, animatedBubbleStyle]}
      accessible={false}>
      {children}
    </Animated.View>
  );
}

function MessageBubble({
  children,
  isActiveLongPress,
}: PropsWithChildren<{
  isActiveLongPress?: boolean;
}>) {
  const {theme} = useTheme();
  const {
    isSentByCurrentUser,
    username,
    showSenderInfo,
    bubbleHeight,
    hasReply,
    isTargetMessage,
    userId,
  } = useMessageItemContext();
  const navigation =
    useNavigation<Navigation<Screens.CHAT_MESSAGES>['navigation']>();
  const normalBackgroundColor = isSentByCurrentUser
    ? theme.background.layout.brand
    : theme.background.layout.tertiary;
  const targetBackgroundColor = isSentByCurrentUser
    ? theme.border.neutral.brand
    : theme.action.background.brand.disabled;
  // Scale down the bubble on long press to indicate it's being pressed. Reduced scale when is a large bubble (half of the screen height)
  const longPressScale =
    bubbleHeight > windowDimensions.height / 2 ? 0.99 : 0.91;

  const bubbleStyles: ViewStyle = isSentByCurrentUser
    ? {
        borderColor: theme.border.neutral.brand,
        alignSelf: 'flex-end',
        borderTopRightRadius: 0,
        height: bubbleHeight,
      }
    : {
        borderColor: theme.border.neutral.default,
        alignSelf: 'flex-start',
        borderTopLeftRadius: 0,
        height: bubbleHeight,
      };

  const baseBubbleStyle: ViewStyle[] = [
    styles.bubble,
    bubbleStyles,
    {backgroundColor: normalBackgroundColor},
    hasReply ? styles.bubbleWithReply : {},
  ];

  const onPress = useCallback(() => {
    const user = userId ? usersMapper.get(userId) : undefined;
    const userUnavailable =
      !user ||
      user?.status === ChatUserStatus.Deactivated ||
      !!user?.deleted_at;
    if (userUnavailable) {
      return;
    }
    navigation.navigate(Screens.PROFILE, {userId: userId!});
  }, [userId, navigation]);

  const bubbleContent = (
    <>
      {showSenderInfo && (
        <Typography
          onPress={onPress}
          variant="xs"
          weight="semiBold"
          style={styles.username}
          allowFontScaling={false}>
          {username || ''}
        </Typography>
      )}
      {children}
    </>
  );

  if (isTargetMessage) {
    return (
      <MessageBubbleHighlightWrapper
        baseBubbleStyle={baseBubbleStyle}
        normalBackgroundColor={normalBackgroundColor}
        targetBackgroundColor={targetBackgroundColor}>
        {bubbleContent}
      </MessageBubbleHighlightWrapper>
    );
  }

  return (
    <EaseView
      animate={{
        scale: isActiveLongPress ? longPressScale : 1,
      }}
      transition={{
        type: 'timing',
        duration: 120,
      }}
      style={baseBubbleStyle}
      accessible={false}>
      {bubbleContent}
    </EaseView>
  );
}

function MessageFooter() {
  const {t} = useTranslation();
  const {theme, iconSizes} = useTheme();
  const {edited, ts, status, extendedType, deliver_status, channel, isAi} =
    useMessageItemContext();
  const isPinMessageEnabled = useFeatureFlag('CHATS_V2_PIN_MESSAGE');
  const isPinned = useIsPinned(channel, ts);
  const date = getTime(getDateFromULID(ts));
  const {showDialog} = useAiMessageDialog();

  return (
    <View style={styles.bubblefooter}>
      {isAi && (
        <Pressable onPress={showDialog}>
          <IconAiSummary color={theme.brand['500']} />
        </Pressable>
      )}
      {isPinMessageEnabled && isPinned && (
        <IconPinned size={iconSizes.x3} color={theme.text.neutral.lighter} />
      )}
      <Typography
        variant="xxs"
        color={theme.text.neutral.lighter}
        allowFontScaling={false}
        numberOfLines={1}>
        {edited ? (
          <Typography
            variant="xxs"
            color={theme.text.neutral.disabled}
            allowFontScaling={false}
            numberOfLines={1}
            fontStyle="italic">
            {`${t('chat.edit.edited')} `}
          </Typography>
        ) : null}
        {date}
      </Typography>
      {extendedType === ExtendedMessageType.Default && (
        <MessageStatusDisplayer
          status={status}
          deliverStatus={deliver_status}
        />
      )}
    </View>
  );
}

function ErrorMessage({onRetry}: {onRetry: () => void}) {
  const {t} = useTranslation();
  const {theme} = useTheme();
  const {status} = useMessageItemContext();
  const isError = status === MessageStatus.Error;

  return isError ? (
    <View style={styles.errorMessageContainer}>
      <Typography
        variant="xs"
        fontStyle="italic"
        color={theme.text.neutral.lighter}
        numberOfLines={1}
        adjustsFontSizeToFit>
        {t('chat.errors.no_send_message')}
      </Typography>
      <Pressable onPress={onRetry}>
        <Typography
          variant="xs"
          weight="semiBold"
          color={theme.text.neutral.brand}>
          {t('general.try_again')}
        </Typography>
      </Pressable>
    </View>
  ) : null;
}

function Row({children}: PropsWithChildren) {
  const {isSentByCurrentUser} = useMessageItemContext();

  return (
    <View
      style={[
        styles.rowContainer,
        isSentByCurrentUser && styles.rightContainer,
      ]}>
      {children}
    </View>
  );
}

MessageItem.Avatar = MessageAvatar;
MessageItem.Bubble = MessageBubble;
MessageItem.ReplyBubble = ReplyBubble;
MessageItem.Footer = MessageFooter;
MessageItem.ForwardedLabel = ForwardedLabel;
MessageItem.ErrorMessage = ErrorMessage;
MessageItem.Row = Row;
