import { getSDKConfig } from "config";
import ProfileService, {
  CurrencyBody,
  Localization,
  Module,
  ModuleType,
  PrivacyPolicyBody,
  ProfileStatusBody,
  RegionCode,
  YouTubeSort,
  PodcastType,
} from "@komi-app/profiles-sdk";

import AuthService, { ProfileRoleNames, SdkConfig } from "@komi-app/auth-sdk";
import SalesService, { SubscriptionWithPaymentData } from "@komi-app/sales-sdk";

import TalentService from "./TalentService";
import {
  InviteCollaboratorRequest,
  ResendInviteCollaboratorRequest,
  RevokeInviteCollaboratorRequest,
  TalentProfileCollaboratorInvitationStatus,
} from "models/talent/talent-profile-collaborator.model";

import {
  CreateTalentProfileModuleRequest,
  TalentModuleMixItem,
  UpdateTalentProfileModuleRequest,
  ReorderTalentProfileModuleRequest,
  TalentProfileModule,
  autoSaveProfileRequest,
  DeleteLocalizationRequest,
  publishProfileRequest,
} from "models/talent/talent-profile-module.model";

import { Response } from "redux/Common/types";
import {
  TalentProfileRequest,
  FinishTalentProfileRequest,
  VerifyBecomeTalentOtpRequest,
  DataForm,
  Analytics,
  GetAnalyticRequest,
  Talent,
} from "redux/Talent/types";

import {
  CALENDAR_PROVIDERS,
  ExportDataFormRequest,
  ExportFanClubRequest,
  getDataFormCountResult,
  getFanClubCountResult,
  MetaPixel,
  TalentAccountStatusType,
  TalentProfileDisplayNameTypes,
  TalentProfileStatusType,
  TalentProfileThemeTypes,
  User,
  UserProfile,
  UserState,
} from "redux/User/types";

import { GeneralApiProblem } from "./api/apiProblem";

import {
  MetaPixelParams,
  PodcastSyncLatestParams,
  PodcastValidateParams,
  RequestTalentProfileRequest,
} from "types";

import { Api } from "./api";
import { YoutubeParams, YoutubeResponse } from "types/youtube";

import CommunityService from "./CommunityService";
import { ProductKey } from "types/sales";
import { snakeCase } from "lodash";
import { FLAGS, isOn } from "@komi-app/flags-sdk";

const DEFAULT_UUID = "00000000-0000-0000-0000-000000000000";

export enum UPDATE_PROPERTY {
  LOCAL_CURRENCY = "local_currency",
  PRIVACY_POLICY = "privacy_policy",
  TALENT_NAME = "talent_name",
  TALENT_STATUS = "talent_status",
  TALENT_META_PIXEL_UPDATE = "talent_meta_pixel",
  TALENT_META_PIXEL_DELETE = "talent_meta_pixel_delete",
  USERNAME = "username",
  ENABLE_LOCALIZATION = "enable_localization",
  UPDATE_LOCALIZATION = "update_localization",
}

type UpdateError = {
  error: UPDATE_PROPERTY;
};

const defaultRejection: GeneralApiProblem = {
  kind: "rejected",
  message: "Required",
  status: 422,
};

const OK = {
  ok: true,
  response: {},
};

const defaultThemeColor = {
  overlayColor: "#FFFFFF",
  backgroundColor: "#121212",
  overlayOpacity: 0.1,
  typographyColor: "#FFFFFF",
};

class DeprecatedError extends Error {
  constructor() {
    super("Deprecated");
  }
}

export default class TalentServiceV1 extends TalentService {
  private profileService: ProfileService;
  private authService: AuthService;
  private salesService: SalesService;
  // private communityService: CommunityService; // NOTE: To be replaced with v1

  constructor(api: Api) {
    super(api);

    const profilesConfig = getSDKConfig("profiles");
    const authConfig: SdkConfig = getSDKConfig("auth");
    const salesConfig: SdkConfig = getSDKConfig("sales");

    this.salesService = new SalesService(salesConfig);

    this.profileService = new ProfileService(profilesConfig);
    this.authService = new AuthService(authConfig);
    // this.communityService = new CommunityService(api);
  }

  init(profileId: string) {
    this.profileService.init(profileId);
    this.authService.init(profileId);
    this.salesService.init(profileId);
    // TODO: community
  }

  getProfile = async (): Promise<Response<Talent>> => {
    const { handle, modules, profileInfo } =
      await this.profileService.getProfile();

    // NOTE: profile must be retrieve before anything else goes on
    this.init(profileInfo.profileId);

    const [basicInfo, subscriptions] = await Promise.all([
      this.authService.getBasicInfo(),
      //TODO sales
      this.salesService.getStripeSubscriptions(),
    ]);

    const defaultLocalization: Partial<Localization> =
      profileInfo.localization.find(
        (localization) => localization.id === DEFAULT_UUID
      ) || {};

    const fullName = profileInfo.name.split(" ");
    // const firstName: string = fullName.shift() || "";
    // const lastName: string = fullName.join(" ");

    const { avatar, firstName, lastName, email, phone, profiles, roles } =
      basicInfo;

    const legacyRoles = await this.authService.getLegacyRoles();

    const subs: Record<string, any> = {};

    for (const key in subscriptions) {
      const subscription = subscriptions[key as ProductKey];

      // subs[key] =
      const sub: Record<string, any> = {};
      for (const k in subscription) {
        sub[snakeCase(k)] =
          subscription[k as keyof SubscriptionWithPaymentData];
      }

      subs[key] = sub;
    }

    const collaborationTalentProfiles = profiles
      ? profiles.map(({ profileId, avatar, handle, displayName }) => ({
          id: "",
          talentProfile: {
            accountStatus: "active", // TODO
            avatar: avatar || "",
            displayName: displayName || "",
            firstName: displayName || "",
            id: profileId,
            lastName: "",
            user: {
              id: -1,
              username: handle,
            },
          },
        }))
      : [];

    const talentProfile = {
      id: defaultLocalization.profileId,
      firstName: firstName,
      lastName: lastName,
      username: handle,
      displayNameType:
        defaultLocalization.displayNameType as TalentProfileDisplayNameTypes,
      displayName: defaultLocalization.displayName || "",
      displayNameImage: defaultLocalization.displayNameImage || "",
      displayNameImageScale: defaultLocalization.displayNameImageScale || "",
      showDisplayName: defaultLocalization.showDisplayName,
      socialProfileLinks: defaultLocalization.profileLinks as any,
      covers: void 0,
      instagram: void 0,
      youtube: void 0,
      website: void 0,
      popularity: "",
      privacyPolicy: defaultLocalization.privacyPolicyUrl || "",
      privacyPolicyName: defaultLocalization.privacyPolicyName || "",
      createdVia: profileInfo.createdVia,
      //This is for onboard status, probably deprecated
      status: profileInfo.status as TalentProfileStatusType,
      accountStatus: profileInfo.status as TalentAccountStatusType,
      accomplishments: void 0,
      phone: void 0,
      avatar: defaultLocalization.avatar || "",
      bio: void 0,
      miniBio: void 0,
      trendingOrder: void 0,
      categoryTrendingOrder: void 0,
      //TODO : get the 5 digit user id (from UAM?)
      userId: void 0,
      commissionRate: void 0,
      currentVersionId: void 0,
      //TODO : Since we only allow one pixel, change the handling on komi-talent
      facebookPixels: defaultLocalization.metaPixel
        ? [
            {
              ...defaultLocalization.metaPixel,
              conversionAPIAccessToken:
                defaultLocalization.metaPixel.conversionApiAccessToken,
            },
          ]
        : [],
      fanClubName: "",
      fanClubBenefits: "",
      themeType: defaultLocalization.themeType as TalentProfileThemeTypes,
      themeColor: defaultLocalization.theme,
      // "country": defaultLocalization.regions?.map(reg => reg.code.toLowerCase()),
      country: defaultLocalization.regions?.map((reg) =>
        reg.code.toLowerCase()
      )[0],
      dateOfBirth: void 0,
      localizationActive: profileInfo.isLocalizationEnabled,
      localizations: profileInfo.localization
        .filter((local) => local.id !== DEFAULT_UUID)
        .map((local) => {
          return {
            active: local.isActive,
            copyFrom: null,
            countries:
              local.regions?.map((reg) => reg.code.toLowerCase()) || [],
            //This is redundant, can be removed from komi-talent in the future
            currency: "USD",
            id: local.id,
            isFirstTimePublished: local.isFirstTimePublished,
            isPublished: local.isPublished || false,
            name: local.name,
          };
        }),
      //TODO : check the modules format needed
      modules: modules as any,
      user: {
        id: -1, // NOTE: set to -1 to unlock templates
        username: handle,
      },
      shopifyAccounts: [], //TODO : link shopify Accounts data
      subscriptions: subs,
    };

    const profile = {
      // id: profileInfo.profileId,
      id: 0,
      firstName: firstName,
      lastName: lastName,
      email: email,
      phone: phone,
      phoneOtp: void 0,
      username: handle,
      emailVerified: void 0,
      emailMarketingPermissionsGranted: void 0,
      accountStatus: profileInfo.status as TalentAccountStatusType,
      avatar: avatar || "",
      gender: void 0,
      dateOfBirth: void 0,
      //TODO : get timezone and currency details from region array
      // "country": defaultLocalization.regions?.map(reg => reg.code.toLowerCase()),
      country: defaultLocalization.regions?.map((reg) =>
        reg.code.toLowerCase()
      )[0],
      timezone: void 0,
      localCurrency: profileInfo.currency,
      city: void 0,
      state: void 0,
      paymentEnabled: void 0,
      transferEnabled: void 0,
      thirdPartyInfo: void 0,
      forgotPasswordToken: void 0,
      activationCode: void 0,
      activationCodeVersion: void 0,
      forgotPasswordTokenExpire: void 0,
      //TODO : check from where to populate created by id
      // "createdById": profileInfo.,
      email2faEnabled: void 0,
      phone2faEnabled: void 0,
      talentProfile: talentProfile,
      //TODO: get the user id of logged in user
      // "authenticatedUserId": userId,
      collaborationTalentProfiles,
      roles: Object.values(legacyRoles),
      isAdminLogin: Boolean(
        roles.globalRoles.super_admin || roles.globalRoles.super_user
      ),
    };

    // return this.api.getUserProfile()
    //   .then(resp => {
    //     const value = resp as any;
    //     this.authenticated = true;
    //     this._id = value.ok && value.response?.user?.id;
    //     return resp;
    //   })

    return { ok: true, response: profile };
  };

  deleteLocalization = async ({
    localizationId,
  }: DeleteLocalizationRequest): Promise<Response<any>> => {
    await this.profileService.deleteLocalization(localizationId);

    return OK;
  };

  requestTalentProfile = async ({
    talentName,
    talentSocialProfileUrl,
  }: RequestTalentProfileRequest): Promise<Response<any>> => {
    // TODO
    // await this.authService.requestProfile({
    //   handle: talentName,
    //   socialProfileUrl: talentSocialProfileUrl
    // });

    return OK;
  };

  autoSaveProfile = async ({
    localizationId,
    modules,
    profile,
  }: autoSaveProfileRequest): Promise<Response<any>> => {
    //Set default localization id if it is null
    if (!localizationId) localizationId = DEFAULT_UUID;

    const {
      accountStatus,
      avatar,
      displayName,
      displayNameImage,
      displayNameImageScale,
      displayNameType,
      privacyPolicy,
      privacyPolicyName,
      showDisplayName,
      socialProfileLinks,
      themeColor,
      themeType,
    } = profile;

    const hasHeader = !!(
      avatar ||
      showDisplayName ||
      displayNameType ||
      displayName ||
      displayNameImage ||
      displayNameImageScale
    );

    const hasPrivacyPolicy = !!(
      privacyPolicy ||
      privacyPolicy === null ||
      privacyPolicyName
    );
    const hasTheme = !!(themeType || themeColor);

    const header = !hasHeader
      ? null
      : {
          ...(avatar && { avatar }),
          showDisplayName: showDisplayName === true,
          displayNameType:
            displayNameType || TalentProfileDisplayNameTypes.TEXT,
          ...(displayName && { displayName }),
          ...(displayNameImage && { displayNameImage }),
          ...(displayNameImageScale && { displayNameImageScale }),
        };

    const policy = !hasPrivacyPolicy
      ? null
      : {
          privacyPolicyName: privacyPolicyName || "",
          privacyPolicyUrl: privacyPolicy || null,
        };

    await Promise.all([
      modules &&
        this.profileService.updateModules(localizationId, {
          modules: modules.map(
            ({
              id,
              name,
              items,
              order,
              showTitle,
              type,
              visible,
              groupId,
              localizationId,
              talentProfileId,
            }) =>
              ({
                order,
                showTitle: showTitle === undefined ? true : showTitle,
                visible: visible === undefined ? true : visible,
                ...(talentProfileId && { profileId: talentProfileId }),
                ...(localizationId && { localizationId: localizationId }),
                ...(groupId && { parentId: groupId }),
                ...(name && { title: name }),
                ...(id && { id }),
                // groupId: groupId || null,
                items,
                type: type as ModuleType,
              }) as Module
          ), // typing issues with the transforms
          // will be solved post migration
        }),
      socialProfileLinks &&
        this.profileService.updateLinks(localizationId, {
          links: socialProfileLinks,
        }), // TODO: versionId
      header && this.profileService.updateHeader(localizationId, header),
      policy && this.profileService.updatePrivacyPolicy(localizationId, policy),
      accountStatus &&
        this.profileService.updateStatus({
          status: accountStatus,
          isProfileOnline: accountStatus === TalentAccountStatusType.ACTIVE, // TODO: make server side only
        }),
      hasTheme &&
        this.profileService.updateTheme(localizationId, {
          type: themeType || TalentProfileThemeTypes.DARK,
          theme: {
            ...defaultThemeColor,
            ...(themeColor && {
              ...(themeColor.overlayColor && {
                overlayColor: themeColor.overlayColor,
              }),
              ...(themeColor.backgroundColor && {
                backgroundColor: themeColor.backgroundColor,
              }),
              ...(themeColor.overlayOpacity && {
                overlayOpacity: themeColor.overlayOpacity,
              }),
              ...(themeColor.typographyColor && {
                typographyColor: themeColor.typographyColor,
              }),
            }),
          },
        }),
      // TODO: save auth info
    ]);

    return OK;
  };

  cancelInviteCollaborator = async ({
    email,
  }: InviteCollaboratorRequest): Promise<Response<any>> => {
    await this.authService.removeProfileCollaborator({
      email,
      roleName: ProfileRoleNames.ADMIN,
    });

    return OK;
  };

  /**
   * In Legacy this function was responsible for updating multiple properties that are now handeld in seperate endpoints
   * therefore it uses logic to determine the endpoints for profile service
   * @param payload
   */
  updateTalentProfile = async (
    payload: TalentProfileRequest
  ): Promise<Response<any>> => {
    const { userState } = payload as any;
    let { localizationId } = payload as any;

    //Set default localization id if it is null
    if (!localizationId) localizationId = DEFAULT_UUID;

    const {
      localCurrency,
      privacyPolicyName,
      privacyPolicy,
      username,
      accountStatus,
      facebookPixels,
    } = payload as any; //localCurrency does not exist on object - bad typing

    const { firstName, lastName, localizationActive, localizations } = payload;

    const updateCurrency = !!localCurrency;
    const updatePolicy = !!(privacyPolicyName || privacyPolicy);
    const updateName = !!(firstName || lastName);
    const updateStatus = !!accountStatus;
    const updatePixel = !!(facebookPixels && facebookPixels.length > 0);
    const deletePixel = !!(facebookPixels && facebookPixels.length === 0);
    const updateUsername = !!username;
    const enableLocalization = localizationActive !== undefined;
    const updateLocalization = !!localizations;

    const user: User = userState?.user;
    const talent: UserProfile = user?.talentProfile as UserProfile; // talentProfile is supposed to exist if we're here...
    const state: UserState = {
      ...userState,
      user: { ...user, talentProfile: { ...talent } },
    };

    const responses = await Promise.all([
      updateCurrency && this.updateCurrency({ currency: localCurrency }),
      updatePolicy &&
        this.updatePrivacyPolicy(localizationId, {
          privacyPolicyName,
          privacyPolicyUrl: privacyPolicy,
        }),
      updateName && this.updateName({ firstName, lastName }),
      updateStatus &&
        this.updateStatus({
          status: accountStatus,
          isProfileOnline: accountStatus === TalentAccountStatusType.ACTIVE,
        }),
      updatePixel && this.setMetaPixel(localizationId, facebookPixels),
      deletePixel && this.deleteMetaPixel(localizationId),
      updateUsername && this.updateUsername(username),
      enableLocalization && this.enableLocalization(localizationActive),
      updateLocalization &&
        this.updateLocalization(
          localizations.map(({ id, active, countries, name }) => ({
            // TODO: review optionality
            id: id as string,
            profileId: talent.id as string,
            avatar: talent.avatar as string,
            displayName: talent.displayName as string,
            displayNameImage: talent.displayNameImage as string,
            displayNameImageScale: talent.displayNameImageScale as string,
            displayNameType: talent.displayNameType as string,
            isActive: active,
            name,
            privacyPolicyName: talent.privacyPolicyName as string,
            showDisplayName: talent.showDisplayName as boolean,
            themeType: talent.themeType as string,
            privacyPolicyUrl: talent.privacyPolicy as string,
            regions: countries.map((code) => ({
              code: code.toUpperCase() as RegionCode,
            })),
          }))
        ),
    ]);

    let actionned = false;
    const errors: UPDATE_PROPERTY[] = [];

    responses.forEach((patch) => {
      if (!patch) return;

      if (patch.error) return void errors.push((patch as UpdateError).error);

      actionned = true;
      patchState(state, patch);
    });

    if (!actionned)
      throw new Error(
        `Unsupported payload {${Object.keys(payload).join(", ")}}`
      );

    if (errors.length)
      return {
        ...defaultRejection,
        message: `Unable to execute ${errors.join(`,`)}`,
      };

    //Return just user as response
    return { ok: true, response: { ...state.user } };
  };

  getTalentProfileById = (id: string): Promise<Response<any>> => {
    throw new DeprecatedError();
  };

  updateTalentProfileById = (
    id: string,
    payload: TalentProfileRequest
  ): Promise<Response<any>> => {
    throw new DeprecatedError();
  };

  createTalentProfile = async (
    payload: TalentProfileRequest
  ): Promise<Response<any>> => {
    // Profiles are created from the client side
    throw new DeprecatedError();
  };

  finishTalentProfile = (
    payload: FinishTalentProfileRequest
  ): Promise<Response<any>> => {
    throw new DeprecatedError();
  };

  getAvailabilityCalendars = (): Promise<Response<any>> => {
    throw new DeprecatedError();
  };

  connectCalendarProvider = (payload: {
    provider_name: string;
    redirect_uri: string;
  }): Promise<Response<any>> => {
    throw new DeprecatedError();
  };

  disconnectCalendarProvider = (
    providerName: CALENDAR_PROVIDERS
  ): Promise<Response<any>> => {
    throw new DeprecatedError();
  };

  authorizationTokenCalendarProvider = (payload: {
    code: string;
    redirect_uri: string;
  }): Promise<Response<any>> => {
    throw new DeprecatedError();
  };

  createTalentProfileModule = (
    payload: CreateTalentProfileModuleRequest<TalentModuleMixItem>
  ): Promise<Response<any>> => {
    throw new DeprecatedError();
  };

  updateTalentProfileModule = (
    payload: UpdateTalentProfileModuleRequest<TalentModuleMixItem>
  ): Promise<Response<any>> => {
    throw new DeprecatedError();
  };

  reorderTalentProfileModule = (
    payload: ReorderTalentProfileModuleRequest[]
  ): Promise<Response<any>> => {
    throw new DeprecatedError();
  };

  deleteTalentProfileModule = (
    payload: TalentProfileModule<TalentModuleMixItem>
  ): Promise<Response<any>> => {
    throw new DeprecatedError();
  };

  getCollaborator = async (): Promise<Response<any>> => {
    const collaborators = await this.authService.getProfileCollaborators();
    return {
      ok: true,
      response: [
        ...collaborators.active.map((activeCollaborator) => ({
          id: activeCollaborator.legacyId,
          status: TalentProfileCollaboratorInvitationStatus.ACCEPTED,
          email: activeCollaborator.email,
          inviteLink: "",
        })),
        ...collaborators.pending.map((pendingCollaborator) => ({
          id: pendingCollaborator.collaboratorId,
          status: TalentProfileCollaboratorInvitationStatus.PENDING,
          email: pendingCollaborator.email,
          inviteLink: "",
        })),
      ],
    };
  };

  inviteCollaborator = async ({
    email,
  }: InviteCollaboratorRequest): Promise<Response<any>> => {
    await this.authService.addProfileCollaborator({
      email,
      roleName: ProfileRoleNames.ADMIN,
    });

    return OK;
  };

  revokeInviteCollaborator = async ({
    email,
  }: RevokeInviteCollaboratorRequest): Promise<Response<any>> => {
    await this.authService.removeProfileCollaborator({
      email,
      roleName: ProfileRoleNames.ADMIN,
    });

    return OK;
  };

  getFanClubCount = (
    payload: ExportFanClubRequest
  ): Promise<Response<getFanClubCountResult>> => {
    throw new DeprecatedError();
  };

  exportFanClubCSV = (
    payload: ExportFanClubRequest
  ): Promise<Response<any>> => {
    throw new DeprecatedError();
  };

  verifyBecomeTalentOtp = (
    payload: VerifyBecomeTalentOtpRequest
  ): Promise<Response<any>> => {
    throw new DeprecatedError();
  };

  getDataFormCount = async (
    payload: ExportDataFormRequest
  ): Promise<Response<getDataFormCountResult>> => {
    if (!payload.moduleId) throw new TypeError("Missing moduleId");

    const count = await this.profileService.getDataFormCount({
      ...payload,
      moduleId: payload.moduleId,
    });

    return {
      ok: true,
      response: {
        totalSubmissions: count,
      },
    };
  };

  getDataCaptureForms = async (): Promise<Response<any>> => {
    const forms = await this.profileService.getDataCaptureForms();

    return {
      ok: true,
      response: forms,
    };
  };

  getListDataForm = async (): Promise<Response<any>> => {
    //TODO: investigate if we need separate endpoints to handle old and new froms
    return this.getDataCaptureForms();
  };

  // getListPreRelease = async (): Promise<Response<DataForm[]>> => {
  getListPreRelease = async (): Promise<Response<any>> => {
    const list = await this.profileService.getListPreRelease();

    return {
      ok: true,
      response: list,
    };
  };

  // getPreReleaseCount = async (payload: ExportDataFormRequest): Promise<Response<getDataFormCountResult>> => {
  getPreReleaseCount = async (
    payload: ExportDataFormRequest
  ): Promise<Response<any>> => {
    const count = await this.profileService.getPreReleaseCount(payload);

    return {
      ok: true,
      response: count,
    };
  };

  exportPreReleaseCSV = async (payload: any): Promise<Response<any>> => {
    const csv = await this.profileService.exportPreReleaseCSV(payload);

    return {
      ok: true,
      response: csv,
    };
  };

  exportDataCaptureCSV = async (
    payload: ExportDataFormRequest
  ): Promise<Response<any>> => {
    if (!payload.moduleId) throw new TypeError("Missing moduleId");

    const csv = await this.profileService.exportDataCaptureCSV({
      ...payload,
      moduleId: payload.moduleId,
    });

    return {
      ok: true,
      response: csv,
    };
  };

  validateMetaPixel = async (
    params: MetaPixelParams
  ): Promise<Response<any>> => {
    await this.profileService.validateMetaPixel(DEFAULT_UUID, params);

    return OK;
  };

  getYoutubeCollection = async (
    params: YoutubeParams
  ): Promise<Response<YoutubeResponse>> => {
    const { sort, totalSelected, url } = params;

    const { title, items } = await this.profileService.getYoutubeCollection({
      url,
      sort: sort as YouTubeSort,
      ...(totalSelected && { limit: `${totalSelected}` }),
    });

    const legacy = {
      title,
      items: items.map(({ url, metadata: { title, thumbnail } }) => ({
        url,
        metadata: {
          title,
          thumbnail_url: thumbnail,
        },
      })),
    };

    return {
      ok: true,
      response: legacy,
    };
  };

  getSyncLatestPodcasts = async (
    params: PodcastSyncLatestParams
  ): Promise<Response<any>> => {
    const {
      appleActive,
      appleUrl,
      firstIndex,
      isDifferent,
      spotifyActive,
      spotifyUrl,
      totalSelected,
    } = params;

    const { episodes, message } = await this.profileService.getLatestPodcasts({
      links: {
        ...(appleActive && { apple: appleUrl }),
        ...(spotifyActive && { spotify: spotifyUrl }),
      },
      ...(firstIndex && { index: firstIndex as PodcastType }),
      ...(isDifferent && { diverge: isDifferent }),
      ...(totalSelected && { limit: totalSelected }),
    });

    const legacy = episodes.map(({ title, show, author, urls, images }) => ({
      trackName: title,
      collectionName: show,
      author,
      urls: urls.map(({ type, url, preview }) => ({
        type,
        externalUrl: url,
        audioPreview: preview,
      })),
      images,
    }));

    return {
      ok: true,
      response: {
        episodes: legacy,
        message,
      },
    };
  };

  validatePodcastLinks = async ({
    apple,
    spotify,
    numberOfEpisodes,
  }: PodcastValidateParams): Promise<Response<any>> => {
    const withApple = apple && apple.active;
    const withSpotify = spotify && spotify.active;

    await this.profileService.validatePodcastLinks({
      links: {
        ...(withApple && { apple: apple.url }),
        ...(withSpotify && { spotify: spotify.url }),
      },
      ...(numberOfEpisodes && { numberOfEpisodes }),
    });

    return OK;
  };

  getLocalizationModules = async (
    talentProfileId: string,
    localizationId: string = DEFAULT_UUID
  ): Promise<Response<any>> => {
    //Set default localization id if it is null
    if (!localizationId) localizationId = DEFAULT_UUID;
    const modules = await this.profileService.getModules(localizationId);
    return {
      ok: true,
      response: {
        modules: modules.map(
          ({
            groupId,
            id,
            items,
            order,
            showTitle,
            title,
            type,
            visible,
            localizationId,
            parentId,
            profileId,
            versionId,
            template,
            ...rest
          }) => ({
            id,
            order,
            // TODO: Name is legacy, new system uses title
            // Switch to title exclusively when no longer running with legacy
            name: title || (rest as Module).name,
            showTitle,
            type,
            items,
            visible,
            localizationId,
            groupId: parentId || groupId,
            talentProfileId: profileId,
            template,
            versionId,
          })
        ),
      },
    };
  };

  resendInviteCollaborator = async ({
    email,
  }: ResendInviteCollaboratorRequest): Promise<Response<any>> => {
    await this.authService.addProfileCollaborator({
      email,
      roleName: ProfileRoleNames.ADMIN,
    });

    return OK;
  };

  getTalentAnalytic = (
    payload: GetAnalyticRequest
  ): Promise<Response<Analytics>> => {
    throw new DeprecatedError();
  };

  publicProfile = async (payload: publishProfileRequest): Promise<any> => {
    const { localizationId } = payload;

    await this.profileService.publish({
      //Assign default UUID
      localizations: localizationId.map((ids) => ids ?? DEFAULT_UUID),
    });

    return OK;
  };

  updateCurrency = async (
    data: CurrencyBody
  ): Promise<Partial<UserState> | UpdateError> => {
    try {
      await this.profileService.updateCurrency(data);
      return { user: { localCurrency: data.currency } };
    } catch (e) {
      console.error(e);
      return { error: UPDATE_PROPERTY.LOCAL_CURRENCY };
    }
  };

  updatePrivacyPolicy = async (
    localizationId: string = DEFAULT_UUID,
    data: PrivacyPolicyBody
  ): Promise<Partial<UserState> | UpdateError> => {
    try {
      await this.profileService.updatePrivacyPolicy(
        localizationId ?? DEFAULT_UUID,
        data
      );
      return { user: { talentProfile: { ...data } } };
    } catch (e) {
      console.error(e);
      return { error: UPDATE_PROPERTY.PRIVACY_POLICY };
    }
  };

  updateName = async (data: {
    firstName: string;
    lastName: string;
  }): Promise<Partial<UserState> | UpdateError> => {
    try {
      await this.profileService.updateName({
        name: fullname(data.firstName, data.lastName),
      });
      return { user: { firstName: data.firstName, lastName: data.lastName } };
    } catch (e) {
      console.error(e);
      return { error: UPDATE_PROPERTY.TALENT_NAME };
    }
  };

  updateStatus = async (
    data: ProfileStatusBody
  ): Promise<Partial<UserState> | UpdateError> => {
    try {
      await this.profileService.updateStatus(data);
      return {
        user: {
          talentProfile: {
            accountStatus: data.status as TalentAccountStatusType,
          },
        },
      };
    } catch (e) {
      console.error(e);
      return { error: UPDATE_PROPERTY.TALENT_STATUS };
    }
  };

  setMetaPixel = async (
    localizationId: string = DEFAULT_UUID,
    data: MetaPixel[]
  ): Promise<Partial<UserState> | UpdateError> => {
    try {
      await this.profileService.setMetaPixel(localizationId ?? DEFAULT_UUID, {
        pixelId: data[0].pixelId,
        conversionApiAccessToken: data[0].conversionAPIAccessToken,
      });
      return { user: { talentProfile: { facebookPixels: data } } };
    } catch (e) {
      console.error(e);
      return { error: UPDATE_PROPERTY.TALENT_META_PIXEL_UPDATE };
    }
  };

  deleteMetaPixel = async (
    localizationId: string = DEFAULT_UUID
  ): Promise<Partial<UserState> | UpdateError> => {
    try {
      await this.profileService.deleteMetaPixel(localizationId ?? DEFAULT_UUID);
      return { user: { talentProfile: { facebookPixels: [] } } };
    } catch (e) {
      console.error(e);
      return { error: UPDATE_PROPERTY.TALENT_META_PIXEL_DELETE };
    }
  };

  updateUsername = async (
    username: string
  ): Promise<Partial<UserState> | UpdateError> => {
    try {
      await this.profileService.updateHandle({ handle: username });
      return { user: { username } };
    } catch (e) {
      console.error(e);
      return { error: UPDATE_PROPERTY.USERNAME };
    }
  };

  enableLocalization = async (
    enabled: boolean
  ): Promise<Partial<UserState> | UpdateError> => {
    try {
      await this.profileService.enableLocalization(enabled);
      return { user: { talentProfile: { localizationActive: enabled } } };
    } catch (e) {
      console.error(e);
      return { error: UPDATE_PROPERTY.ENABLE_LOCALIZATION };
    }
  };

  updateLocalization = async (
    data: Localization[]
  ): Promise<Partial<UserState> | UpdateError> => {
    try {
      // the return array is reduced to an object in order to patch the state

      await Promise.all(
        data.map((localization) =>
          this.profileService.updateLocalization(localization.id, localization)
        )
      );

      return {
        user: {
          talentProfile: {
            localizations: data.reduce((mapping, localization, index) => {
              mapping[index] = {
                id: localization.id,
                active: localization.isActive,
                countries:
                  localization.regions?.map((region) => region.code) || [],
              };
              return mapping;
            }, {} as any),
          },
        },
      };
    } catch (e) {
      console.error(e);
      return { error: UPDATE_PROPERTY.UPDATE_LOCALIZATION };
    }
  };
}

function fullname(firstname: string, lastname: string) {
  return `${firstname || ""} ${lastname || ""}`.trim();
}

// patching the state directly as the state is local to "updateTalentProfile"

function patchState(state: Record<string, any>, patch: Record<string, any>) {
  for (const key in patch) {
    const value = patch[key];

    if (Array.isArray(value)) {
      state[key] = value;
    } else if (typeof value === "object") {
      patchState(state[key], value);
    } else {
      state[key] = value;
    }
  }
}
