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

const EditMeme = ({
  route,
}: NativeStackScreenProps<RootStackParamList, ROUTE.EDIT_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,
  )!;

  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
  const meme = useObject<Meme>(
    Meme.schema.name,
    BSON.UUID.createFromHexString(route.params.id),
  )!;

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

  const [uri, setUri] = useState<string>();
  const [mimeType, setMimeType] = useState<string>();
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error>();
  const [staging, setStaging] = useState<StagingMeme>();
  const originalStaging = useRef<StagingMeme>();

  const resetState = useCallback(
    async (newUri: string) => {
      setLoading(true);
      // eslint-disable-next-line unicorn/no-useless-undefined
      setError(undefined);

      setUri(newUri);

      const guessedMimeType = await guessMimeType(newUri);
      if (!guessedMimeType) {
        setError(
          new Error('Could not determine MIME type or file is not supported.'),
        );
        return;
      }
      setMimeType(guessedMimeType);

      const stagingMeme = {
        title: validateMemeTitle(meme.title),
        isFavorite: meme.isFavorite,
        tags: new Map(meme.tags.map(tag => [tag.id.toHexString(), tag])),
      };

      setStaging(stagingMeme);
      originalStaging.current = stagingMeme;

      setLoading(false);
    },
    [meme.isFavorite, meme.tags, meme.title],
  );

  useEffect(
    () => void resetState(AndroidScoped.appendPath(storageUri, meme.filename)),
    [meme.filename, resetState, storageUri],
  );

  const handleSave = useCallback(() => {
    if (!mimeType || !staging) return;

    setIsSaving(true);

    realm.write(() => {
      meme.tags.forEach(tag => {
        if (!staging.tags.has(tag.id.toHexString())) {
          tag.memes.slice(tag.memes.indexOf(meme), 1);
          tag.memesLength -= 1;
          tag.dateModified = new Date();
        }
      });

      staging.tags.forEach(tag => {
        if (!meme.tags.some(memeTag => memeTag.id.equals(tag.id))) {
          tag.memes.push(meme);
          tag.memesLength = tag.memes.length;
          tag.dateModified = new Date();
        }
      });

      meme.title = staging.title.parsed;
      // @ts-expect-error - Realm is a fuck
      meme.tags = [...staging.tags.values()];
      meme.tagsLength = staging.tags.size;
      meme.dateModified = new Date();
    });

    goBack();
  }, [goBack, meme, mimeType, realm, staging]);

  const handleDelete = useCallback(async () => {
    setIsSaving(true);
    await deleteMeme(realm, storageUri, meme);
    goBack();
  }, [goBack, meme, realm, storageUri]);

  const handleFixUri = useCallback(async () => {
    const file = await pickSingle({ type: allowedMimeTypes }).catch(noOp);
    if (!file) return;

    const guessedMimeType = await guessMimeType(file.uri, file.type);
    if (!guessedMimeType) {
      setError(
        new Error('Could not determine MIME type or file is not supported.'),
      );
      return;
    }

    const memeType = getMemeTypeFromMimeType(guessedMimeType);
    if (!memeType) return;

    const fileExtension = extension(guessedMimeType);
    if (!fileExtension) return;

    const filename = `${meme.id.toHexString()}-${
      Date.now() / 1000
    }.${fileExtension}`;
    const newUri = AndroidScoped.appendPath(storageUri, filename);

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

    realm.write(() => {
      meme.filename = filename;
      meme.memeType = memeType;
      meme.mimeType = guessedMimeType;
      meme.size = size;
    });

    void resetState(newUri);
  }, [meme, realm, resetState, storageUri]);

  return (
    <>
      <Appbar.Header>
        <Appbar.BackAction onPress={() => goBack()} />
        <Appbar.Content title={'Edit Meme'} />
        <Appbar.Action
          icon={meme.isFavorite ? 'heart' : 'heart-outline'}
          onPress={() => favoriteMeme(realm, meme)}
        />
        <Appbar.Action icon="delete" onPress={handleDelete} />
      </Appbar.Header>
      <Banner
        visible={!!error}
        actions={[
          {
            label: 'Fix URI',
            onPress: handleFixUri,
          },
          {
            label: 'Delete Meme',
            onPress: handleDelete,
          },
        ]}>
        {error?.message}
      </Banner>
      <ScrollView
        contentContainerStyle={[
          editorStyles.scrollView,
          orientation === 'portrait'
            ? editorStyles.scrollViewPortrait
            : editorStyles.scrollViewLandscape,
          { backgroundColor: colors.background },
        ]}>
        <View style={editorStyles.editorView}>
          <MemeEditor
            uri={uri}
            mimeType={mimeType}
            loading={loading}
            setLoading={setLoading}
            error={error}
            setError={setError}
            staging={staging}
            setStaging={setStaging}
          />
        </View>
        <View style={editorStyles.saveButtonView}>
          <Button
            mode="contained"
            icon="floppy"
            onPress={handleSave}
            disabled={
              loading ||
              !!error ||
              isSaving ||
              !staging?.title.valid ||
              originalStaging.current === staging
            }
            loading={isSaving}
            style={editorStyles.soloSaveButton}>
            Save
          </Button>
        </View>
      </ScrollView>
    </>
  );
};

export default EditMeme;
