import { useApolloClient } from '@apollo/client';
import * as FileSystem from 'expo-file-system';
import * as MediaLibrary from 'expo-media-library';
import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import {
  ActivityIndicator,
  Alert,
  Modal,
  Platform,
  StyleSheet,
  Text,
  View,
} from 'react-native';
import { Divider, ProgressBar } from 'react-native-paper';
import Toast from 'react-native-toast-message';
import { connect } from 'react-redux';
import { createStructuredSelector } from 'reselect';
import * as Sentry from '@sentry/react-native';
import { logImEXEvent } from '../../firebase/firebase.analytics';
import { GET_DOC_SIZE_TOTALS } from '../../graphql/documents.queries';
import {
  selectCurrentCameraJobId,
  selectDeleteAfterUpload,
} from '../../redux/app/app.selectors';
import {
  selectBodyshop,
  selectCurrentUser,
} from '../../redux/user/user.selectors';
import { formatBytes, handleUpload } from '../../util/document-upload.utility';

const mapStateToProps = createStructuredSelector({
  currentUser: selectCurrentUser,
  bodyshop: selectBodyshop,
  selectedCameraJobId: selectCurrentCameraJobId,
  deleteAfterUpload: selectDeleteAfterUpload,
});

export default connect(mapStateToProps, null)(UploadProgress);

export function UploadProgress({
  currentUser,
  bodyshop,
  selectedCameraJobId,
  deleteAfterUpload,
  uploads,
  setUploads,
  forceRerender,
}) {
  const [progress, setProgress] = useState({
    uploadInProgress: false,
    totalToUpload: 0,
    totalUploaded: 0,
    startTime: null,
    totalFiles: 0,
    totalFilesCompleted: 0,
    currentFile: null,
    files: {}, //uri is the key, value is progress
  });

  let filesToDelete = [];
  const client = useApolloClient();

  const { t } = useTranslation();

  useEffect(() => {
    if (uploads) {
      onDone(uploads);
      setUploads(null);
    }
  }, [uploads]);

  function handleOnSuccess(asset) {
    //NEEDS REDO.
    filesToDelete.push(asset);
    setProgress((progress) => ({
      ...progress,
      //  totalUploaded: progress.totalToUpload + asset.size,
      totalFilesCompleted: progress.totalFilesCompleted + 1,
      files: {
        ...progress.files,
        [asset.uri]: {
          ...progress.files[asset.uri],
          uploadEnd: new Date(),
        },
      },
    }));
  }

  function handleOnProgress({ uri, filename }, percent, loaded) {
    //NEED REDO
    setProgress((progress) => {
      return {
        ...progress,
        totalUploaded:
          progress.totalUploaded +
          (loaded - (progress.files[uri]?.loaded || 0)),
        files: {
          ...progress.files,
          [uri]: {
            ...progress.files[uri],
            percent,
            filename,
            speed: loaded - (progress.files[uri]?.loaded || 0),
            loaded: loaded,
            uploadStart: progress.files[uri]?.uploadStart || new Date(),
          },
        },
      };
    });
  }
  function handleOnError(error) {
    logImEXEvent('imexmobile_upload_documents_error', { error });
    Toast.show({
      type: 'error',
      text1: 'Unable to upload document.',
      text2: error,
      autoHide: false,
    });
  }

  const onDone = async (selectedFiles) => {
    setProgress((progress) => {
      return {
        ...progress,
        uploadInProgress: true,
        statusText: 'Preparing upload...',
      };
    });

    //Validate to make sure the totals for the file sizes do not exceed the total on the job.
    const data = [];
    const totalOfUploads = await selectedFiles.reduce(async (acc, val) => {
      //Get the size of the file based on URI.
      const info = await FileSystem.getInfoAsync(val.uri, { size: true });
      data.push({ ...info, ...val }); //Add in the size.
      val.albumId && MediaLibrary.migrateAlbumIfNeededAsync(val.albumId);
      return (await acc) + info.size;
    }, 0);

    if (selectedCameraJobId !== 'temp') {
      const queryData = await client.query({
        query: GET_DOC_SIZE_TOTALS,
        fetchPolicy: 'network-only',
        variables: {
          jobId: selectedCameraJobId,
        },
      });

      if (
        bodyshop.jobsizelimit -
          queryData.data.documents_aggregate.aggregate.sum.size <=
        totalOfUploads
      ) {
        //No more room... abandon ship.
        setProgress((progress) => ({
          ...progress,
          speed: 0,
          action: null,
          statusText: null,
          uploadInProgress: false,
        }));
        Alert.alert(
          t('mediabrowser.labels.storageexceeded_title'),
          t('mediabrowser.labels.storageexceeded')
        );
        return;
      }
    }
    //We made it this far. We have enough space, so let's start uploading.
    setProgress((progress) => ({
      ...progress,
      totalToUpload: totalOfUploads,
      totalUploaded: 0,
      totalFilesCompleted: 0,
      startTime: new Date(),
      totalFiles: data.length,
      currentFile: null,
      statusText: null,
      files: {}, //uri is the key, value is progress
    }));

    for (var i = 0; i < data.length + 4; i = i + 4) {
      //Reset the files.
      setProgress((progress) => ({ ...progress, files: {} }));
      let proms = [];
      if (data[i]) {
        proms.push(CreateUploadProm(data[i]));
      }
      if (data[i + 1]) {
        proms.push(CreateUploadProm(data[i + 1]));
      }
      if (data[i + 2]) {
        proms.push(CreateUploadProm(data[i + 2]));
      }
      if (data[i + 3]) {
        proms.push(CreateUploadProm(data[i + 3]));
      }

      await Promise.all(proms);
    }

    //Everything is uploaded, delete the succesful ones.
    if (deleteAfterUpload) {
      try {
        console.log('Trying to Delete', filesToDelete);
        if (Platform.OS === 'android') {
          //Create a new asset with the first file to delete.
          // console.log('Trying new delete.');
          try {
            await MediaLibrary.getPermissionsAsync(false);
            const albumremove = await MediaLibrary.removeAssetsFromAlbumAsync(
              filesToDelete,
              filesToDelete[0].albumId
            );
            Toast.show({
              type: 'info',
              text1: 'removeAssetsFromAlbumAsync',
              text2: JSON.stringify(albumremove),
              autoHide: false,
            });
          } catch (error) {
            console.log("🚀 ~ onDone ~ error:", error)
            Toast.show({
              type: 'error',
              text1: 'removeAssetsFromAlbumAsync',
              text2: JSON.stringify(error),
              autoHide: false,
            });
          }

          try {
            const delres = await MediaLibrary.deleteAssetsAsync(
              filesToDelete.map((f) => f.id)
            );

            Toast.show({
              type: 'info',
              text1: 'deleteAssetsAsync',
              text2: JSON.stringify(delres),
              autoHide: false,
            });
          } catch (error) {
            console.log("🚀 ~ onDone ~ error:", error)
            Toast.show({
              type: 'error',
              text1: 'deleteAssetsAsync',
              text2: JSON.stringify(error),
              autoHide: false,
            });
          }

          try {
            const res = Promise.all(
              filesToDelete.map((f) =>
                FileSystem.deleteAsync(f.uri, { idempotent: true })
              )
            );
            Toast.show({
              type: 'info',
              text1: 'FileSystemDelete',
              text2: JSON.stringify(res),
              autoHide: false,
            });
          } catch (error) {
            console.log("🚀 ~ onDone ~ error:", error)
            Toast.show({
              type: 'error',
              text1: 'FileSystemDelete',
              text2: JSON.stringify(error),
              autoHide: false,
            });
          }

          try {
            const album = await MediaLibrary.createAlbumAsync(
              'ImEX Mobile Deleted',
              filesToDelete.pop(),
              false
            );
            //Move the rest.
            if (filesToDelete.length > 0) {
              const moveResult = await MediaLibrary.addAssetsToAlbumAsync(
                filesToDelete,
                album,
                false
              );
            }
            const deleteResult = await MediaLibrary.deleteAlbumsAsync(album);
            Toast.show({
              type: 'info',
              text1: 'deleteAlbumsAsync',
              text2: JSON.stringify(deleteResult),
              autoHide: false,
            });
          } catch (error) {
            console.log("🚀 ~ onDone ~ error:", error)
            Toast.show({
              type: 'error',
              text1: 'deleteAlbumsAsync',
              text2: JSON.stringify(error),
              autoHide: false,
            });
          }
          //Delete the album.

          //This defaults to delete all assets in the album.
        } else {
          await MediaLibrary.deleteAssetsAsync(filesToDelete.map((f) => f.id));
        }
      } catch (error) {
        console.log('Unable to delete picture.', error);
        Sentry.Native.captureException(error);
      }
    }
    filesToDelete = [];
    Toast.show({
      type: 'success',
      text1: ` Upload completed.`,
      //
      //   text2: duration,
    });
    //Reset state.

    setProgress({
      uploadInProgress: false,
      totalToUpload: 0,
      totalUploaded: 0,
      totalFilesCompleted: 0,
      startTime: null,
      totalFiles: 0,
      currentFile: null,
      files: {},
    });

    forceRerender();
  };

  const CreateUploadProm = async (p) => {
    return handleUpload(
      {
        mediaId: p.id,
        onError: handleOnError,
        onProgress: ({ percent, loaded }) =>
          handleOnProgress(p, percent, loaded),
        onSuccess: () => handleOnSuccess(p),
      },
      {
        bodyshop: bodyshop,
        jobId: selectedCameraJobId !== 'temp' ? selectedCameraJobId : null,
        uploaded_by: currentUser.email,
        photo: p,
      }
    );
  };

  return (
    <Modal
      visible={progress.uploadInProgress}
      animationType='slide'
      transparent={true}
      onRequestClose={() => {
        Alert.alert('Cancel?', 'Do you want to abort the upload?', [
          {
            text: 'Yes',
            onPress: () => {
              setUploads(null);
              setProgress({
                uploadInProgress: false,
                totalToUpload: 0,
                totalUploaded: 0,
                totalFilesCompleted: 0,
                startTime: null,
                totalFiles: 0,
                currentFile: null,
                files: {},
              });
            },
          },
          { text: 'No' },
        ]);
      }}
    >
      <View style={styles.modalContainer}>
        <View style={styles.modal}>
          {Object.keys(progress.files).map((key) => (
            <View key={key} style={styles.progressItem}>
              <Text style={styles.progressText}>
                {progress.files[key].filename}
              </Text>
              <View style={styles.progressBarContainer}>
                <ProgressBar
                  progress={progress.files[key].percent}
                  style={styles.progress}
                  color={progress.files[key].percent === 1 ? 'green' : 'blue'}
                />
                <View
                  style={{
                    display: 'flex',
                    flexDirection: 'row',
                    alignItems: 'center',
                  }}
                >
                  <Text>{`${formatBytes(
                    progress.files[key].loaded /
                      (((progress.files[key].uploadEnd || new Date()) -
                        progress.files[key].uploadStart) /
                        1000)
                  )}/sec`}</Text>
                  {progress.files[key].percent === 1 && (
                    <>
                      <ActivityIndicator style={{ marginLeft: 12 }} />
                      <Text style={{ marginLeft: 4 }}>Processing...</Text>
                    </>
                  )}
                </View>
              </View>
            </View>
          ))}
          <View style={styles.centeredView}>
            {progress.statusText ? (
              <>
                <ActivityIndicator style={{ marginLeft: 12 }} />
                <Text style={{ marginLeft: 4 }}>{progress.statusText}</Text>
                <Divider />
              </>
            ) : (
              <>
                <Text>{`${progress.totalFilesCompleted} of ${progress.totalFiles} uploaded.`}</Text>
                <Text>{`${formatBytes(progress.totalUploaded)} of ${formatBytes(
                  progress.totalToUpload
                )} uploaded.`}</Text>
              </>
            )}
          </View>
        </View>
      </View>
    </Modal>
  );
}
const styles = StyleSheet.create({
  modalContainer: {
    display: 'flex',
    flex: 1,
    justifyContent: 'center',
  },
  modal: {
    //flex: 1,
    display: 'flex',
    marginLeft: 20,
    marginRight: 20,
    backgroundColor: 'white',
    borderRadius: 20,
    padding: 18,
    shadowColor: '#000',
    shadowOffset: {
      width: 0,
      height: 2,
    },
    shadowOpacity: 0.25,
    shadowRadius: 4,
    elevation: 5,
  },
  centeredView: {
    justifyContent: 'center',
    alignItems: 'center',
    marginTop: 22,
  },
  progressItem: {
    display: 'flex',
    flexDirection: 'row',
    alignItems: 'center',
    marginBottom: 12,
    marginLeft: 12,
    marginRight: 12,
  },
  progressText: {
    flex: 1,
  },
  progressBarContainer: {
    flex: 3,
    marginLeft: 12,
    marginRight: 12,
  },
});
