import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
import {
  Alert,
  Keyboard,
  LayoutChangeEvent,
  Linking,
  NativeSyntheticEvent,
  View,
} from 'react-native';
import {useTranslation} from 'react-i18next';
import {GiphyMedia} from '@giphy/react-native-sdk';
import Animated from 'react-native-reanimated';
import * as Clipboard from 'expo-clipboard';
import {useIsFocused, useNavigation} from '@react-navigation/native';
import {Camera} from 'react-native-vision-camera';
import {useSafeAreaInsets} from 'react-native-safe-area-context';
import {snackbarSharedOffset, TextInputType, Typography} from '@components';
import {useGifSelector} from '@hooks/useGifSelector';
import {useEvent} from '@hooks/useEvent';
import {
  useMessageActions,
  usePostMessage,
  useUploadMedia,
  useKeyboardHandlerOnReactionsDialog,
} from '@modules/chats/hooks';
import {ChatInput, DialogMissingPermission} from '@modules/chats/components';
import {CHAT_MAX_ATTACHMENTS} from '@modules/chats/constants';
import {ConversationDrafts} from '@modules/chats/instances/ConversationDrafts';
import {MediaEntity, MediaType} from '@modules/chats/interfaces';
import {
  useHasFirstPageLoadError,
  useIsChannel,
  useMessagesSearch,
  useShallowConversation,
} from '@modules/chats/store';
import {
  useInputValue,
  useMentions,
  useMessageActionsContext,
  useMessagesListRef,
  useMessagesListMethodsRef,
  useAudioRecordingAnimated,
  useAudioRecording,
  useAudioRecordingLongPressListener,
  useAiSummary,
} from '@modules/chats/contexts';
import {
  getChatAvatar,
  mapHighlightedSpansToTaggedUsers,
  prepareGifToSend,
} from '@modules/chats/utils';
import {useIsChatUserDeactivated} from '@modules/chats/store/zustand/useGroups';
import {showSnackbar} from '@redux/dispatchers';
import {persistedUserId} from '@services/localStorage';
import {AMPLITUDE_EVENTS, Screens} from '@shared/constants';
import {SPACING, useTheme} from '@shared/theme';
import {isIos, logAmplitudeEvent} from '@shared/utils';

import {ChatReply} from '../ChatReply';
import {ChatDialog} from './components/ChatDialog';
import {MultimediaContainerFooter} from './components/MultimediaContainerFooter';
import {styles} from './styles';
import {SafeAreaBottom} from './components/SafeAreaBottom';
import {ChatMessagesSearchNavigator} from '../ChatMessagesSearch';
import {FooterRightContent} from './components/FooterRightContent';
import {FooterLeftContent} from './components/FooterLeftContent';
import {useSelectFileDialog} from './components/AnimationProvider';
import {AiSummaryRecording} from './components/AiSummary/AiSummaryRecording';
import {AiSummaryTooltip} from './components/AiSummary/AiSummaryTooltip';

interface Props {
  channel: string;
}

export function ChatFooter({channel}: Props) {
  const {t} = useTranslation();
  const {bottom} = useSafeAreaInsets();
  const {theme} = useTheme();
  const navigation = useNavigation();
  const inputRef = useRef<TextInputType>(null);
  const [message, setMessage] = useInputValue();
  const {
    tags,
    resetMentions,
    setInitialTags,
    setChatFooterHeight,
    chatFooterHeight,
  } = useMentions();
  const isFocused = useIsFocused();
  const conversation = useShallowConversation(channel, state => ({
    name: state.name,
    is_channel: state.is_channel,
    is_im: state.is_im,
    other_user: state.other_user,
    picture_url: state.picture_url,
    id: state.id,
  }));
  const avatar = useMemo(
    () => getChatAvatar(conversation, persistedUserId!),
    [conversation],
  );
  const isUserDeactivated = useIsChatUserDeactivated(channel);
  const isSearchVisible = useMessagesSearch(state => state.isSearchVisible);

  const {targetMessage, isEditing, isReplying, reset} =
    useMessageActionsContext();
  const {editMessage} = useMessageActions({channel});
  const [showMissingPermissions, setShowMissingPermissions] = useState(false);
  const isChannel = useIsChannel(channel);
  const messagesListRef = useMessagesListRef();
  const messagesListMethodsRef = useMessagesListMethodsRef();
  const {audioDurationMsRef, stopRecording, resetAudioRecording} =
    useAudioRecording();
  const {inputAnimatedStyle, reset: resetAudioAnimatedValues} =
    useAudioRecordingAnimated();
  const {hideDialog} = useSelectFileDialog();

  const scrollToBottom = useCallback(() => {
    messagesListMethodsRef.current?.scheduleScrollTo(() => {
      messagesListRef.current?.scrollToEnd({animated: false});
    });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  const hasFirstPageLoadError = useHasFirstPageLoadError(channel);

  const {onOpenDisplayer} = useGifSelector({
    onSelect: async (media: GiphyMedia) => {
      const gif = await prepareGifToSend(media);
      reset();
      const params = targetMessage?.data && {replyMessage: targetMessage.data};
      sendGif(gif, params);
      scrollToBottom();
    },
  });

  const {
    uploadImageOrVideo,
    uploadMediaItems,
    uploadFile,
    files,
    removeMedia,
    clearMedia,
    uploadImageBase64,
    uploadImagePaste,
    retryMedia,
    totalFilesBytes,
    areAllFilesUploaded,
  } = useUploadMedia();

  const {isAiGeneratedTextRef} = useAiSummary();

  const onPasteImage = useCallback(
    async (e: NativeSyntheticEvent<{uri: string; bytes: number}>) => {
      if (isIos && e.nativeEvent.uri) {
        const {bytes, uri} = e.nativeEvent;
        await uploadImagePaste(uri, bytes);
      } else {
        const image = await Clipboard.getImageAsync({
          format: 'jpeg',
          jpegQuality: 1,
        });
        if (image) {
          await uploadImageBase64(image.data);
        }
      }
      logAmplitudeEvent(AMPLITUDE_EVENTS.CHATS_2_MESSAGE_MEDIA_ATTACHED, {
        conversationId: conversation?.id,
        mediaQuantity: 1,
        mediaType: 'image',
        mediaOrigin: 'paste',
      });
    },
    [conversation?.id, uploadImagePaste, uploadImageBase64],
  );

  const trimmedMessage = message.trim();
  const showSendButton = !!(trimmedMessage.length || files.length);
  const isSendDisabled = isEditing
    ? trimmedMessage.length === 0
    : !areAllFilesUploaded;

  const clearInput = useCallback(() => {
    setMessage('');
    clearMedia();
    resetMentions();
    inputRef.current?.clear();
    ConversationDrafts.clear(channel);
  }, [setMessage, clearMedia, resetMentions, channel]);

  const {sendMessage, sendMedia, sendVoiceMessage, sendGif} = usePostMessage({
    channel,
    onMessageGenerated: reset,
  });

  const onSendPress = useCallback(() => {
    const photosAndVideosArray = [] as MediaEntity[];
    const filesArray = [] as MediaEntity[];

    files.forEach(file => {
      if (file.type === MediaType.IMAGE || file.type === MediaType.VIDEO) {
        photosAndVideosArray.push(file);
      } else if (file.type === MediaType.FILE) {
        filesArray.push(file);
      }
    });

    const hasImagesOrVideos = photosAndVideosArray.length > 0;
    const hasFiles = filesArray.length > 0;

    const baseParams = {
      replyMessage: targetMessage?.data,
      taggedUsers: tags,
      generatedByAi: isAiGeneratedTextRef.current,
    };

    const baseParamsWithText = {
      ...baseParams,
      text: message,
    };

    if (hasFiles || hasImagesOrVideos) {
      if (hasFiles) {
        filesArray.forEach((file, index) => {
          const isFirstFile = index === 0;

          const params = hasImagesOrVideos
            ? baseParams
            : isFirstFile
            ? baseParamsWithText
            : {};

          sendMedia([file], params);
        });
      }
      if (hasImagesOrVideos) {
        sendMedia(photosAndVideosArray, baseParamsWithText);
      }
    } else {
      // Send message
      sendMessage(message, baseParams);
    }
    clearInput();
    scrollToBottom();
  }, [
    files,
    clearInput,
    sendMedia,
    message,
    sendMessage,
    tags,
    targetMessage?.data,
    scrollToBottom,
    isAiGeneratedTextRef,
  ]);

  const onSendAudio = useCallback(async () => {
    const uri = await stopRecording();

    if (!uri || !audioDurationMsRef.current) {
      return;
    }

    sendVoiceMessage({
      uri,
      durationMs: audioDurationMsRef.current,
      replyMessage: targetMessage?.data,
    });
    scrollToBottom();
    resetAudioRecording();
    resetAudioAnimatedValues();

    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [targetMessage?.data]);
  useAudioRecordingLongPressListener(onSendAudio);

  const onSubmitEdition = useCallback(() => {
    if (targetMessage?.id) {
      editMessage({
        clientMsgId: targetMessage.id,
        newText: message,
        taggedUsers: tags,
      });
    }
    reset();
    clearInput();
  }, [clearInput, editMessage, message, reset, tags, targetMessage?.id]);

  const onSelectFile = useEvent(async () => {
    if (files.length === CHAT_MAX_ATTACHMENTS) {
      showSnackbar({
        title: t('chat.files.limit_count_files', {
          count: CHAT_MAX_ATTACHMENTS,
        }),
        variant: 'info',
      });
      return;
    }

    hideDialog();
    const result = await uploadFile();

    if (!result.ok && result.shouldOpenSettings) {
      setShowMissingPermissions(true);
      return;
    }
    logAmplitudeEvent(AMPLITUDE_EVENTS.CHATS_2_MESSAGE_MEDIA_ATTACHED, {
      conversationId: conversation?.id,
      mediaQuantity: 1,
      mediaType: 'file',
      mediaOrigin: 'fileExplorer',
    });
  });

  const onSelectMedia = useEvent(async () => {
    if (files.length === CHAT_MAX_ATTACHMENTS) {
      showSnackbar({
        title: t('chat.files.limit_count_files', {
          count: CHAT_MAX_ATTACHMENTS,
        }),
        variant: 'info',
      });
      return;
    }

    hideDialog();

    // Need to dismiss the keyboard to avoid UI errors
    Keyboard.dismiss();

    const result = await uploadImageOrVideo({
      multiple: true,
      withVideo: true,
    });

    if (!result.ok && result.shouldOpenSettings) {
      setShowMissingPermissions(true);
      return;
    }

    if (result.ok && result.data) {
      const imageCount = result.data.filter(
        media => media.type === MediaType.IMAGE,
      ).length;
      const videoCount = result.data.filter(
        media => media.type === MediaType.VIDEO,
      ).length;

      let mediaType: 'image' | 'video' | 'imagesAndVideo' = 'image';
      if (imageCount > 0 && videoCount > 0) {
        mediaType = 'imagesAndVideo';
      } else if (videoCount > 0) {
        mediaType = 'video';
      }

      logAmplitudeEvent(AMPLITUDE_EVENTS.CHATS_2_MESSAGE_MEDIA_ATTACHED, {
        conversationId: conversation?.id,
        mediaQuantity: result.data.length,
        mediaType,
        mediaOrigin: 'gallery',
      });
    }
  });

  const onChatFooterLayout = useCallback(
    (e: LayoutChangeEvent) => {
      setChatFooterHeight(e.nativeEvent.layout.height);
    },
    [setChatFooterHeight],
  );

  // Show/hide keyboard when reactions dialog is opened/closed
  useKeyboardHandlerOnReactionsDialog(inputRef);

  /**
   * Focus the input when the reply message id changes
   */
  useEffect(() => {
    if (isReplying && targetMessage?.id) {
      inputRef.current?.focus();
    }
  }, [isReplying, targetMessage?.id]);

  /**
   * Sync snackbar offset with chat footer height based on screen focus
   */
  useEffect(() => {
    snackbarSharedOffset.value = isFocused ? chatFooterHeight : 0;
  }, [isFocused, chatFooterHeight]);

  /**
   * Set original message as input value & focus the input when the editing message changes
   */
  useEffect(() => {
    if (isEditing && targetMessage?.data) {
      // Check if target has tags
      if (targetMessage.data.highlighted_spans.length) {
        // Save the formatted text as the input value
        setMessage(targetMessage.data.formatted_text);
        const mappedTags = mapHighlightedSpansToTaggedUsers(
          targetMessage.data.highlighted_spans,
          targetMessage.data.formatted_text,
        );

        // Convert the highlighted spans to tagged users
        setInitialTags({
          tags: mappedTags,
          tagUserStartPosition: targetMessage.data.formatted_text.length - 1,
          messageBodyWithoutLastTag: targetMessage.data.formatted_text,
        });
      } else {
        setMessage(targetMessage.data.text);
      }
      inputRef.current?.focus();
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [isEditing, targetMessage]);

  const onCameraPress = useCallback(async () => {
    hideDialog();

    if (files.length >= CHAT_MAX_ATTACHMENTS) {
      showSnackbar({
        title: t('chat.files.limit_count_files', {
          count: CHAT_MAX_ATTACHMENTS,
        }),
        variant: 'info',
      });
      return;
    }

    let cameraPermission = Camera.getCameraPermissionStatus();
    if (cameraPermission !== 'granted') {
      cameraPermission = await Camera.requestCameraPermission();
    }

    if (cameraPermission !== 'granted') {
      Alert.alert(
        t('general.permissions.required'),
        t('general.permissions.denied'),
        [
          {text: t('general.cancel'), style: 'cancel'},
          {text: t('general.settings'), onPress: () => Linking.openSettings()},
        ],
      );
      return;
    }

    let microphonePermission = Camera.getMicrophonePermissionStatus();
    if (microphonePermission !== 'granted') {
      microphonePermission = await Camera.requestMicrophonePermission();
    }

    logAmplitudeEvent(AMPLITUDE_EVENTS.CHATS_2_CAMERA_OPENED, {
      conversationId: conversation?.id,
    });
    navigation.navigate(Screens.NEW_CAMERA, {
      hasMicrophonePermission: microphonePermission === 'granted',
      conversationUserName: avatar.title,
      totalFilesBytes,
      // Both camera captures and gallery picks share the same flow now —
      // the camera screen builds the MediaEntity and we route through the
      // unified upload pipeline here.
      onMediaPicked: (item, source) => {
        // Camera captures bypass the size/count limits — those are gated
        // upstream (showCameraScreen blocks entry once at the cap, and
        // the camera screen caps recording duration). We never want to
        // throw away a freshly captured photo/video.
        // Gallery picks go through the full validation; the camera
        // screen awaits the result and only shows preview if accepted.
        return uploadMediaItems([item], {
          enforceLimits: source === 'cameraGallery',
        });
      },
      onMediaConfirmed: (item, caption, source) => {
        if (caption) {
          setMessage(caption);
        }
        logAmplitudeEvent(AMPLITUDE_EVENTS.CHATS_2_MESSAGE_MEDIA_ATTACHED, {
          conversationId: conversation?.id,
          mediaQuantity: 1,
          mediaType: item.type === MediaType.VIDEO ? 'video' : 'image',
          mediaOrigin: source,
        });
      },
      onMediaCanceled: (itemId: string) => {
        // Marks the entity cancelled (so anything still hashing/predicting
        // in the pipeline drops it) and removes it from the input.
        removeMedia(itemId);
      },
    });
  }, [
    navigation,
    avatar.title,
    totalFilesBytes,
    uploadMediaItems,
    removeMedia,
    setMessage,
    files.length,
    t,
    conversation?.id,
    hideDialog,
  ]);

  const onSelectGif = useCallback(() => {
    hideDialog();
    onOpenDisplayer();
  }, [onOpenDisplayer, hideDialog]);

  const memoStyles = useMemo(() => {
    return {
      deletedContainer: [
        styles.deletedContainer,
        {
          backgroundColor: theme.background.layout.default,
          paddingBottom: bottom + SPACING.x1,
        },
      ],
      footerContainer: [
        styles.footerContainer,
        {backgroundColor: theme.background.layout.default},
      ],
      container: [
        styles.container,
        {backgroundColor: theme.background.layout.tertiary},
      ],
      inputWrapper: [styles.inputWrapper, inputAnimatedStyle],
    };
  }, [theme, inputAnimatedStyle, bottom]);

  if (hasFirstPageLoadError) {
    return null;
  }

  return isUserDeactivated ? (
    <View style={memoStyles.deletedContainer}>
      <Typography
        variant="xxs"
        color={theme.text.neutral.lighter}
        align="center">
        {t('chat.groups.disabled_user_chat')}
      </Typography>
    </View>
  ) : (
    <>
      <View onLayout={onChatFooterLayout}>
        <View style={memoStyles.footerContainer}>
          <ChatReply isVisible={isReplying} />
          {isSearchVisible ? (
            <ChatMessagesSearchNavigator />
          ) : (
            <View style={memoStyles.container}>
              <Animated.View style={memoStyles.inputWrapper}>
                <FooterLeftContent clearInput={clearInput} />
                <View
                  style={styles.inputContainer}
                  testID="chat-footer-input-container">
                  <ChatInput
                    ref={inputRef}
                    enableMentions={isChannel}
                    onDialogWillOpen={hideDialog}
                    onPasteImage={onPasteImage}
                    onGifPress={onOpenDisplayer}
                  />
                </View>
              </Animated.View>
              <FooterRightContent
                showEditSubmitButton={isEditing}
                showSendButton={showSendButton}
                isSendDisabled={isSendDisabled}
                onSubmitEdition={onSubmitEdition}
                onSendPress={onSendPress}
                onCameraPress={onCameraPress}
                onSendAudioPress={onSendAudio}
              />
              <AiSummaryRecording />
            </View>
          )}
          <DialogMissingPermission
            isVisible={showMissingPermissions}
            onClose={() => setShowMissingPermissions(false)}
          />
        </View>
        <MultimediaContainerFooter
          files={files}
          onRemoveFile={removeMedia}
          onRetryFile={retryMedia}
        />
        <SafeAreaBottom />
        <ChatDialog
          onSelectFile={onSelectFile}
          onSelectImages={onSelectMedia}
          onCameraPress={onCameraPress}
          onSelectGif={onSelectGif}
        />
      </View>
      <AiSummaryTooltip />
    </>
  );
}
