import FingerprintJS from "@fingerprintjs/fingerprintjs";
import { setUserId, setUserProperties } from "@firebase/analytics";
import {
  checkActionCode,
  confirmPasswordReset,
  sendPasswordResetEmail,
  signInWithEmailAndPassword,
  signOut
} from "@firebase/auth";
import { arrayUnion, doc, getDoc, setDoc, updateDoc } from "@firebase/firestore";
import { getToken } from "@firebase/messaging";
import * as Sentry from "@sentry/react";
import { notification } from "antd";
import axios from "axios";
import i18next from "i18next";
import LogRocket from "logrocket";
import { all, call, delay, put, select, takeLatest } from "redux-saga/effects";
import {
  analytics,
  auth,
  firestore,
  getCurrentUser,
  logImEXEvent,
  messaging,
  updateCurrentUser
} from "../../firebase/firebase.utils";
import { QUERY_EULA } from "../../graphql/bodyshop.queries";
import cleanAxios from "../../utils/CleanAxios";
import client from "../../utils/GraphQLClient";
import dayjs from "../../utils/day";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
import {
  checkInstanceId,
  sendPasswordResetFailure,
  sendPasswordResetSuccess,
  setAuthlevel,
  setImexShopId,
  setInstanceConflict,
  setInstanceId,
  setLocalFingerprint,
  setPartsManagementOnly,
  signInFailure,
  signInSuccess,
  signOutFailure,
  signOutSuccess,
  unauthorizedUser,
  updateUserDetailsSuccess,
  validatePasswordResetFailure,
  validatePasswordResetSuccess
} from "./user.actions";
import UserActionTypes from "./user.types";
import posthog from "posthog-js";
import { bodyshopHasDmsKey, determineDMSTypeByBodyshop, DMS_MAP } from "../../utils/dmsUtils";

const fpPromise = FingerprintJS.load();

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 {
    const user = yield getCurrentUser();
    if (!user) {
      yield put(unauthorizedUser());
      return;
    }

    LogRocket.identify(user.email);
    //amplitude.setUserId(user.email);
    posthog.identify(user.email);

    const eulaQuery = yield client.query({
      query: QUERY_EULA,
      variables: {
        now: dayjs()
      }
    });

    const eulaIsAccepted = eulaQuery.data.eulas.length > 0 && eulaQuery.data.eulas[0].eula_acceptances.length > 0;

    yield put(
      signInSuccess({
        uid: user.uid,
        email: user.email,
        displayName: user.displayName,
        photoURL: user.photoURL,
        authorized: true,
        eulaIsAccepted,
        currentEula: eulaIsAccepted ? null : eulaQuery.data.eulas[0]
      })
    );
  } 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"
      });
      //amplitude.reset();
    } catch {
      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.success({
      title: i18next.t("profile.successes.updated")
    });
  } catch {
    //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}`);

    // Get the visitor identifier when you need it.
    const fp = yield fpPromise;
    const result = yield fp.get();
    const res = yield cleanAxios.get("https://api.ipify.org/?format=json");
    const udoc = yield getDoc(userInstanceRef);

    if (!udoc.data()) {
      yield setDoc(userInstanceRef, {
        timestamp: new Date(),
        fingerprint: result.visitorId,
        //totalFingerprint: result,
        ip: [res.data.ip]
      });
    } else {
      yield updateDoc(userInstanceRef, {
        timestamp: new Date(),
        fingerprint: result.visitorId,
        //totalFingerprint: result,
        ip: arrayUnion(res.data.ip)
      });
    }

    yield put(setLocalFingerprint(result.visitorId));
    yield delay(5 * 60 * 1000);
    if (import.meta.env.PROD) 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 snapshot = yield getDoc(doc(firestore, `userInstance/${uid}`));
    let fingerprint = yield select((state) => state.user.fingerprint);
    yield put(setInstanceConflict());
    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:nickname", [payload.displayName || payload.email]]);

    InstanceRenderManager({
      executeFunction: true,
      args: [],
      rome: () => {
        window.$zoho.salesiq.visitor.name(payload.displayName || payload.email);
        window.$zoho.salesiq.visitor.email(payload.email);
      }
    });

    // Hide Crisp if currently in parts-entry mode (pre-shop-details)
    try {
      const state = yield select();
      const isParts = state?.application?.isPartsEntry === true;
      const instanceSeg = InstanceRenderManager({
        imex: ["imex-online-user", "imex"],
        rome: ["rome-online-user", "rome"]
      });
      // Always ensure segments include instance + user, and append partsManagement if applicable
      const segs = [
        ...instanceSeg,
        ...(isParts
          ? [
              InstanceRenderManager({
                imex: "ImexPartsManagement",
                rome: "RomePartsManagement"
              })
            ]
          : [])
      ];
      window.$crisp?.push(["set", "session:segments", [segs]]);
      if (isParts) {
        window.$crisp?.push(["do", "chat:hide"]);
      }
    } catch {
      // no-op
    }
  } catch (error) {
    console.log("Error updating Crisp settings.", error);
  }

  try {
    Sentry.setUser({
      email: payload.email,
      username: payload.displayName || payload.email
    });
  } catch (error) {
    console.log("Error setting Sentry user.", error);
  }

  setUserId(analytics, payload.email);
  setUserProperties(analytics, payload);
  yield;
}

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: InstanceRenderManager({
        imex: "https://imex.online/passwordreset",
        rome: "https://romeonline.io/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 {
      dayjs.tz.setDefault(payload.timezone);
    } catch (error) {
      console.log(error);
    }

    // Dispatch the imexshopid to Redux store
    yield put(setImexShopId(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 })
    );

    const user = yield select((state) => state.user.currentUser);
    if (payload.features.singleDeviceOnly) {
      if (!(user.email.includes("@imex.") || user.email.includes("@rome.") || user.email.includes("@rometech.")))
        yield put(setInstanceId(user.uid));
    }

    try {
      //amplitude.setGroup('Shop', payload.shopname);
      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]]);
      }

      // Build consolidated Crisp segments including instance, region, features, and parts mode
      const isParts = yield select((state) => state.application.isPartsEntry === true);
      const instanceSeg = InstanceRenderManager({
        imex: ["imex-online-user", "imex"],
        rome: ["rome-online-user", "rome"]
      });

      const featureSegments =
        payload.features?.allAccess === true
          ? ["allAccess"]
          : [
              "basic",
              ...Object.keys(payload.features).filter(
                (key) =>
                  payload.features[key] === true ||
                  (typeof payload.features[key] === "string" && !isNaN(Date.parse(payload.features[key])))
              )
            ];

      const hasDmsKey = bodyshopHasDmsKey(payload);
      const dmsType = hasDmsKey ? determineDMSTypeByBodyshop(payload) : null;

      const additionalSegments = [
        dmsType === DMS_MAP.cdk && DMS_MAP.cdk.toUpperCase(),
        dmsType === DMS_MAP.pbs && DMS_MAP.pbs.toUpperCase(),
        dmsType === DMS_MAP.reynolds && DMS_MAP.reynolds.toUpperCase(),
        payload.accountingconfig?.qbo === true && "QBO",
        payload.accountingconfig?.qbo === false && !hasDmsKey && "QBD"
      ].filter(Boolean);

      featureSegments.push(...additionalSegments);

      const regionSeg = payload.region_config ? `region:${payload.region_config}` : null;
      const segments = [...instanceSeg, ...(regionSeg ? [regionSeg] : []), ...featureSegments];
      if (isParts) {
        segments.push(InstanceRenderManager({ imex: "ImexPartsManagement", rome: "RomePartsManagement" }));
      }

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

      // Hide/show Crisp chat based on parts mode or features
      window.$crisp?.push(["do", isParts ? "chat:hide" : "chat:show"]);

      InstanceRenderManager({
        executeFunction: true,
        args: [],
        rome: () => {
          window.$zoho.salesiq.visitor.info({ "Shop Name": payload.shopname });
        }
      });
    } catch (error) {
      console.warn("Couldnt find $crisp.", error.message);
    }

    yield put(setPartsManagementOnly(payload.features.partsManagementOnly));
  } 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)
  ]);
}
