import Fingerprint2 from "@fingerprintjs/fingerprintjs";
import * as Sentry from "@sentry/browser";
import { notification } from "antd";
import { setUserId, setUserProperties } from "firebase/analytics";
import {
  checkActionCode,
  confirmPasswordReset,
  signInWithEmailAndPassword,
  signOut,
  sendPasswordResetEmail,
} from "firebase/auth";
import { doc } from "firebase/firestore";
import i18next from "i18next";
import LogRocket from "logrocket";
import { all, call, delay, put, select, takeLatest } from "redux-saga/effects";
import { factory } from "../../App/App.container";
import {
  analytics,
  auth,
  firestore,
  getCurrentUser,
  logImEXEvent,
  updateCurrentUser,
} from "../../firebase/firebase.utils";
import {
  checkInstanceId,
  sendPasswordResetFailure,
  sendPasswordResetSuccess,
  setAuthlevel,
  setInstanceConflict,
  setLocalFingerprint,
  signInFailure,
  signInSuccess,
  signOutFailure,
  signOutSuccess,
  unauthorizedUser,
  updateUserDetailsSuccess,
  validatePasswordResetFailure,
  validatePasswordResetSuccess,
} from "./user.actions";
import UserActionTypes from "./user.types";
import axios from "axios";
import { messaging } from "../../firebase/firebase.utils";
import { getToken } from "firebase/messaging";

export function* onEmailSignInStart() {
  yield takeLatest(UserActionTypes.EMAIL_SIGN_IN_START, signInWithEmail);
}
export function* signInWithEmail({ payload: { email, password } }) {
  try {
    logImEXEvent("redux_sign_in_attempt", { user: email });

    const { user } = yield signInWithEmailAndPassword(auth, email, password);

    yield put(
      signInSuccess({
        uid: user.uid,
        email: user.email,
        displayName: user.displayName,
        photoURL: user.photoURL,
        authorized: true,
      })
    );
  } catch (error) {
    yield put(signInFailure(error));
    logImEXEvent("redux_sign_in_failure", { user: email, error });
  }
}

export function* onCheckUserSession() {
  yield takeLatest(UserActionTypes.CHECK_USER_SESSION, isUserAuthenticated);
}
export function* isUserAuthenticated() {
  try {
    logImEXEvent("redux_auth_check");

    const user = yield getCurrentUser();
    if (!user) {
      yield put(unauthorizedUser());
      return;
    }

    LogRocket.identify(user.email);

    yield put(
      signInSuccess({
        uid: user.uid,
        email: user.email,
        displayName: user.displayName,
        photoURL: user.photoURL,
        authorized: true,
      })
    );
  } catch (error) {
    yield put(signInFailure(error));
  }
}
export function* onSignOutStart() {
  yield takeLatest(UserActionTypes.SIGN_OUT_START, signOutStart);
}
export function* signOutStart() {
  try {
    logImEXEvent("redux_sign_out");

    const state = yield select();

    //unsub from topic.

    try {
      const fcm_tokens = yield getToken(messaging);
      yield call(axios.post, "/notifications/unsubscribe", {
        fcm_tokens,
        imexshopid: state.user.bodyshop.imexshopid,
        type: "messaging",
      });
    } catch (error) {
      console.log("No FCM token. Skipping unsubscribe.");
    }

    yield signOut(auth);
    yield put(signOutSuccess());
    localStorage.removeItem("token");
  } catch (error) {
    yield put(signOutFailure(error.message));
  }
}

export function* onUpdateUserDetails() {
  yield takeLatest(UserActionTypes.UPDATE_USER_DETAILS, updateUserDetails);
}
export function* updateUserDetails(userDetails) {
  try {
    const updatedDetails = yield updateCurrentUser(userDetails.payload);

    yield put(updateUserDetailsSuccess(updatedDetails));
    notification.open({
      type: "success",
      message: i18next.t("profile.successes.updated"),
    });
  } catch (error) {
    //yield put(signOutFailure(error.message));
  }
}
export function* onSetInstanceId() {
  yield takeLatest(UserActionTypes.SET_INSTANCE_ID, setInstanceIdSaga);
}
export function* setInstanceIdSaga({ payload: uid }) {
  try {
    const userInstanceRef = doc(firestore, `userInstance/${uid}`);

    const fingerprint = Fingerprint2.x64hash128(
      (yield Fingerprint2.getPromise({})).map((c) => c.value).join(""),
      31
    );

    yield userInstanceRef.set({
      timestamp: new Date(),
      fingerprint,
    });

    yield put(setLocalFingerprint(fingerprint));
    yield delay(5 * 60 * 1000);
    if (process.env.NODE_ENV === "production") yield put(checkInstanceId(uid));
  } catch (error) {
    console.log("error", error);
  }
}

export function* onCheckInstanceId() {
  yield takeLatest(UserActionTypes.CHECK_INSTANCE_ID, checkInstanceIdSaga);
}
export function* checkInstanceIdSaga({ payload: uid }) {
  try {
    const userInstanceRef = doc(firestore, `userInstance/${uid}`);

    const snapshot = yield userInstanceRef.get();
    let fingerprint = yield select((state) => state.user.fingerprint);

    if (snapshot.data().fingerprint === fingerprint) {
      yield delay(5 * 60 * 1000);
      yield put(checkInstanceId(uid));
    } else {
      console.log("ERROR: Fingerprints do not match. Conflict detected.");
      logImEXEvent("instance_confict");
      yield put(setInstanceConflict());
    }
  } catch (error) {
    console.log("error", error);
  }
}

export function* onSignInSuccess() {
  yield takeLatest(UserActionTypes.SIGN_IN_SUCCESS, signInSuccessSaga);
}

export function* signInSuccessSaga({ payload }) {
  LogRocket.identify(payload.email);

  try {
    // window.$crisp.push(["set", "user:email", [payload.email]]);

    window.$crisp.push([
      "set",
      "user:nickname",
      [payload.displayName || payload.email],
    ]);

    window.$crisp.push(["set", "session:segments", [["user"]]]);

    Sentry.setUser({
      email: payload.email,
      username: payload.displayName || payload.email,
    });
  } catch (error) {
    console.log("Error updating Crisp settings.", error);
  }

  // if (!payload.email.includes("@imex.")) yield put(setInstanceId(payload.uid));
  setUserId(analytics, payload.email);
  setUserProperties(analytics, payload);
  yield logImEXEvent("redux_sign_in_success");
}

export function* onSendPasswordResetStart() {
  yield takeLatest(
    UserActionTypes.SEND_PASSWORD_RESET_EMAIL_START,
    sendPasswordResetEmailSaga
  );
  yield takeLatest(
    UserActionTypes.SEND_PASSWORD_RESET_EMAIL_START_AGAIN,
    sendPasswordResetEmailSaga
  );
}
export function* sendPasswordResetEmailSaga({ payload }) {
  try {
    yield sendPasswordResetEmail(auth, payload, {
      url: "https://imex.online/passwordreset",
    });

    yield put(sendPasswordResetSuccess());
  } catch (error) {
    yield put(sendPasswordResetFailure(error.message));
  }
}

export function* onValidatePasswordResetStart() {
  yield takeLatest(
    UserActionTypes.VALIDATE_PASSWORD_RESET_START,
    validatePasswordResetStart
  );
}
export function* validatePasswordResetStart({ payload: { password, code } }) {
  try {
    checkActionCode(auth, code);
    yield confirmPasswordReset(auth, code, password);
    yield put(validatePasswordResetSuccess());
  } catch (error) {
    yield put(validatePasswordResetFailure(error.message));
  }
}

export function* onSetShopDetails() {
  yield takeLatest(
    UserActionTypes.SET_SHOP_DETAILS,
    SetAuthLevelFromShopDetails
  );
}
export function* SetAuthLevelFromShopDetails({ payload }) {
  try {
    const userEmail = yield select((state) => state.user.currentUser.email);
    try {
      //console.log("Setting shop timezone.");
      // moment.tz.setDefault(payload.timezone);
    } catch (error) {
      console.log(error);
    }

    factory.client(payload.imexshopid);

    const authRecord = payload.associations.filter(
      (a) => a.useremail.toLowerCase() === userEmail.toLowerCase()
    );

    yield put(setAuthlevel(authRecord[0] ? authRecord[0].authlevel : 0));
    yield put(
      updateUserDetailsSuccess(
        authRecord[0]
          ? { validemail: authRecord[0].user.validemail }
          : { validemail: false }
      )
    );
    try {
      window.$crisp.push(["set", "user:company", [payload.shopname]]);
      if (authRecord[0] && authRecord[0].user.validemail) {
        window.$crisp.push(["set", "user:email", [authRecord[0].user.email]]);
      }
    } catch (error) {
      console.error("Couldnt find $crisp.");
    }
  } catch (error) {
    yield put(signInFailure(error.message));
  }
}

export function* userSagas() {
  yield all([
    call(onEmailSignInStart),
    call(onCheckUserSession),
    call(onSignOutStart),
    call(onUpdateUserDetails),
    call(onSetInstanceId),
    call(onCheckInstanceId),
    call(onSignInSuccess),
    call(onSendPasswordResetStart),
    call(onValidatePasswordResetStart),
    call(onSetShopDetails),
  ]);
}
