import * as SecureStore from 'expo-secure-store';
import * as WebBrowser from 'expo-web-browser';
import ky from 'ky';
import propTypes from 'prop-types';
import React from 'react';
import { Alert, Platform } from 'react-native';
import { useQueryClient } from 'react-query';
import * as Sentry from 'sentry-expo';
import config from '../config';
import { useAsyncError, useSecureState } from '../utilities';
import useFacebookProvider from './providers/facebook';
import useGoogleProvider from './providers/google';
import useGoogleProviderForStandalone from './providers/google-standalone';
import useUtahIDProvider from './providers/utahid';

export const STATUS = {
  loading: 'loading',
  success: 'success',
  failure: 'failure',
  idle: 'idle',
};

WebBrowser.maybeCompleteAuthSession();
const AuthContext = React.createContext();

export function AuthContextProvider({ children, onReady }) {
  const [authInfo, setAuthInfo] = useSecureState(config.USER_STORE_KEY);
  // has the following shape:
  // {
  //    oauthUser: object as returned from the oauth request,
  //    providerName: string,
  //    user: object as returned from the login api endpoint,
  //    registered: boolean
  // }

  const [userType, setUserType] = useSecureState(config.USER_TYPE_KEY);
  const [status, setStatus] = React.useState(STATUS.idle);
  const facebookProvider = useFacebookProvider();
  const googleProvider = __DEV__ || Platform.OS === 'ios' ? useGoogleProvider() : useGoogleProviderForStandalone();
  const utahidProvider = useUtahIDProvider();
  // provider should have the following shape:
  // {
  //   logIn: resolves with oauthUser object,
  //   logOut: resolves with null,
  //   getBearerToken: resolves with token, (utahid only)
  // }

  const PROVIDER_LOOKUP = {
    facebook: facebookProvider,
    google: googleProvider,
    utahid: utahidProvider,
  };
  const currentProvider = React.useRef(null);
  const throwAsyncError = useAsyncError();

  React.useEffect(() => {
    // best practice to speed up browser for android
    // ref: https://docs.expo.io/guides/authentication/#warming-the-browser
    WebBrowser.warmUpAsync();

    return () => {
      WebBrowser.coolDownAsync();
    };
  }, []);

  React.useEffect(() => {
    if (authInfo === null || authInfo) {
      if (authInfo) {
        console.log('authInfo', authInfo);
        currentProvider.current = PROVIDER_LOOKUP[authInfo.providerName];
      }
      onReady();
    }
  }, [authInfo]);

  const logIn = async (providerName) => {
    setStatus(STATUS.loading);

    try {
      const oauthUser = await PROVIDER_LOOKUP[providerName].logIn();

      let token;
      if (oauthUser) {
        token = await PROVIDER_LOOKUP[providerName].getBearerToken();
      } else {
        // user cancelled login
        setStatus(STATUS.failure);

        setAuthInfo({
          user: null,
          registered: false,
        });

        return { success: false, registered: false };
      }

      const loginResponse = await ky
        .post(`${config.API}/user/login`, {
          json: {
            auth_id: oauthUser.sub,
            auth_provider: providerName,
            email: oauthUser.email,
            first_name: oauthUser.given_name,
            last_name: oauthUser.family_name,
          },
          headers: {
            Authorization: token,
          },
        })
        .json();
      const { user, registered } = loginResponse;

      setAuthInfo({
        oauthUser,
        user,
        providerName,
        registered,
      });

      setStatus(STATUS.success);

      return { success: true, registered };
    } catch (error) {
      console.log(`error logging in: ${error?.message}`);
      Sentry.Native.captureException(error);
      setStatus(STATUS.failure);

      throw error;
    }
  };

  const registerUser = async (userData) => {
    const token = await getBearerToken();
    const responseJson = await ky
      .post(`${config.API}/user/register`, {
        json: userData,
        headers: {
          Authorization: token,
        },
      })
      .json();

    setAuthInfo({
      ...authInfo,
      user: responseJson.newUser,
      registered: true,
    });
  };

  const queryClient = useQueryClient();
  const logOut = (skipConfirm = false) => {
    return new Promise((resolve) => {
      const doLogOut = async () => {
        setStatus(STATUS.loading);

        if (currentProvider.current.hasValidToken()) {
          try {
            const response = await ky.post(`${config.API}/user/logout`, {
              headers: {
                Authorization: await getBearerToken(),
              },
            });

            if (response.status !== 200) {
              console.error(`logout failed: ${response.body}`);
            }
          } catch (error) {
            console.error(`logout failed: ${error.message}`);
          }
        }

        try {
          await currentProvider.current.logOut();
        } catch (error) {
          console.error(`provider logout failed: ${error.message}`);
        }

        setAuthInfo(null);

        await SecureStore.deleteItemAsync(config.USER_STORE_KEY);
        await SecureStore.deleteItemAsync(config.USER_TYPE_KEY);

        queryClient.clear();

        setStatus(STATUS.idle);

        resolve(true);
      };

      if (skipConfirm) {
        return doLogOut();
      }

      Alert.alert(
        'Are you sure?',
        null,
        [
          {
            text: 'Cancel',
            style: 'cancel',
            onPress: () => resolve(false),
          },
          {
            text: 'Logout',
            onPress: doLogOut,
          },
        ],
        {
          cancelable: true,
          onDismiss: () => resolve(false),
        }
      );
    });
  };

  const getBearerToken = async () => {
    console.log('context/getBearerToken');

    if (!authInfo) {
      throw new Error('user is not logged in!');
    }

    try {
      return await currentProvider.current.getBearerToken();
    } catch (error) {
      throwAsyncError(error);
    }
  };

  return (
    <AuthContext.Provider
      value={{ authInfo, logIn, logOut, status, getBearerToken, setUserType, userType, registerUser }}
    >
      {children}
    </AuthContext.Provider>
  );
}
AuthContextProvider.propTypes = {
  children: propTypes.object,
  onReady: propTypes.func,
};

export default function useAuth() {
  const context = React.useContext(AuthContext);

  if (!context) {
    throw new Error('AuthContext is required');
  }

  return context;
}

@@ -1862,53 +1862,116 @@
  %7D;%0A
+%0A
   
-const currentProvider = React.useRef(null);
+let currentProvider;%0A  if (authInfo) %7B%0A    currentProvider = PROVIDER_LOOKUP%5BauthInfo.providerName%5D;%0A  %7D%0A
 %0A  c
@@ -2339,155 +2339,8 @@
 ) %7B%0A
-      if (authInfo) %7B%0A        console.log('authInfo', authInfo);%0A        currentProvider.current = PROVIDER_LOOKUP%5BauthInfo.providerName%5D;%0A      %7D%0A
     
@@ -4320,24 +4320,16 @@
 rovider.
-current.
 hasValid
@@ -4823,24 +4823,16 @@
 rovider.
-current.
 logOut()
@@ -5824,24 +5824,24 @@
 %0A%0A    try %7B%0A
+
       return
@@ -5867,16 +5867,8 @@
 der.
-current.
 getB

make sure that we always have a fresh version of the current auth provider