import { ExchangeRateDefault } from "constants/exchange-rate";
import {
  CancelInviteCollaboratorRequest,
  InviteCollaboratorRequest,
  ResendInviteCollaboratorRequest,
  RevokeInviteCollaboratorRequest,
} from "models/talent/talent-profile-collaborator.model";
import {
  autoSaveProfileRequest,
  CreateTalentProfileModuleRequest,
  DeleteLocalizationRequest,
  publishProfileRequest,
  ReorderTalentProfileModuleRequest,
  TalentModuleMixItem,
  TalentProfileModule,
  UpdateTalentProfileModuleRequest,
} from "models/talent/talent-profile-module.model";
import moment from "moment";
import { Action } from "redux-actions";
import { SagaIterator } from "redux-saga";
import { delay, select } from "redux-saga/effects";
import { RootState } from "redux/rootReducer";
import { GetAnalyticRequest, TalentProfileRequest } from "redux/Talent/types";
import {
  authService,
  profileGetter,
  ryeShopifyService,
  spotifyService,
  talentService,
  userService,
  salesService,
  getDigitalProductService,
} from "services";
import { call, put, takeLatest } from "typed-redux-saga";
import {
  authorizationTokenCalendarProviderActions,
  autoSaveProfileActions,
  cancelInviteCollaboratorActions,
  checkUserExistedActions,
  connectCalendarProviderActions,
  createTalentProfileModuleActions,
  deleteLocalizationActions,
  deleteTalentProfileModuleActions,
  disconnectCalendarProviderActions,
  forceSaveAndLogoutActions,
  forceSaveAndpublishProfileActions,
  getAnalyticsLocalizationModulesActions,
  getAvailabilityCalendarsActions,
  getCollaboratorInvitationsActions,
  getCollaboratorProfileActions,
  getExchangeRateActions,
  getExchangeRateUSDActions,
  getLocalizationModulesActions,
  getSpotifyUserTokenActions,
  getTalentAnalyticActions,
  getTalentAnalyticMonthlyActions,
  getUserProfileActions,
  inviteCollaboratorActions,
  logoutActions,
  publishProfileActions,
  reorderTalentProfileModuleActions,
  resendInviteCollaboratorActions,
  revokeInviteCollaboratorActions,
  sendEmailOTPActions,
  sendOTPActions,
  setIsBlockingEditActions,
  setTalentProfileModuleListActions,
  setUploadingProductsActions,
  setUserCustomerActions,
  updateCollaboratorProfileActions,
  updatePasswordActions,
  updateSpotifyUserActions,
  updateTalentProfileActions,
  updateTalentProfileModuleActions,
  updateUserProfileActions,
} from "./actions";
import {
  CALENDAR_PROVIDERS,
  CheckUserExistedRequest,
  Flagged,
  LocalizationItem,
  LogoutRequest,
  SendEmailOTPRequest,
  SendPhoneOTPRequest,
  SpotifyUser,
  UpdatePasswordRequest,
  User,
} from "./types";

import { v4 as uuidv4 } from "uuid";

import { setShopifyStoreAction } from "redux/Shopify/actions";
import config from "config";
import { setRyeShopifyStoreAction } from "redux/RyeShopify/actions";
import omit from "lodash/omit";

import { FLAGS, isOn } from "@komi-app/flags-sdk";
import { DigitalProductItem } from "@komi-app/profiles-sdk";
import { ModuleType } from "@komi-app/shared-types";
import { GroupItem } from "../../models/talent/talent-profile-module.model";

function* logout(actions?: Action<LogoutRequest>) {
  try {
    if (actions?.payload?.waiting) {
      yield delay(actions.payload.waiting);
    }

    yield* call(authService.logout);
    yield* call(spotifyService.logout);
    yield* put(logoutActions.SUCCESS());

    if (actions?.payload?.force) {
      if (isOn(FLAGS.FIX_SB_642_REDIRECT_SIGNOUT_FROM_TALENT)) {
        window.location.href = `${config.client.url}/signout`;
      } else {
        window.location.href = `${config.client.url}?action=LOGOUT`;
      }
    }
  } catch (error) {
    yield* put(logoutActions.FAILURE("Cannot logout"));
  }
}

function* getUserProfile(actions?: Action<Flagged>) {
  // FIXME: types
  // const userResult: any = yield* call(authService.getUserProfile);
  // yield* call(() => growthBook.loadFeatures)

  const userResult: any = yield* call(profileGetter);

  if (userResult?.ok) {
    const user = userResult.response;

    if (user?.roles) {
      yield* put(getUserProfileActions.SUCCESS(user));
      const modules = (
        user?.talentProfile?.versions?.[0]?.modules ||
        user?.talentProfile?.modules ||
        []
      ).map((module: any) => ({ ...module, id: module.id || uuidv4() })); // FIXME: client should not create ids
      yield* put(
        setTalentProfileModuleListActions({
          modules,
          prevModules: modules,
          defaultModules: modules,
        })
      );
    }

    if (user?.talentProfile?.shopifyMerchants) {
      const stores = [];
      for (const [key, value] of Object.entries(
        user?.talentProfile?.shopifyMerchants
      )) {
        const { name, order, accessToken, connected }: any = value;
        stores.push({
          accessToken: accessToken,
          name: name,
          order: order,
          domain: key,
          connected,
        });
      }
      yield* put(setShopifyStoreAction(stores));
    }

    const talentProfileId = user?.talentProfile?.id;
    if (talentProfileId) {
      const connectedStores = yield* call(
        ryeShopifyService().getRyeStores,
        talentProfileId
      );
      const stores = connectedStores.map((store) => ({
        name: store.name,
        domain: store.shop,
        connected: store.connected,
      }));

      yield* put(setRyeShopifyStoreAction(stores));
    }

    const hasCustomer = user?.roles.some(({ id, name }: any) => id === 6);

    if (hasCustomer && isOn(FLAGS.FEAT_AIR_649_REDEEM_DISCOUNT)) {
      yield* put(setUserCustomerActions.REQUEST());

      try {
        const customer = yield* call(
          salesService().getCustomerInfo
        );

        yield* put(setUserCustomerActions.SUCCESS(customer));
      } catch (error) {
        yield* put(setUserCustomerActions.FAILURE(error));
      }
    }
  } else {
    yield* put(getUserProfileActions.FAILURE(userResult.message));

    // TODO: landing message (cf. UAM google redirect)
    if (isOn(FLAGS.FIX_SB_642_REDIRECT_SIGNOUT_FROM_TALENT)) {
      window.location.href = `${config.client.url}/signout`;
    } else {
      window.location.href = `${config.client.url}?action=LOGOUT`;
    }
  }
}

function* getCollaboratorProfile() {
  const userResult: any = yield* call(authService.getUserProfile);
  if (userResult?.ok) {
    const user = userResult.response;
    yield* put(getCollaboratorProfileActions.SUCCESS(user));
  } else {
    yield* put(getCollaboratorProfileActions.FAILURE(userResult.message));
  }
}

//FIXME : currently returns the profile user/ auth user, replacing the UserState which has auth user data to profile user.
// resulting in the UserState holding different kinds of data in journey {AuthUser, ProfileUser}
function* updateTalentProfile({ payload }: Action<TalentProfileRequest>) {
  // let result: any;
  // if (
  //   authService.talentProfileId &&
  //   typeof authService.talentProfileId === "string"
  // ) {
  //   result = yield* call(
  //     talentService().updateTalentProfileById,
  //     authService.talentProfileId,
  //     payload
  //   );
  // } else {

  //TODO: Update type?
  let updatedPayload: any = payload;
  const profileServiceRouting = isOn(FLAGS.PROFILE_SERVICE_ROUTING);

  if (!profileServiceRouting) {
    //if the FF is off, omit the new properties
    updatedPayload = omit(updatedPayload, ["updateProperty", "UserState"]);
  }

  const result: any = yield* call(talentService().updateTalentProfile, {
    ...updatedPayload,
  });

  // }
  if (result.ok) {
    yield* put(updateTalentProfileActions.SUCCESS(result.response));
    payload?.onSuccess?.(result.response);
  } else {
    yield* put(updateTalentProfileActions.FAILURE(result.message));
    payload?.onError?.();
  }
}

function* getSpotifyUserToken({}: Action<any>) {
  const result: any = yield* call(userService.spotifyRefreshToken);
  if (result.ok) {
    yield* put(updateSpotifyUserActions.REQUEST(result.response)); // go to update
  }
}

function* updateSpotifyUser({ payload }: Action<SpotifyUser>) {
  yield* call(spotifyService.signin, payload);
  // run callback update token
  if (payload.callback && typeof payload.callback === "function") {
    payload.callback();
  }
}

function* updatePassword({ payload }: Action<UpdatePasswordRequest>) {
  const result: any = yield* call(userService.updatePassword, payload);
  if (result.ok) {
    yield* put(updatePasswordActions.SUCCESS(result.response));
  } else {
    yield* put(updatePasswordActions.FAILURE(result.message));
  }
}

function* getTalentAnalytic({ payload }: Action<GetAnalyticRequest>) {
  const result: any = yield* call(talentService().getTalentAnalytic, payload);
  if (result.ok) {
    yield* put(getTalentAnalyticActions.SUCCESS(result.response));
  } else {
    yield* put(getTalentAnalyticActions.FAILURE(result.message));
  }
}

function* getTalentAnalyticMonthly() {
  const result: any = yield* call(talentService().getTalentAnalytic, {
    from: moment().subtract(30, "days").startOf("day").toISOString(),
    to: moment().endOf("day").toISOString(),
  });
  if (result.ok) {
    yield* put(getTalentAnalyticMonthlyActions.SUCCESS(result.response));
  } else {
    yield* put(getTalentAnalyticMonthlyActions.FAILURE(result.message));
  }
}

function* getAvailabilityCalendars() {
  const result: any = yield* call(talentService().getAvailabilityCalendars);
  if (result.ok) {
    yield* put(
      getAvailabilityCalendarsActions.SUCCESS(result.response?.calendars)
    );
  } else {
    yield* put(getAvailabilityCalendarsActions.FAILURE(result.message));
  }
}

function* connectCalendarProvider({
  payload,
}: Action<{
  provider_name: string;
  redirect_uri: string;
  state: string;
  callback: (url: string) => void;
}>) {
  const result: any = yield* call(talentService().connectCalendarProvider, {
    provider_name: payload.provider_name,
    redirect_uri: payload.redirect_uri,
    state: payload.state,
  });

  if (result.ok && result?.response?.url?.length > 0) {
    payload.callback(result?.response?.url);
  } else {
    yield* put(connectCalendarProviderActions.FAILURE(payload.provider_name));
  }
}

function* disconnectCalendarProvider({ payload }: Action<CALENDAR_PROVIDERS>) {
  const result: any = yield* call(
    talentService().disconnectCalendarProvider,
    payload
  );

  if (result.ok) {
    yield* put(disconnectCalendarProviderActions.SUCCESS(payload));
  } else {
    yield* put(disconnectCalendarProviderActions.FAILURE(payload));
  }
}

function* authorizationTokenCalendarProvider({
  payload,
}: Action<{
  code: string;
  redirect_uri: string;
  provider: CALENDAR_PROVIDERS;
}>) {
  const result: any = yield* call(
    talentService().authorizationTokenCalendarProvider,
    {
      code: payload?.code,
      redirect_uri: payload?.redirect_uri,
    }
  );

  if (result.ok) {
    yield* put(
      authorizationTokenCalendarProviderActions.SUCCESS(payload?.provider)
    );
  } else {
    yield* put(
      authorizationTokenCalendarProviderActions.FAILURE(payload?.provider)
    );
  }
}

function* sendOTP({ payload }: Action<SendPhoneOTPRequest>) {
  const result: any = yield* call(userService.sendPhoneOTP, payload);
  if (result.ok) {
    yield* put(sendOTPActions.SUCCESS(result.response));
  } else {
    yield* put(sendOTPActions.FAILURE(result.message));
  }
}

function* sendEmailOTP({ payload }: Action<SendEmailOTPRequest>) {
  const result: any = yield* call(userService.sendEmailOTP, payload);
  if (result.ok) {
    yield* put(sendEmailOTPActions.SUCCESS(result.response));
  } else {
    yield* put(sendEmailOTPActions.FAILURE(result.message));
  }
}

function* checkUserExisted({ payload }: Action<CheckUserExistedRequest>) {
  const result: any = yield* call(userService.checkUserExisted, payload);

  if (result.ok) {
    yield* put(checkUserExistedActions.SUCCESS(result.response));
  } else {
    yield* put(checkUserExistedActions.FAILURE(result.message));
  }
}

function* getExchangeRates({ payload }: Action<string>) {
  const result: any = yield* call(userService.getExchangeRates, payload);
  if (result.ok) {
    yield* put(getExchangeRateActions.SUCCESS(result.response));
  } else {
    yield* put(getExchangeRateActions.SUCCESS(ExchangeRateDefault));
  }
}

function* getExchangeRatesUSD({}: Action<string>) {
  const result: any = yield* call(userService.getExchangeRates, "USD");
  if (result.ok) {
    yield* put(getExchangeRateUSDActions.SUCCESS(result.response));
  } else {
    yield* put(getExchangeRateUSDActions.SUCCESS(ExchangeRateDefault));
  }
}

function* createTalentProfileModule({
  payload,
}: Action<CreateTalentProfileModuleRequest<TalentModuleMixItem>>) {
  const result: any = yield* call(talentService().createTalentProfileModule, {
    ...payload,
  });
  if (result.ok) {
    yield* put(createTalentProfileModuleActions.SUCCESS(result.response));
    yield* call(reorderTalentProfileModule);
  } else {
    yield* put(
      createTalentProfileModuleActions.FAILURE({
        error: result.message,
        item: payload,
      })
    );
  }
}

function* updateTalentProfileModule({
  payload,
}: Action<UpdateTalentProfileModuleRequest<TalentModuleMixItem>>) {
  const result: any = yield* call(talentService().updateTalentProfileModule, {
    ...payload,
  });
  if (result.ok) {
    yield* put(updateTalentProfileModuleActions.SUCCESS(result.response));
  } else {
    yield* put(
      updateTalentProfileModuleActions.FAILURE({
        error: result.message,
        item: payload,
      })
    );
  }
}

function* deleteTalentProfileModule({
  payload,
}: Action<TalentProfileModule<TalentModuleMixItem>>) {
  const result: any = yield* call(talentService().deleteTalentProfileModule, {
    ...payload,
  });
  if (result.ok) {
    yield* put(deleteTalentProfileModuleActions.SUCCESS(payload));
    yield* call(reorderTalentProfileModule);
  } else {
    yield* put(
      deleteTalentProfileModuleActions.FAILURE({
        error: result.message,
        item: payload,
      })
    );
  }
}

function* reorderTalentProfileModule(
  action?: Action<ReorderTalentProfileModuleRequest[]>
) {
  const talentProfileModules: TalentProfileModule<TalentModuleMixItem>[] =
    yield select((state: RootState) => state.userState.talentProfileModules);
  const reorderPayload: ReorderTalentProfileModuleRequest[] =
    !!action && !!action.payload
      ? action?.payload?.map((module) => ({ ...module }))
      : (talentProfileModules.map((module, index) => ({
          id: module.id,
          order: index,
        })) as ReorderTalentProfileModuleRequest[]);
  const result: any = yield* call(
    talentService().reorderTalentProfileModule,
    reorderPayload
  );
  if (result.ok) {
    yield* put(reorderTalentProfileModuleActions.SUCCESS(result.response));
  } else {
    yield* put(reorderTalentProfileModuleActions.FAILURE(result.message));
  }
}

function* getCollaboratorInvitations() {
  const result: any = yield* call(talentService().getCollaborator);
  if (result.ok) {
    yield* put(getCollaboratorInvitationsActions.SUCCESS(result.response));
  } else {
    yield* put(getCollaboratorInvitationsActions.FAILURE(result.message));
  }
}
function* inviteCollaborator(action?: Action<InviteCollaboratorRequest>) {
  const result: any = yield* call(
    talentService().inviteCollaborator,
    action?.payload as InviteCollaboratorRequest
  );
  if (result.ok) {
    yield* put(inviteCollaboratorActions.SUCCESS(result.response));
    yield* call(getCollaboratorInvitations);
  } else {
    yield* put(inviteCollaboratorActions.FAILURE(result.message));
  }
}

function* cancelInviteCollaborator(
  action?: Action<CancelInviteCollaboratorRequest>
) {
  const result: any = yield* call(
    talentService().cancelInviteCollaborator,
    action?.payload as InviteCollaboratorRequest
  );
  if (result.ok) {
    yield* put(cancelInviteCollaboratorActions.SUCCESS(result.response));
    yield* call(getCollaboratorInvitations);
  } else {
    yield* put(cancelInviteCollaboratorActions.FAILURE(result.message));
  }
}

function* revokeInviteCollaborator(
  action?: Action<RevokeInviteCollaboratorRequest>
) {
  const result: any = yield* call(
    talentService().revokeInviteCollaborator,
    action?.payload as RevokeInviteCollaboratorRequest
  );
  if (result.ok) {
    yield* put(revokeInviteCollaboratorActions.SUCCESS(result.response));
    yield* call(getCollaboratorInvitations);
  } else {
    yield* put(revokeInviteCollaboratorActions.FAILURE(result.message));
  }
}

function* resendInviteCollaborator(
  action?: Action<ResendInviteCollaboratorRequest>
) {
  const result: any = yield* call(
    talentService().resendInviteCollaborator,
    action?.payload as ResendInviteCollaboratorRequest
  );
  if (result.ok) {
    yield* put(resendInviteCollaboratorActions.SUCCESS(result.response));
    yield* call(getCollaboratorInvitations);
  } else {
    yield* put(resendInviteCollaboratorActions.FAILURE(result.message));
  }
}

function* updateUserProfile({ payload }: Action<User>) {
  const result: any = yield* call(userService.updateConsumerProfile, payload);
  if (result.ok) {
    yield* put(updateUserProfileActions.SUCCESS(result.response));
  } else {
    yield* put(updateUserProfileActions.FAILURE(result.message));
  }
}

function* updateCollaboratorProfile({ payload }: Action<User>) {
  const result: any = yield* call(userService.updateConsumerProfile, payload);
  if (result.ok) {
    yield* put(updateCollaboratorProfileActions.SUCCESS(result.response));
  } else {
    yield* put(updateCollaboratorProfileActions.FAILURE(result.message));
  }
}

function* uploadDigitalProducts({
  payload,
}: {
  payload: autoSaveProfileRequest;
}) {
  const { digitalProductsFilesToUpload, ...previousVersion } = payload;

  let modules = previousVersion.modules;

  if (
    !!digitalProductsFilesToUpload &&
    !!previousVersion.updateTalentId &&
    Object.keys(digitalProductsFilesToUpload).length
  ) {
    const uploadTokenRequest = getDigitalProductTokenRequestBody(
      previousVersion.modules,
      digitalProductsFilesToUpload
    );

    yield* put(setUploadingProductsActions(true));

    const tokens: any = yield* call(talentService().getUploadTokens, {
      localizationId: previousVersion.localizationId,
      products: uploadTokenRequest,
    });
    let uploadTokens: Record<string, string> = {};

    if (tokens && tokens?.ok) {
      uploadTokens = tokens?.response?.data?.uploadTokens || {};

      const profileId = previousVersion.updateTalentId;
      for (const productId of Object.keys(uploadTokens)) {
        const file = digitalProductsFilesToUpload?.[productId];
        if (file) {
          try {
            const result: any = yield* call(getDigitalProductService().uploadFile, {
              profileId,
              token: uploadTokens[productId],
              file,
            });

            if (!result?.success) {
              throw new Error(result.message);
            }
          } catch (error) {
            console.log("error", error);
            yield* put(autoSaveProfileActions.FAILURE(error));
            return;
          }
        }
      }
    } else {
      yield* put(autoSaveProfileActions.FAILURE(tokens.message));
      return;
    }

    yield* put(setUploadingProductsActions(false));

    // Merge the new urls back into the modules
    modules = modules.map((module) => {
      const updateDigitalProductItems = (items: DigitalProductItem[]) => {
        return items.map((item) => ({
          ...item,
          landingPage: {
            ...item.landingPage,
            // Pass the token to profile service
            // We can't set the product url directly in file mode, the jwt ensures the correct path is set
            productUrl: uploadTokens[item.id] || item.landingPage.productUrl,
          },
        }));
      };

      if (module.type === ModuleType.GROUP) {
        const groupModule = module as TalentProfileModule<GroupItem>;
        return {
          ...groupModule,
          items: groupModule.items.map((subModule) => {
            if (subModule.type === ModuleType.DIGITAL_PRODUCT) {
              const digitalProductModule =
                subModule as TalentProfileModule<DigitalProductItem>;
              return {
                ...digitalProductModule,
                items: updateDigitalProductItems(digitalProductModule.items),
              };
            }
            return subModule;
          }),
        };
      }

      if (module.type === ModuleType.DIGITAL_PRODUCT) {
        const digitalProductModule =
          module as TalentProfileModule<DigitalProductItem>;
        return {
          ...digitalProductModule,
          items: updateDigitalProductItems(digitalProductModule.items),
        };
      }

      return module;
    });
  }

  return modules;
}

function* autoSaveProfile({ payload }: Action<autoSaveProfileRequest>) {
  const { digitalProductsFilesToUpload, ...previousVersion } = payload;

  const newModules = yield* call(uploadDigitalProducts, {
    payload: {
      ...previousVersion,
      digitalProductsFilesToUpload,
    },
  });
  if (!newModules) {
    return;
  }

  const autoSaveResult: any = yield* call(talentService().autoSaveProfile, {
    ...previousVersion,
    modules: newModules,
  });

  if (autoSaveResult.ok) {
    const currentVersion = autoSaveResult.response;

    yield* put(
      autoSaveProfileActions.SUCCESS({ currentVersion, previousVersion })
    );
    previousVersion?.onSuccess?.();
  } else {
    if (["timeout", "cannot-connect"].includes(autoSaveResult.kind)) {
      previousVersion?.onNetworkError?.();
    } else {
      yield* put(autoSaveProfileActions.FAILURE(autoSaveResult.message));
      if (
        autoSaveResult.message ===
        "Someone else is currently editing this profile, can not save your version!"
      ) {
        yield* put(setIsBlockingEditActions(true));
      }
    }
  }
}

function* publishProfile({ payload }: Action<publishProfileRequest>) {
  const result: any = yield* call(talentService().publicProfile, payload);
  if (result.ok) {
    payload?.onSuccess?.();
    yield* put(publishProfileActions.SUCCESS(payload));
  } else {
    if (["timeout", "cannot-connect"].includes(result.kind)) {
      payload?.onNetworkError?.();
    } else {
      yield* put(publishProfileActions.FAILURE(result.message));
    }
  }
}

/**
 * This function generates the request for the upload tokens
 * @param modules - All the modules
 * @param digitalProductsFilesToUpload - The digital products files to upload
 * @returns The digital product token request body
 */
const getDigitalProductTokenRequestBody = (
  modules: TalentProfileModule<TalentModuleMixItem>[],
  digitalProductsFilesToUpload: Record<string, File | undefined>
) => {
  const flattenedModules = modules.flatMap((module) => {
    if (module.type === "GROUP") {
      return module.items as TalentProfileModule<TalentModuleMixItem>[];
    }
    return [module];
  });

  const digitalProductModules = flattenedModules.filter(
    (
      module
    ): module is TalentProfileModule<TalentModuleMixItem> & {
      items: DigitalProductItem[];
    } => module.type === "DIGITAL_PRODUCT"
  );

  const digitalProductItems = digitalProductModules.flatMap(
    (module) => module.items
  );

  return Object.entries(digitalProductsFilesToUpload).map(
    ([productId, file]) => {
      const item = digitalProductItems.find((item) => item.id === productId);
      return {
        productId,
        productName: item?.title || "",
        fileName: file?.name || "",
      };
    }
  );
};

function* forceSaveAndPublishProfile({
  payload,
}: Action<autoSaveProfileRequest>) {
  const { digitalProductsFilesToUpload, ...previousVersion } = payload;
  const {
    localizationId,
    localizationIds,
    profile,
    updatedById,
    updateTalentId,
    onNetworkError,
    onSuccess,
  } = previousVersion;
  // Result is the new profile version

  const newModules = yield* call(uploadDigitalProducts, {
    payload: {
      ...previousVersion,
      digitalProductsFilesToUpload,
    },
  });
  if (!newModules) {
    return;
  }

  const autoSaveResult: any = yield* call(talentService().autoSaveProfile, {
    localizationId,
    profile,
    modules: newModules,
    updatedById,
    updateTalentId,
    onNetworkError,
    onSuccess,
  });
  if (autoSaveResult.ok) {
    // Updates the store
    const currentVersion = autoSaveResult.response;

    yield* put(
      autoSaveProfileActions.SUCCESS({
        previousVersion: {
          ...previousVersion,
          versions: [autoSaveResult.response],
        },
        currentVersion,
      })
    );
    previousVersion.profile.versions = currentVersion.response;
    const publishPayload = {
      localizationId: localizationIds || [],
      publishTalentId: updatedById,
      onNetworkError: onNetworkError,
      onSuccess: onNetworkError,
    };
    const publishResult: any = yield* call(
      talentService().publicProfile,
      publishPayload
    );
    if (publishResult.ok) {
      previousVersion?.onSuccess?.();
      yield* put(
        publishProfileActions.SUCCESS({ localizationId: localizationIds }),
      );
    } else {
      if (["timeout", "cannot-connect"].includes(publishResult.kind)) {
        previousVersion?.onNetworkError?.();
        yield* put(
          forceSaveAndpublishProfileActions.FAILURE(publishResult.message)
        );
      } else {
        yield* put(publishProfileActions.FAILURE(publishResult.message));
      }
    }
  } else {
    if (["timeout", "cannot-connect"].includes(autoSaveResult.kind)) {
      previousVersion?.onNetworkError?.();
      yield* put(
        forceSaveAndpublishProfileActions.FAILURE(autoSaveResult.message)
      );
    } else {
      yield* put(autoSaveProfileActions.FAILURE(autoSaveResult.message));
    }
  }
}

function* forceSaveProfileAndLogout({
  payload,
}: Action<autoSaveProfileRequest>) {
  const result = yield* call(talentService().autoSaveProfile, payload);

  window.location.href = "/logout";
  if ((result as any).ok) {
    window.location.href = "/logout";
  } else {
    if (["timeout", "cannot-connect"].includes((result as any).kind)) {
      payload?.onNetworkError?.();
    } else {
      window.location.href = "/logout";
    }
  }
}

function* deleteLocalization({ payload }: Action<DeleteLocalizationRequest>) {
  const result = yield* call(talentService().deleteLocalization, payload);

  if ((result as any).ok) {
    const localizations: LocalizationItem[] = yield select(
      (state: RootState) => state.userState.user?.talentProfile?.localizations
    );
    const newList =
      localizations?.filter((item) => item.id !== payload.localizationId) || [];
    yield* put(deleteLocalizationActions.SUCCESS(newList));
    payload?.onSuccess?.();
  } else {
    if (["timeout", "cannot-connect"].includes((result as any).kind)) {
      payload?.onNetworkError?.();
    } else {
      payload?.onError?.();
    }
    yield* put(deleteLocalizationActions.FAILURE((result as any).message));
  }
}

function* getLocalizationModules({
  payload,
}: Action<DeleteLocalizationRequest>) {
  const result = yield* call(
    talentService().getLocalizationModules,
    payload.talentProfileId,
    payload.localizationId
  );
  if (result.ok) {
    yield* put(getLocalizationModulesActions.SUCCESS(result.response));
    payload?.onSuccess?.();
  } else {
    yield* put(getLocalizationModulesActions.FAILURE(result.message));
    payload?.onError?.();
  }
}

function* getAnalyticsLocalizationModules({
  payload,
}: Action<DeleteLocalizationRequest>) {
  const result = yield* call(
    talentService().getLocalizationModules,
    payload.talentProfileId,
    payload.localizationId
  );
  if (result.ok) {
    yield* put(getAnalyticsLocalizationModulesActions.SUCCESS(result.response));
    payload?.onSuccess?.();
  } else {
    yield* put(getAnalyticsLocalizationModulesActions.FAILURE(result.message));
    payload?.onError?.();
  }
}

export default function* userSagas(): SagaIterator {
  yield* takeLatest(logoutActions.REQUEST, logout);
  yield* takeLatest(getUserProfileActions.REQUEST, getUserProfile);

  yield* takeLatest(updateTalentProfileActions.REQUEST, updateTalentProfile);

  yield* takeLatest(updatePasswordActions.REQUEST, updatePassword);
  yield* takeLatest(getTalentAnalyticActions.REQUEST, getTalentAnalytic);
  yield* takeLatest(
    getTalentAnalyticMonthlyActions.REQUEST,
    getTalentAnalyticMonthly
  );

  yield* takeLatest(
    getAvailabilityCalendarsActions.REQUEST,
    getAvailabilityCalendars
  );
  yield* takeLatest(
    connectCalendarProviderActions.REQUEST,
    connectCalendarProvider
  );
  yield* takeLatest(
    authorizationTokenCalendarProviderActions.REQUEST,
    authorizationTokenCalendarProvider
  );
  yield* takeLatest(
    disconnectCalendarProviderActions.REQUEST,
    disconnectCalendarProvider
  );
  yield* takeLatest(sendOTPActions.REQUEST, sendOTP);
  yield* takeLatest(sendEmailOTPActions.REQUEST, sendEmailOTP);
  yield* takeLatest(checkUserExistedActions.REQUEST, checkUserExisted);
  yield* takeLatest(getExchangeRateActions.REQUEST, getExchangeRates);
  yield* takeLatest(getExchangeRateUSDActions.REQUEST, getExchangeRatesUSD);
  yield* takeLatest(
    createTalentProfileModuleActions.REQUEST,
    createTalentProfileModule
  );
  yield* takeLatest(
    updateTalentProfileModuleActions.REQUEST,
    updateTalentProfileModule
  );
  yield* takeLatest(
    deleteTalentProfileModuleActions.REQUEST,
    deleteTalentProfileModule
  );
  yield* takeLatest(
    reorderTalentProfileModuleActions.REQUEST,
    reorderTalentProfileModule
  );
  yield* takeLatest(updateSpotifyUserActions.REQUEST, updateSpotifyUser);
  yield* takeLatest(getSpotifyUserTokenActions.REQUEST, getSpotifyUserToken);
  yield* takeLatest(
    getCollaboratorInvitationsActions.REQUEST,
    getCollaboratorInvitations
  );

  yield* takeLatest(inviteCollaboratorActions.REQUEST, inviteCollaborator);

  yield* takeLatest(
    cancelInviteCollaboratorActions.REQUEST,
    cancelInviteCollaborator
  );

  yield* takeLatest(
    revokeInviteCollaboratorActions.REQUEST,
    revokeInviteCollaborator
  );

  yield* takeLatest(
    resendInviteCollaboratorActions.REQUEST,
    resendInviteCollaborator
  );

  yield* takeLatest(
    getCollaboratorProfileActions.REQUEST,
    getCollaboratorProfile
  );

  yield* takeLatest(
    updateCollaboratorProfileActions.REQUEST,
    updateCollaboratorProfile
  );

  yield* takeLatest(updateUserProfileActions.REQUEST, updateUserProfile);
  yield* takeLatest(autoSaveProfileActions.REQUEST, autoSaveProfile);
  yield* takeLatest(publishProfileActions.REQUEST, publishProfile);
  yield* takeLatest(
    forceSaveAndpublishProfileActions.REQUEST,
    forceSaveAndPublishProfile
  );
  yield* takeLatest(
    forceSaveAndLogoutActions.REQUEST,
    forceSaveProfileAndLogout
  );
  yield* takeLatest(deleteLocalizationActions.REQUEST, deleteLocalization);
  yield* takeLatest(
    getLocalizationModulesActions.REQUEST,
    getLocalizationModules
  );
  yield* takeLatest(
    getAnalyticsLocalizationModulesActions.REQUEST,
    getAnalyticsLocalizationModules
  );
}
