import React, { useCallback, useRef, useState } from 'react';
import { Appbar, Banner, Button, useTheme } from 'react-native-paper';
import { useNavigation } from '@react-navigation/native';
import { ScrollView, View } from 'react-native';
import { NativeStackScreenProps } from '@react-navigation/native-stack';
import { useRealm } from '@realm/react';
import { BSON } from 'realm';
import { AndroidScoped, FileSystem } from 'react-native-file-access';
import { useSelector } from 'react-redux';
import { extension } from 'react-native-mime-types';
import { useDeviceOrientation } from '@react-native-community/hooks';
import { pick } from 'react-native-document-picker';
import {
  documentPickerResponseToAddMemeFile,
  ROUTE,
  RootStackParamList,
} from '../../types';
import { Meme, Tag } from '../../database';
import { RootState } from '../../state';
import {
  allowedMimeTypes,
  getMemeType,
  validateMemeTitle,
} from '../../utilities';
import { MemeEditor } from '../../components';
import editorStyles from './editorStyles';

const AddMeme = ({
  route,
}: NativeStackScreenProps<RootStackParamList, ROUTE.ADD_MEME>) => {
  const { goBack } = useNavigation();
  const { colors } = useTheme();
  const orientation = useDeviceOrientation();
  const realm = useRealm();
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
  const storageUri = useSelector(
    (state: RootState) => state.settings.storageUri,
  )!;

  const [index, setIndex] = useState(0);
  const files = useRef(route.params.files);
  const file = useRef(files.current[index]);
  const isLastFile = index === files.current.length - 1;

  const [memeUri, setMemeUri] = useState(file.current.uri);
  const [memeFilename, setMemeFilename] = useState(file.current.filename);
  const [memeError, setMemeError] = useState<Error>();
  const [memeTitle, setMemeTitle] = useState(validateMemeTitle('New Meme'));
  const [memeIsFavorite, setMemeIsFavorite] = useState(false);
  const [memeTags, setMemeTags] = useState(new Map<string, Tag>());

  const [isSaving, setIsSaving] = useState(false);
  const [isSavingAndAddingMore, setIsSavingAndAddingMore] = useState(false);

  const saveMeme = useCallback(async () => {
    const uuid = new BSON.UUID();
    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
    const mimeType = file.current.type!;
    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
    const memeType = getMemeType(mimeType)!;

    const fileExtension = extension(mimeType) as string;
    if (!fileExtension) goBack();

    const filename = `${uuid.toHexString()}-${Math.round(
      Date.now() / 1000,
    )}.${fileExtension}`;
    const uri = AndroidScoped.appendPath(storageUri, filename);

    await FileSystem.cp(file.current.uri, uri);
    const { size } = await FileSystem.stat(uri);

    realm.write(() => {
      const meme: Meme | undefined = realm.create<Meme>(Meme.schema.name, {
        id: uuid,
        type: memeType,
        filename,
        mimeType,
        size,
        title: memeTitle.parsed,
        isFavorite: memeIsFavorite,
        tags: [...memeTags.values()],
        tagsLength: memeTags.size,
      });

      memeTags.forEach(tag => {
        tag.dateModified = new Date();
        tag.memes.push(meme);
        tag.memesLength = tag.memes.length;
      });
    });
  }, [goBack, memeIsFavorite, memeTags, memeTitle.parsed, realm, storageUri]);

  const handleSave = useCallback(async () => {
    setIsSaving(true);
    await saveMeme();
    goBack();
  }, [goBack, saveMeme]);

  const handleSaveAndNext = useCallback(async () => {
    setIsSaving(true);
    await saveMeme();
    setIsSaving(false);

    setIndex(index + 1);
    file.current = files.current[index + 1];

    setMemeUri(file.current.uri);
    setMemeFilename(file.current.filename);
    setMemeTitle(validateMemeTitle('New Meme'));
    setMemeIsFavorite(false);
    setMemeTags(new Map<string, Tag>());
  }, [index, saveMeme]);

  const handleSaveAndAddMore = useCallback(async () => {
    setIsSavingAndAddingMore(true);
    await saveMeme();
    setIsSavingAndAddingMore(false);

    setIndex(0);
    const response = await pick({
      type: allowedMimeTypes,
      allowMultiSelection: true,
    }).catch(goBack);
    if (!response) return;
    files.current = documentPickerResponseToAddMemeFile(response);
    file.current = files.current[0];

    setMemeUri(file.current.uri);
    setMemeFilename(file.current.filename);
    setMemeTitle(validateMemeTitle('New Meme'));
    setMemeIsFavorite(false);
    setMemeTags(new Map<string, Tag>());
  }, [goBack, saveMeme]);

  return (
    <>
      <Appbar.Header>
        <Appbar.BackAction onPress={() => goBack()} />
        <Appbar.Content title={'Add Meme'} />
        <Appbar.Action
          icon={memeIsFavorite ? 'heart' : 'heart-outline'}
          onPress={() => setMemeIsFavorite(!memeIsFavorite)}
        />
      </Appbar.Header>
      <Banner
        visible={!!memeError}
        actions={[
          {
            label: 'Cancel',
            onPress: goBack,
          },
        ]}>
        The selected URI appears to be broken. This may have been caused by the
        file being corrupted or unsupported.
      </Banner>
      <ScrollView
        contentContainerStyle={[
          editorStyles.scrollView,
          orientation === 'portrait'
            ? editorStyles.scrollViewPortrait
            : editorStyles.scrollViewLandscape,
          { backgroundColor: colors.background },
        ]}>
        <View style={editorStyles.editorView}>
          <MemeEditor
            memeUri={memeUri}
            memeFilename={memeFilename}
            memeError={memeError}
            setMemeError={setMemeError}
            memeTitle={memeTitle}
            setMemeTitle={setMemeTitle}
            memeTags={memeTags}
            setMemeTags={setMemeTags}
          />
        </View>
        <View style={editorStyles.saveButtonView}>
          <Button
            mode="contained-tonal"
            icon="plus"
            onPress={handleSaveAndAddMore}
            disabled={
              !memeTitle.valid ||
              isSaving ||
              isSavingAndAddingMore ||
              !!memeError ||
              !isLastFile
            }
            loading={isSavingAndAddingMore}
            style={editorStyles.saveAndAddButton}>
            Save & Add More
          </Button>
          <Button
            mode="contained"
            icon="floppy"
            onPress={isLastFile ? handleSave : handleSaveAndNext}
            disabled={
              !memeTitle.valid ||
              isSaving ||
              isSavingAndAddingMore ||
              !!memeError
            }
            loading={isSaving}
            style={editorStyles.saveButton}>
            {isLastFile ? 'Save' : 'Save & Next'}
          </Button>
        </View>
      </ScrollView>
    </>
  );
};

export default AddMeme;
