import React, {useEffect, useState} from 'react';
import {Linking, BackHandler} from 'react-native';
import VersionCheck from 'react-native-version-check';
import * as Sentry from '@sentry/react-native';
import {Severity} from '@sentry/types';
import AsyncStorage from '@react-native-async-storage/async-storage';
import {QueryClient, QueryClientProvider} from 'react-query';
import SplashScreen from 'react-native-splash-screen';
import {SafeAreaProvider} from 'react-native-safe-area-context';
import {NavigationContainer} from '@react-navigation/native';
import Config from 'react-native-config';
import Loader from '@components/Loader';
import Navigation from '@navigation/index';
import {TncProvider} from '@contexts/tnc';
import AppUpdateModal from '@components/AppUpdateModal';
import useUser, {UserProvider, ACCESS_TOKEN} from '@contexts/user';
import {
  issuanceServiceInstance,
  cloudWalletServiceInstance,
} from '@services/Axios';
import {LoaderProvider} from '@contexts/loader';
import {SENTRY_DSN, ENV} from '@constants/api';

const queryClient = new QueryClient();
type getDescriptionTypes = {
  optional: string;
  mandatory: string;
};

const getDescription: getDescriptionTypes = {
  optional: 'Asli wallet recommends that you update to the latest version.',
  mandatory: 'An update to Asli wallet is required to continue.',
};

const getUpdateTitle = {
  optional: 'Update Asli?',
  mandatory: 'Update required',
};

const Wrapper = () => {
  const {getAccessToken, user, logout, isTokenExpired, getPhoneNumber} =
    useUser();

  useEffect(() => SplashScreen.hide(), []);

  useEffect(() => {
    (async () => {
      if (!user?.accessToken) {
        await getAccessToken();
      } else {
        const tokenExpired = await isTokenExpired();
        const phoneNumber = await getPhoneNumber();

        if (!!user?.accessToken && tokenExpired) {
          Sentry.captureMessage(
            `Logout fn called, Mobile Number : ${phoneNumber} / Access Token : ${user?.accessToken} / tokenExpired : ${tokenExpired} `,
            Severity.Log,
          );
          await logout();
          return;
        }

        // Add a Issuance request interceptor
        issuanceServiceInstance.interceptors.request.use(
          async config => {
            const value = await AsyncStorage.getItem(ACCESS_TOKEN);

            if (config?.headers && !!value) {
              config.headers.Authorization = value;
            }
            return config;
          },
          error => Promise.reject(error),
        );

        // Add a Issuance response interceptor
        issuanceServiceInstance.interceptors.response.use(
          response => response,
          async error => {
            if (error?.response?.status === 401) {
              Sentry.captureMessage(
                `Logout fn called, Mobile Number : ${phoneNumber} / Access Token : ${
                  user?.accessToken
                } / tokenExpired : ${tokenExpired} / Error : ${JSON.stringify(
                  error?.response,
                )}`,
                Severity.Error,
              );
              await logout();
              return;
            }

            return Promise.reject(error);
          },
        );

        // Add a  Cloud Service request interceptor
        cloudWalletServiceInstance.interceptors.request.use(
          async config => {
            const value = await AsyncStorage.getItem(ACCESS_TOKEN);

            if (config?.headers && !!value) {
              config.headers.Authorization = `Bearer ${value}`;
            }
            return config;
          },
          error => Promise.reject(error),
        );

        // Add a response interceptor
        cloudWalletServiceInstance.interceptors.response.use(
          response => response,
          async error => {
            if (error?.response?.status === 401) {
              Sentry.captureMessage(
                `Cloud Wallet API 401,Logout fn called, Mobile Number : ${phoneNumber} / Access Token : ${
                  user?.accessToken
                } / tokenExpired : ${tokenExpired} / Error : ${JSON.stringify(
                  error?.response,
                )}`,
                Severity.Error,
              );
              await logout();
              return;
            }

            return Promise.reject(error);
          },
        );
      }
    })();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [user?.accessToken]);

  return (
    <React.Fragment>
      <Navigation />
      <Loader />
    </React.Fragment>
  );
};

const App = () => {
  const [showUpdateModal, setshowUpdateModal] = useState<boolean>(false);
  const updateType: string = Config.APP_STORE_UPDATE_TYPE;
  useEffect(() => {
    Sentry.init({
      dsn: SENTRY_DSN,
      tracesSampleRate: 1.0,
      environment: ENV,
    });
  }, []);

  useEffect(() => {
    const checkLatestVersion = async () => {
      const isUpdateRequired = await VersionCheck.needUpdate({
        forceUpdate: true,
      });
      if (isUpdateRequired.isNeeded) {
        setshowUpdateModal(true);
      }
    };

    checkLatestVersion();
  }, []);

  const updateApp = async () => {
    const isUpdateRequired = await VersionCheck.needUpdate();
    Linking.openURL(isUpdateRequired.storeUrl);
  };

  const hideUpdateModal = async () => {
    //Exit, if update is mandatory
    if (updateType !== 'optional') {
      BackHandler.exitApp();
    }
    setshowUpdateModal(false);
  };

  return (
    <QueryClientProvider client={queryClient}>
      <SafeAreaProvider>
        <NavigationContainer>
          <LoaderProvider>
            <TncProvider>
              <UserProvider>
                <Wrapper />
                <AppUpdateModal
                  visible={showUpdateModal}
                  description={
                    getDescription[updateType as keyof typeof getDescription]
                  }
                  type={updateType}
                  hideModal={hideUpdateModal}
                  title={
                    getUpdateTitle[updateType as keyof typeof getUpdateTitle]
                  }
                  updateApp={updateApp}
                />
              </UserProvider>
            </TncProvider>
          </LoaderProvider>
        </NavigationContainer>
      </SafeAreaProvider>
    </QueryClientProvider>
  );
};

export default Sentry.wrap(App);
