import { useState } from 'react';
import { ScrollView, TouchableOpacity } from 'react-native';
import { Center, Heading, Text, useToast, VStack } from '@gluestack-ui/themed';
import * as ImagePicker from 'expo-image-picker';
import * as FileSystem from 'expo-file-system';
import * as yup from 'yup';
import { Controller, useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';

import defaultAvatar from '@assets/userPhotoDefault.png';

import { api } from '@services/api';
import { AppError } from '@utils/AppError';
import { slug } from '@utils/Slug';

import { useAuth } from '@hooks/useAuth';

import { Button } from '@components/Button';
import { Input } from '@components/Input';
import { ScreenHeader } from '@components/ScreenHeader';
import { UserPhoto } from '@components/UserPhoto';
import { ToastMessage } from '@components/ToastMessage';

type ProfileForm = {
  name: string;
  email: string;
  old_password: string;
  new_password: string;
  confirm_password: string;
};

const profileSchema = yup.object({
  name: yup.string().required('Informe o nome.'),
  new_password: yup
    .string()
    .min(8, 'Senha deve ter no mínimo 8 caracteres.')
    .nullable()
    .transform((value) => (!!value ? value : null)),
  confirm_password: yup
    .string()
    .nullable()
    .transform((value) => (!!value ? value : null))
    .oneOf([yup.ref('new_password'), null], 'Senhas não coincidem.')
    .when('new_password', {
      is: (field: any) => field,
      then: yup
        .string()
        .nullable()
        .required('Senhas não coincidem')
        .transform((value) => (!!value ? value : null)),
    }),
});

export function Profile() {
  const [isUpdating, setIsUpdating] = useState(false);

  const toast = useToast();
  const { user, updateUserData } = useAuth();

  const {
    control,
    handleSubmit,
    formState: { errors },
  } = useForm<ProfileForm>({
    defaultValues: {
      name: user.name,
      email: user.email,
      old_password: '',
      new_password: '',
      confirm_password: '',
    },
    resolver: yupResolver(profileSchema),
  });

  async function handleProfileUpdate(data: ProfileForm) {
    try {
      setIsUpdating(true);

      await api.put('/users', {
        name: data.name,
        password: data.new_password,
        old_password: data.old_password,
      });

      const updatedUserData = user;
      updatedUserData.name = data.name;
      updateUserData(updatedUserData);

      toast.show({
        placement: 'top',
        render: ({ id }) => (
          <ToastMessage
            id={id}
            title="Perfil atualizado com sucesso!"
            action="success"
            onClose={() => toast.close(id)}
          />
        ),
      });
    } catch (error) {
      const isAppError = error instanceof AppError;
      const title = isAppError ? error.message : 'Não foi possível atualizar.';
      toast.show({
        placement: 'top',
        render: ({ id }) => (
          <ToastMessage
            id={id}
            title={title}
            description="Tente novamente mais tarde."
            action="error"
            onClose={() => toast.close(id)}
          />
        ),
      });
    } finally {
      setIsUpdating(false);
    }
  }

  async function handleUserPhotoSelection() {
    try {
      const photoSelection = await ImagePicker.launchImageLibraryAsync({
        mediaTypes: ImagePicker.MediaTypeOptions.Images,
        quality: 1,
        aspect: [4, 4],
        allowsEditing: true,
      });

      if (photoSelection.canceled) {
        return;
      }

      const photoURI = photoSelection.assets[0].uri;
      if (photoURI) {
        // for some reason type `FileInfo` does not show `size` field
        const photoInfo = (await FileSystem.getInfoAsync(photoURI)) as {
          size: number;
        };

        if (photoInfo.size && photoInfo.size > 5242880) {
          return toast.show({
            placement: 'top',
            render: ({ id }) => (
              <ToastMessage
                id={id}
                action="error"
                onClose={() => toast.close(id)}
                title="Imagem muito grande!"
                description="Por favor, escolha uma de até 5MB"
              />
            ),
          });
        }

        const fileExtension = photoSelection.assets[0].uri.split('.').pop();
        const photoFile = {
          name: `${slug(user.name)}.${fileExtension}`.toLowerCase(),
          uri: photoSelection.assets[0].uri,
          type: `${photoSelection.assets[0].type}/${fileExtension}`,
        } as any;

        const userPhotoUploadForm = new FormData();
        userPhotoUploadForm.append('avatar', photoFile);

        const avatarUpdateResponse = await api.patch('/users/avatar', userPhotoUploadForm, {
          headers: {
            'Content-Type': 'multipart/form-data',
          },
        });

        const userUpdated = user;
        userUpdated.avatar = avatarUpdateResponse.data.avatar;
        updateUserData(userUpdated);

        toast.show({
          placement: 'top',
          render: ({ id }) => (
            <ToastMessage
              id={id}
              title="Foto do perfil atualizada com sucesso!"
              action="success"
              onClose={() => toast.close(id)}
            />
          ),
        });
      }
    } catch (error) {
      console.error(error);
    }
  }

  return (
    <VStack flex={1}>
      <ScreenHeader title="Perfil" />

      <ScrollView contentContainerStyle={{ paddingHorizontal: 36 }}>
        <Center mt="$6" px="$10">
          <UserPhoto
            source={
              user.avatar ? { uri: `${api.defaults.baseURL}/avatar/${user.avatar}` } : defaultAvatar
            }
            alt="imagem de perfil"
            size="xl"
          />

          <TouchableOpacity onPress={handleUserPhotoSelection}>
            <Text color="$green500" fontFamily="$heading" fontSize="$md" mt="$2" mb="$8">
              Alterar foto
            </Text>
          </TouchableOpacity>

          <Center w="$full" gap="$4">
            <Controller
              control={control}
              name="name"
              render={({ field: { onChange, value } }) => (
                <Input
                  placeholder="Nome"
                  bg="$gray600"
                  onChangeText={onChange}
                  value={value}
                  errorMessage={errors.name?.message}
                />
              )}
            />

            <Controller
              control={control}
              name="email"
              render={({ field: { onChange, value } }) => (
                <Input bg="$gray600" isReadOnly onChangeText={onChange} value={value} />
              )}
            />
          </Center>

          <Heading alignSelf="flex-start" color="$gray200" fontSize="$md" mt="$12" mb="$2">
            Alterar senha
          </Heading>

          <Center w="$full" gap="$4">
            <Controller
              control={control}
              name="old_password"
              render={({ field: { onChange } }) => (
                <Input
                  placeholder="Senha antiga"
                  bg="$gray600"
                  secureTextEntry
                  onChangeText={onChange}
                />
              )}
            />

            <Controller
              control={control}
              name="new_password"
              render={({ field: { onChange } }) => (
                <Input
                  placeholder="Nova senha"
                  bg="$gray600"
                  secureTextEntry
                  onChangeText={onChange}
                  errorMessage={errors.new_password?.message}
                />
              )}
            />

            <Controller
              control={control}
              name="confirm_password"
              render={({ field: { onChange } }) => (
                <Input
                  placeholder="Confirme a nova senha"
                  bg="$gray600"
                  secureTextEntry
                  onChangeText={onChange}
                  errorMessage={errors.confirm_password?.message}
                />
              )}
            />

            <Button
              title="Atualizar"
              mt={4}
              onPress={handleSubmit(handleProfileUpdate)}
              isLoading={isUpdating}
            />
          </Center>
        </Center>
      </ScrollView>
    </VStack>
  );
}
