import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
import {View, Dimensions} from 'react-native';
import {useTranslation} from 'react-i18next';
import {useSafeAreaInsets} from 'react-native-safe-area-context';
import {
  Image as PickerImage,
  ImageOrVideo,
} from 'react-native-image-crop-picker';
import {useNavigation} from '@react-navigation/native';
import {IconEdit} from '@tabler/icons-react-native';
import {Button, IconButton} from '@components';
import {useFullscreenVideo} from '@components/_custom/Video';
import {AttachmentType} from '@interfaces/attachments';
import {ERROR} from '@shared/strings';
import {useTheme, SPACING} from '@shared/theme';
import {openCropper, uploadImage, uploadVideo} from '@shared/utils';

import {isImage, getImageDimensions, generateAssetsThumbnails} from './utils';
import {useNewPost} from '../../hooks/useNewPost';
import {styles} from './styles';
import Thumbs from './components/Thumbs';
import ActiveAsset from './components/ActiveAsset';
import {THUMB_SIZE} from './components/Thumbs/styles';

const MAIN_PREVIEW_HEIGHT_FACTOR = 0.5;
const THUMBS_BAR_HEIGHT = THUMB_SIZE + SPACING['x1.5'] * 2;

function SelectedMediaScreen() {
  const navigation = useNavigation();
  const {t} = useTranslation();
  const {theme} = useTheme();
  const {bottom, top} = useSafeAreaInsets();
  const {
    images,
    videos,
    setImages,
    setVideos,
    setCurrentSize,
    pickedAssets,
    setPickedAssets,
  } = useNewPost();
  const [assets, setAssets] = useState<ImageOrVideo[]>(pickedAssets || []);
  const [activeIndex, setActiveIndex] = useState<number>(0);
  const active = assets[activeIndex];
  const nextPositionRef = useRef<number>(images.length + videos.length);
  const [videoThumbs, setVideoThumbs] = useState<Record<string, string>>({});
  const {openVideo, renderFullscreenVideo} = useFullscreenVideo();

  const {height: screenHeight} = Dimensions.get('window');
  const maxPreviewHeight = screenHeight * MAIN_PREVIEW_HEIGHT_FACTOR;
  const activeAspectRatio = useMemo(() => {
    const w = active?.width;
    const h = active?.height;
    return w && h ? w / h : undefined;
  }, [active]);

  useEffect(() => {
    let isMounted = true;
    generateAssetsThumbnails(assets, videoThumbs, (path, thumb) => {
      if (isMounted && thumb) {
        setVideoThumbs(prev => ({...prev, [path]: thumb}));
      }
    });
    return () => {
      isMounted = false;
    };
  }, [assets, videoThumbs]);

  const onEditActive = useCallback(async () => {
    const current = assets[activeIndex];
    if (!current || !isImage(current)) {
      return;
    }
    try {
      const cropped = await openCropper(current as PickerImage);
      const measured = await getImageDimensions(cropped.path, {
        width: (cropped as {width?: number}).width,
        height: (cropped as {height?: number}).height,
      });
      setAssets(prev =>
        prev.map(f =>
          f.path === current.path
            ? {
                ...cropped,
                width: measured.width,
                height: measured.height,
              }
            : f,
        ),
      );
    } catch (_e) {
      // Ignore cancel
    }
  }, [activeIndex, assets]);

  const onPressActive = useCallback(() => {
    const current = assets[activeIndex];
    if (!current) {
      return;
    }
    if (isImage(current)) {
      onEditActive();
    } else {
      openVideo(current.path);
    }
  }, [activeIndex, assets, onEditActive, openVideo]);

  const addImage = useCallback(
    async (image: PickerImage) => {
      const position = nextPositionRef.current++;
      setImages(prev =>
        prev.concat([
          {
            position,
            path: image.path,
            isLoading: true,
            type: AttachmentType.IMAGE,
            size: image.size.toString(),
            name: image.filename,
            url: image.path,
          },
        ]),
      );
      const crop = image.cropRect;
      const newImage = await uploadImage({
        ...image,
        height: crop?.height || image.height,
        width: crop?.width || image.width,
      });
      if (newImage !== ERROR) {
        setImages(prev =>
          prev.map(item =>
            item.path === image.path
              ? {...item, ...newImage, isLoading: false}
              : item,
          ),
        );
        setCurrentSize(prev => prev + image.size);
      } else {
        setImages(prev => prev.filter(item => item.path !== image.path));
      }
    },
    [setCurrentSize, setImages],
  );

  const addVideo = useCallback(
    async (video: ImageOrVideo) => {
      const position = nextPositionRef.current++;
      setVideos(prev =>
        prev.concat([
          {
            position,
            path: video.path,
            isLoading: true,
            type: AttachmentType.VIDEO,
            size: (video.size || 0).toString(),
            name: video.filename,
            url: video.path,
          },
        ]),
      );
      const newVideo = await uploadVideo(video);
      if (newVideo !== ERROR) {
        setVideos(prev =>
          prev.map(item =>
            item.path === video.path
              ? {...item, ...newVideo, isLoading: false}
              : item,
          ),
        );
        setCurrentSize(prev => prev + (video.size || 0));
      } else {
        setVideos(prev => prev.filter(item => item.path !== video.path));
      }
    },
    [setCurrentSize, setVideos],
  );

  const onGoBack = useCallback(() => {
    setPickedAssets([]);
    navigation.goBack();
  }, [navigation, setPickedAssets]);

  const onAddAll = useCallback(() => {
    for (const f of assets) {
      if (isImage(f)) {
        addImage(f);
      } else {
        addVideo(f);
      }
    }
    setPickedAssets([]);
    navigation.goBack();
  }, [assets, addImage, addVideo, navigation, setPickedAssets]);

  return (
    <View
      style={[
        styles.container,
        {backgroundColor: theme.background.elements.default, paddingTop: top},
      ]}>
      <View style={styles.header}>
        <Button
          text={t('general.cancel')}
          variant="tertiary"
          size="sm"
          onPress={onGoBack}
        />
        <Button
          text={t('general.add')}
          size="sm"
          onPress={onAddAll}
          variant="secondary"
        />
      </View>
      <View
        style={[
          styles.content,
          {paddingBottom: bottom + THUMBS_BAR_HEIGHT + SPACING.x2},
        ]}>
        <ActiveAsset
          active={active}
          onPressActive={onPressActive}
          activeAspectRatio={activeAspectRatio}
          maxPreviewHeight={maxPreviewHeight}
          videoThumbs={videoThumbs}
        />
        {!!active && isImage(active) && (
          <IconButton
            Icon={IconEdit}
            variant="secondary"
            style={styles.editButton}
            onPress={onPressActive}
          />
        )}
        <Thumbs
          assets={assets}
          activeIndex={activeIndex}
          onSelectIndex={setActiveIndex}
          videoThumbs={videoThumbs}
        />
      </View>
      {renderFullscreenVideo()}
    </View>
  );
}

export default SelectedMediaScreen;
