import React, {
  useState,
  useEffect,
  forwardRef,
  ForwardedRef,
  useImperativeHandle,
  useCallback,
  useRef,
} from 'react';
import {
  View,
  Keyboard,
  LayoutChangeEvent,
  ViewStyle,
  StyleProp,
  TouchableOpacity,
} from 'react-native';
import {useTranslation} from 'react-i18next';
import {
  changeTaggedUserStartPosition,
  deleteTaggedUser,
  addTaggedUser,
} from 'tag-users';
import {Video, Image} from 'react-native-image-crop-picker';
import Animated, {
  useAnimatedStyle,
  useSharedValue,
} from 'react-native-reanimated';
import {KeyboardStickyView} from 'react-native-keyboard-controller';
import {
  IconCamera,
  IconPhoto,
  IconSend,
  IconVideo,
} from '@tabler/icons-react-native';
import {GiphyMedia} from '@giphy/react-native-sdk';
import {IconButton, Spinner} from '@components';
import {TextInputType} from '@components/_core/TextInput';
import {BODY_MAX_LENGTH} from '@components/_custom/NewPost';
import SelectImageOrVideo from '@components/_custom/SelectImageOrVideo';
import {useKeyboard} from '@hooks/useKeyboard';
import {useKeyboardHeight} from '@hooks/useKeyboardHeight';
import {useSafeAreaBottomPadding} from '@hooks/useSafeAreaBottomPadding';
import {useUpdateTaggedUserPosition} from '@hooks/useUpdateTaggedUserPosition';
import {User} from '@interfaces/user';
import {
  Attachment,
  AttachmentType,
  FileURI,
  GifAttachment,
  RequestAttachment,
} from '@interfaces/attachments';
import {Comment as CommentType} from '@interfaces/comments';
import GifDisplayer from '@modules/post/components/GifDisplayer';
import {CommentAttachmentsData} from '@modules/post/screens/PublishPost/interfaces';
import {useMarketplaceCommentToEdit} from '@modules/marketplace/stores/useMarketplaceStore';
import {useAppSelector} from '@redux/utils';
import {ERROR} from '@shared/strings';
import {COLORS} from '@shared/colors';
import {useGifDialog} from '@shared/hooks/useGifDialog';
import {
  uploadImage,
  uploadVideo,
  sizeToBytes,
  openCamera,
  logImageCropped,
} from '@shared/utils';
import {SPACING, useTheme} from '@shared/theme';

import {MentionSuggestions} from './components/MentionSuggestions';
import {TagUsersMentionSuggestions, TagUsersTextInput} from './components/Tag';
import {MAX_ATTACHMENTS_SIZE, MAX_MULTIMEDIA_QUANTITY} from './constants';
import {styles} from './styles';
import MediaDisplayer from './components/MediaDisplayer';
import {UploadedVideo, UploadedImage, PublishCommentData} from './interfaces';

interface AddCommentProps {
  autoFocus?: boolean;
  groupId?: number;
  onBlur?: () => void;
  displayGif?: boolean;
  style?: StyleProp<ViewStyle>;
  onPublishComment: (
    data: PublishCommentData,
  ) => Promise<CommentType> | undefined;
  loading?: boolean;
  keyProp?: string;
  isMarketplace?: boolean;
}

export interface AddCommentRefType {
  initiateTag: (userId: number, userName: string) => void;
  focus: () => void;
}

interface TaggedUser {
  start: number;
  length: number;
  type: {
    user: {
      id: number;
    };
  };
}

export const AddComment = forwardRef(function AddComment(
  {
    autoFocus = false,
    groupId,
    onBlur,
    displayGif = true,
    style,
    onPublishComment,
    loading,
    keyProp,
    isMarketplace = false,
  }: AddCommentProps,
  ref: ForwardedRef<AddCommentRefType>,
) {
  const reduxCommentToEdit = useAppSelector(
    ({comment}) => comment.commentToEdit,
  );
  const marketplaceCommentToEdit = useMarketplaceCommentToEdit();
  const commentToEdit = isMarketplace
    ? marketplaceCommentToEdit
    : reduxCommentToEdit;
  const [messageBody, setMessageBody] = useState('');
  const [messageBodyWithoutLastTag, setMessageBodyWithoutLastTag] =
    useState('');
  const [taggedUsers, setTaggedUsers] = useState<TaggedUser[]>([]);
  const [focused, setFocused] = useState(false);
  const [attachmentsSize, setAttachmentSize] = useState(0);
  const [loadingMultimedia, setLoadingMultimedia] = useState(false);
  const [images, setImages] = useState<UploadedImage[]>([]);
  const [videos, setVideos] = useState<UploadedVideo[]>([]);
  const [gif, setGif] = useState<Nullable<GifAttachment>>(null);
  const [fileSelectDialogIsOpen, setFileSelectDialogIsOpen] = useState(false);
  const [composerStackHeight, setComposerStackHeight] = useState(0);
  const textInputRef = useRef<TextInputType>(null);
  const [mentionSuggestions, setMentionSuggestions] =
    useState<TagUsersMentionSuggestions>(() => ({
      userSearch: '',
      tagUser: false,
      isComposerFocused: false,
      onPickTaggableUser: () => {},
    }));
  const onMentionSuggestionsChange = useCallback(
    (next: TagUsersMentionSuggestions) => {
      setMentionSuggestions(next);
    },
    [],
  );
  const keyboardHeight = useKeyboardHeight();
  // TODO: Refactor this whole component, specially bottom padding calculation
  const bottom = useSafeAreaBottomPadding({bottom: 18, extra: SPACING.x2});
  const bottomPadding = useSharedValue(bottom);
  const {theme, iconSizes} = useTheme();

  useUpdateTaggedUserPosition(taggedUsers, messageBody, setTaggedUsers);

  const handleShowKeyboard = useCallback(() => {
    bottomPadding.value = SPACING.x2;
    setFocused(true);
  }, [bottomPadding]);
  const handleHideKeyboard = useCallback(() => {
    bottomPadding.value = bottom;
    setFocused(false);
  }, [bottom, bottomPadding]);
  useKeyboard({onShow: handleShowKeyboard, onHide: handleHideKeyboard}, true);

  const attachments: (UploadedVideo | UploadedImage)[] = [...images, ...videos];
  const commonDisable =
    attachments.length === MAX_MULTIMEDIA_QUANTITY ||
    !!gif ||
    loading ||
    loadingMultimedia;

  const {t} = useTranslation();

  const addImage = async (image: Image) => {
    setLoadingMultimedia(true);
    const {height, width, x, y} = image.cropRect!;
    const newImage = (await uploadImage({
      ...image,
      height,
      width,
    })) as UploadedImage | string;
    if (newImage !== ERROR) {
      setImages(prevImages => prevImages.concat([newImage as UploadedImage]));
      setAttachmentSize(prevSize => prevSize + image.size);
      logImageCropped('Comment', x, y);
    }
    setLoadingMultimedia(false);
  };

  const addVideo = async (video: Image) => {
    setLoadingMultimedia(true);
    const newVideo = await uploadVideo(video as Video & FileURI);
    if (newVideo !== ERROR) {
      setVideos(prevVideos => prevVideos.concat([newVideo as UploadedVideo]));
      setAttachmentSize(prevSize => prevSize + video.size);
    }
    setLoadingMultimedia(false);
  };

  useEffect(() => {
    if (gif) {
      setLoadingMultimedia(false);
    }
  }, [gif]);

  const handleGifPress = (media: GiphyMedia) => {
    setLoadingMultimedia(true);
    setGif(media as GifAttachment);
  };

  const handleDeleteGif = () => setGif(null);

  const handleDeleteVideo = (deletedVideo: UploadedVideo) => {
    const remainingVideos: UploadedVideo[] = [];
    setVideos(prevVideos => {
      prevVideos.forEach(
        video =>
          video.path !== deletedVideo.path && remainingVideos.push(video),
      );
      return remainingVideos;
    });
    setAttachmentSize(
      prevSize => prevSize - (sizeToBytes(deletedVideo.size) as number),
    );
  };

  const handleDeleteImage = (deletedImage: UploadedImage) => {
    const remainingImages: UploadedImage[] = [];
    setImages(prevImages => {
      prevImages.forEach(
        image =>
          image.path !== deletedImage.path && remainingImages.push(image),
      );
      return remainingImages;
    });
    setAttachmentSize(
      prevSize => prevSize - (sizeToBytes(deletedImage.size) as number),
    );
  };

  const handlePrecheck = useCallback(() => {
    const hasMaxSize = attachmentsSize > MAX_ATTACHMENTS_SIZE;
    Keyboard.dismiss();
    return !(loadingMultimedia || hasMaxSize);
  }, [attachmentsSize, loadingMultimedia]);

  const onChangeMessageBody = (newBody: string) => setMessageBody(newBody);

  const handleTaggedUserStartPositionChange = useCallback(
    (userId: number, newStartPosition: number) => {
      setTaggedUsers(prevTaggedUsers =>
        changeTaggedUserStartPosition(
          prevTaggedUsers,
          userId,
          newStartPosition,
        ),
      );
    },
    [],
  );

  const handleTaggedUserAdd = useCallback((user: User) => {
    setTaggedUsers(prevTaggedUsers => addTaggedUser(prevTaggedUsers, user));
  }, []);

  const handleTaggedUserDelete = useCallback((userId: number) => {
    setTaggedUsers(prevTaggedUsers =>
      deleteTaggedUser(prevTaggedUsers, userId),
    );
  }, []);

  const handleTagUsersInputBlur = useCallback(() => {
    setFocused(false);
    onBlur?.();
  }, [onBlur]);

  const handleTagUsersInputFocus = useCallback(() => {
    setFocused(true);
  }, []);

  const handleMessageBodyWithoutLastTagChange = useCallback((val: string) => {
    setMessageBodyWithoutLastTag(val);
  }, []);

  const onGifSelect = useCallback(
    (media: GiphyMedia) => {
      if (handlePrecheck()) {
        handleGifPress(media);
      }
    },
    [handlePrecheck],
  );

  const {showDialog: onOpenGifDialog} = useGifDialog({
    onSelect: onGifSelect,
    enabled: displayGif,
  });

  const commonSvgColor = commonDisable ? COLORS.GRAY_TWENTY : theme.neutralText;

  useImperativeHandle(
    ref,
    () => {
      return {
        initiateTag: (userId: number, userName: string) => {
          const length = userName.length + 1;
          const tagUser = {
            start: 0,
            length,
            type: {
              user: {
                id: userId,
              },
            },
          };
          textInputRef.current?.focus();
          setTaggedUsers([tagUser]);
          setMessageBody(`@${userName} `);
          setMessageBodyWithoutLastTag(`@${userName} `);
        },
        focus: () => {
          textInputRef.current?.focus();
        },
      };
    },
    [textInputRef],
  );

  const safeBottomPaddingStyle = useAnimatedStyle(() => ({
    paddingBottom: bottomPadding.value,
  }));

  const handlePublishComment = useCallback(async () => {
    await onPublishComment?.({
      attachmentsSize,
      commentToEdit,
      messageBody,
      images,
      videos,
      taggedUsers,
      gif,
    });

    setMessageBody('');
    setMessageBodyWithoutLastTag('');
    setTaggedUsers([]);
    setAttachmentSize(0);
    setImages([]);
    setVideos([]);
    setGif(null);
  }, [
    attachmentsSize,
    commentToEdit,
    gif,
    images,
    messageBody,
    onPublishComment,
    taggedUsers,
    videos,
  ]);

  useEffect(() => {
    if (commentToEdit) {
      const {
        body,
        attachments: commentAttachments,
        bodyAttributes,
      } = commentToEdit;

      // Need to wait for the edit modal to close before focusing the text input
      setTimeout(() => {
        textInputRef.current?.focus();
      }, 1000);
      setMessageBody(body || '');
      setMessageBodyWithoutLastTag(body || '');
      bodyAttributes && setTaggedUsers(bodyAttributes);
      const aux: CommentAttachmentsData = commentAttachments.reduce(
        (
          attachmentsData: CommentAttachmentsData,
          attachment: RequestAttachment,
        ) => {
          const typedAttachment = attachment as Attachment;
          const attachmentToPush = {
            ...typedAttachment,
            path: attachment.url,
          };
          switch (attachment.type) {
            case AttachmentType.IMAGE:
              attachmentsData.images.push(attachmentToPush);
              break;
            case AttachmentType.VIDEO:
              attachmentsData.videos.push(attachmentToPush);
              break;
            case AttachmentType.GIF:
              attachmentsData.gif = {
                ...(attachment as GifAttachment),
                id: attachment.externalReference!.id,
              };
              break;
          }
          attachmentsData.attachmentsSize += sizeToBytes(attachment.size);
          return attachmentsData;
        },
        {
          attachmentsSize: 0,
          images: [],
          videos: [],
          gif: null,
        },
      );
      setAttachmentSize(aux.attachmentsSize);
      setImages(aux.images as UploadedImage[]);
      setVideos(aux.videos as UploadedVideo[]);
      setGif(aux.gif);
    } else {
      setMessageBody('');
      setMessageBodyWithoutLastTag('');
      setTaggedUsers([]);
      setAttachmentSize(0);
      setImages([]);
      setVideos([]);
      setGif(null);
    }
  }, [commentToEdit, textInputRef]);

  const renderVideoIcon = useCallback(() => {
    return <IconVideo color={commonSvgColor} size={iconSizes.x7} />;
  }, [commonSvgColor, iconSizes.x7]);

  const onPressCamera = () =>
    handlePrecheck() && openCamera({save: addImage, options: {cropping: true}});

  const onPressVideo = () =>
    handlePrecheck() &&
    openCamera({
      save: addVideo,
      options: {mediaType: 'video'},
    });

  const onOpenDialog = () => {
    Keyboard.dismiss();
    setFileSelectDialogIsOpen(true);
  };
  const onCloseDialog = () => setFileSelectDialogIsOpen(false);

  const disablePublishButton =
    loading ||
    loadingMultimedia ||
    !(messageBody.length || attachments.length || gif);

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

  /** Modal `bottomInset` is from the physical screen bottom; include keyboard so the sheet clears above it. */
  const mentionSheetBottomInset = composerStackHeight + keyboardHeight;

  return (
    <KeyboardStickyView key={keyProp}>
      <MentionSuggestions
        groupId={groupId}
        taggedUsers={taggedUsers}
        composerBottomInset={mentionSheetBottomInset}
        {...mentionSuggestions}
      />
      <View
        onLayout={onComposerStackLayout}
        style={[
          {
            backgroundColor: theme.background.elements.default,
            borderColor: theme.border.neutral.default,
          },
          style,
          styles.divider,
        ]}>
        <Animated.View
          style={[
            styles.container,
            {backgroundColor: theme.background.elements.default},
            !focused && !gif && !attachments.length && safeBottomPaddingStyle,
          ]}>
          <TagUsersTextInput
            ref={textInputRef}
            onMentionSuggestionsChange={onMentionSuggestionsChange}
            onBlur={handleTagUsersInputBlur}
            onFocus={handleTagUsersInputFocus}
            placeholder={t(
              commentToEdit ? 'post.edit_comment' : 'post.write_comment',
            )}
            placeholderTextColor={theme.text.neutral.lighter}
            maxLength={BODY_MAX_LENGTH}
            value={messageBody}
            messageBodyWithoutLastTag={messageBodyWithoutLastTag}
            setMessageBodyWithoutLastTag={handleMessageBodyWithoutLastTagChange}
            multiline
            onChangeText={onChangeMessageBody}
            taggedUsers={taggedUsers}
            changeTaggedUserStartPosition={handleTaggedUserStartPositionChange}
            addTaggedUser={handleTaggedUserAdd}
            deleteTaggedUser={handleTaggedUserDelete}
            autoCorrect
            autoCapitalize="sentences"
            autoFocus={autoFocus}
            groupId={groupId}
          />
          <TouchableOpacity
            style={[
              styles.iconContainer,
              {borderColor: theme.border.neutral.brand},
            ]}
            onPress={handlePublishComment}
            disabled={disablePublishButton}
            accessibilityLabel={t('general.send')}>
            {loading || loadingMultimedia ? (
              <Spinner />
            ) : (
              <IconSend color={theme.text.neutral.brand} size={iconSizes.x6} />
            )}
          </TouchableOpacity>
        </Animated.View>
        {!!(focused && !gif && !attachments.length) && (
          <View
            style={[
              styles.buttonsContainer,
              {backgroundColor: theme.background.layout.default},
            ]}>
            <IconButton
              variant="flat"
              onPress={onPressCamera}
              iconColor={commonSvgColor}
              Icon={IconCamera}
              disabled={commonDisable}
            />
            <IconButton
              variant="flat"
              onPress={onPressVideo}
              iconColor={commonSvgColor}
              Icon={renderVideoIcon}
              disabled={commonDisable}
            />
            <IconButton
              variant="flat"
              onPress={onOpenDialog}
              iconColor={commonSvgColor}
              Icon={IconPhoto}
              disabled={commonDisable}
            />
            {displayGif && (
              <GifDisplayer
                disabled={
                  !!(attachments.length || gif || loadingMultimedia || loading)
                }
                onPress={onOpenGifDialog}
                color={commonSvgColor}
              />
            )}
          </View>
        )}
        <SelectImageOrVideo.Dialog
          dialogIsOpen={fileSelectDialogIsOpen}
          addImage={addImage}
          addVideo={addVideo}
          onClose={onCloseDialog}
        />
        <MediaDisplayer
          attachments={attachments}
          gif={gif}
          isFocused={focused}
          handleDeleteGif={handleDeleteGif}
          handleDeleteImage={handleDeleteImage}
          handleDeleteVideo={handleDeleteVideo}
        />
      </View>
    </KeyboardStickyView>
  );
});

export * from './interfaces';
export * from './constants';
