import { AccessToken, LoginManager } from 'react-native-fbsdk';
import firebase, { RNFirebase } from 'react-native-firebase';

import { GoogleSignin } from 'react-native-google-signin';
import { createLogic, Done } from 'redux-logic';
import { AuthActions } from '../actions';
import { Dispatch, RootState, UserBody, UserRole } from '../models';
import { Firestore, LogicDeps } from './';
const FCM = firebase.app().messaging();
const Auth = firebase.app().auth();

const authStateLogic = createLogic({
  type: 'persist/REHYDRATE',
  warnTimeout: 0,
  processOptions: {
    dispatchMultiple: true,
  },
  async process({ getState }: LogicDeps & { getState: () => RootState }, dispatch: Dispatch) {
    const cachedUser = getState().user;
    if (!cachedUser.body) {
      dispatch(AuthActions.UserUnauthorized());
    } else {
      dispatch(AuthActions.initializeUserFlow(cachedUser.body.id, cachedUser.role));
    }
    Auth.onAuthStateChanged(async (user) => {
      if (!user) {
        dispatch(AuthActions.UserUnauthorized());
      }
    });
    try {
      const doc = await Firestore.collection('config').doc('1.0').get();
      if (doc.exists && (doc.data() as any).unsupported) {
        dispatch(AuthActions.AppVersionSupportTerminated((doc.data() as any).message) || null);
      }
    } catch (error) {
      console.error(error);
    }
  },
});

const logoutLogic = createLogic({
  type: AuthActions.ActionKey.LOGOUT_REQUESTED,
  async process({ getState }: LogicDeps & { getState: () => RootState }, dispatch: Dispatch, done: Done) {
    const userId = getState().user.body.id;
    try {
      await Firestore.collection('users').doc(userId).update({ pushToken: undefined });
      await Auth.signOut();
      dispatch(AuthActions.logoutSuccess());
    } catch (error) {
      dispatch(AuthActions.logoutFail(error));
    }
    done();
  },
});

const createUserReference = (userId: string, role: UserRole) =>
  Firestore
    .collection('users')
    .doc(userId)
    .set({ role });

const emailSignupLogic = createLogic({
  type: AuthActions.ActionKey.SIGNUP_WITH_EMAIL_REQUESTED,
  async process({ action }: LogicDeps & { action: AuthActions.SignupWithEmailRequested },
    dispatch: Dispatch, done: Done) {
    try {
      const data: any = await Auth.createUserWithEmailAndPassword(action.email, action.password);
      const user: RNFirebase.User = data._user;
      await Firestore
        .collection(action.userRole)
        .doc(user.uid)
        .set({
          email: user.email,
          id: user.uid,
          name: action.name,
        });
      await createUserReference(user.uid, action.userRole);
      dispatch(AuthActions.firstLogin());
      dispatch(AuthActions.userUidResolved(user.uid, action.userRole));
    } catch (error) {
      dispatch(AuthActions.loginFail('Login failed', action.userRole));
    }
    done();
  },
});

const emailLoginLogic = createLogic({
  type: AuthActions.ActionKey.LOGIN_WITH_EMAIL_REQUESTED,
  async process({ action }: LogicDeps & { action: AuthActions.LoginWithEmailRequested },
    dispatch: Dispatch, done: Done) {
    try {
      const user = await Auth.signInWithEmailAndPassword(action.email, action.password);
      dispatch(AuthActions.userUidResolved(user.uid, action.userRole));
    } catch (error) {
      dispatch(AuthActions.loginFail(error.code, action.userRole));
    }
    done();
  },
});

const userResolvedLogic = createLogic({
  type: AuthActions.ActionKey.USER_UID_RESOLVED,
  async process({ action }: LogicDeps & { action: AuthActions.UserUidResolved }, dispatch: Dispatch, done: Done) {
    try {
      const ref = await Firestore.collection('users').doc(action.uid).get();
      if ((ref.data() as any).role !== action.userRole) {
        throw new Error('auth/wrong-role');
      }
      const snaphot = await Firestore.collection(action.userRole).doc(action.uid).get();
      dispatch(AuthActions.loginSuccess(snaphot.data() as UserBody, action.userRole));
      FCM.requestPermissions();
      const token = await FCM.getToken();
      await Firestore
        .collection('users')
        .doc(action.uid)
        .update({ pushToken: token });
    } catch (error) {
      AuthActions.loginFail(error, action.userRole);
    }
    done();
  },
});

const facebookLoginLogic = createLogic({
  type: AuthActions.ActionKey.LOGIN_WITH_FACEBOOK_REQUESTED,
  warnTimeout: 0,
  processOptions: {
    dispatchMultiple: true,
  },
  async process({ action }: LogicDeps & { action: AuthActions.LoginWithFacebookRequested },
    dispatch: Dispatch, done: Done) {
    try {
      const result = await LoginManager.logInWithReadPermissions(['public_profile', 'email']);
      if (result.isCancelled) {
        dispatch(AuthActions.loginFail('', action.userRole));
        done();
      }
      const data = await AccessToken.getCurrentAccessToken();
      if (!data) {
        throw new Error('Błąd logowania');
      }
      const credential = firebase.auth.FacebookAuthProvider.credential(data.accessToken);
      const firebaseData: any = await firebase.auth().signInWithCredential(credential);
      const user: RNFirebase.User = firebaseData._user;
      const ref = await Firestore.collection('users').doc(user.uid).get();
      if (!ref.exists) {
        dispatch(AuthActions.firstLogin());
        await createUserReference(user.uid, action.userRole);
      } else if ((ref.data() as any).role !== action.userRole) {
        throw new Error('auth/wrong-role');
      }
      await Firestore.collection(action.userRole)
        .doc(user.uid)
        .set({
          email: user.email,
          id: user.uid,
          name: user.displayName,
          avatar: `https://graph.facebook.com/${user.providerData[0].uid}/picture?height=500`,
        },
        { merge: true });
      dispatch(AuthActions.userUidResolved(user.uid, action.userRole));
    } catch (error) {
      dispatch(AuthActions.loginFail(error.message, action.userRole));
    }
    done();
  },
});

const googleLoginLogic = createLogic({
  type: AuthActions.ActionKey.LOGIN_WITH_GOOGLE_REQUESTED,
  processOptions: {
    dispatchMultiple: true,
  },
  warnTimeout: 0,
  async process({ action }: LogicDeps & { action: AuthActions.LoginWithGoogleRequested },
    dispatch: Dispatch, done: Done) {
    try {
      await GoogleSignin.configure({
        webClientId: '303470821592-br9aqhh9fa0k3oedoqr47op3fh4tpb1u.apps.googleusercontent.com',
      });
      const data = await GoogleSignin.signIn();
      const firebaseData: any = await firebase.auth().signInWithCredential(
        firebase.auth.GoogleAuthProvider.credential(data.idToken, data.accessToken));
      const user: RNFirebase.User = firebaseData._user;
      const ref = await Firestore.collection('users').doc(user.uid).get();
      if (!ref.exists) {
        dispatch(AuthActions.firstLogin());
        await createUserReference(user.uid, action.userRole);
      } else if ((ref.data() as any).role !== action.userRole) {
        throw new Error('auth/wrong-role');
      }
      await Firestore.collection(action.userRole)
        .doc(user.uid)
        .set({
          email: user.email,
          id: user.uid,
          name: user.displayName,
        },
        { merge: true });
      dispatch(AuthActions.userUidResolved(user.uid, action.userRole));
    } catch (error) {
      dispatch(AuthActions.loginFail(error.message, action.userRole));
    }
    done();
  },
});

const initializeFlowFromLogin = createLogic({
  type: AuthActions.ActionKey.LOGIN_SUCCESS,
  latest: true,
  process({ action }: LogicDeps & { action: AuthActions.LoginSuccess },
    dispatch: Dispatch, done: Done) {
    dispatch(AuthActions.initializeUserFlow(action.user.id, action.userRole));
    done();
  },
});

export const authLogic = [
  googleLoginLogic,
  facebookLoginLogic,
  emailSignupLogic,
  emailLoginLogic,
  userResolvedLogic,
  logoutLogic,
  authStateLogic,
  initializeFlowFromLogin,
];
