import { Cookies } from 'react-cookie';
import { ApolloClient } from '@apollo/client';
import { isProvisionedSubscriptionVariant } from 'beta/utils/typeGuards';
import { AVAILABLE_PHONE_NUMBERS } from 'graphql/queries/mobile-signup/numberPot';
import { PRODUCT_BY_ID, VARIANT_PRODUCT_BY_ID } from 'graphql/queries/mobile-signup/productById';
import { cloneDeep } from 'lodash';
import { extendObservable, makeObservable, observable, runInAction } from 'mobx';
import {
  alltSamanOfferStep,
  companyStaffStepsData,
  companyStepsData,
  staffSteps,
  staffStepsData,
  stepsData,
} from 'pages/farsimi/kaupa/StepSetup';
import {
  AvailablePhoneNumbersQuery,
  CartItem,
  CartItemInput,
  ContactInfoInput,
  ContractExtraPayerInfoInput,
  ContractRequestType,
  MsisdnType,
  NumberType,
  OptionVariant,
  PhoneNumberRightHolderInfoInput,
  ProductByIdQuery,
  ProvisionedSubscriptionVariant,
  PurchaseInfo,
  PurchaseInfoInput,
  ServiceRequestInput,
  ServiceRequestType,
  SignupContractInput,
  SimCardType,
  SubscriptionVariantOptionType,
  UserInfoInput,
  Variant,
  VariantProductByIdQuery,
} from 'typings/graphql';
import { hasMobileServiceRequest, hasValidPurchaseInfo } from 'utils/helpers';

import Authentication from './authentication';
import Cart from './cart';
import { SignupStep } from './signupSteps';

export enum MobileSignupProducts {
  Askrift = 'askrift',
  Frelsi = 'frelsi',
  Stakt = 'stakt',
  Fyrirtaeki = 'fyrirtaeki',
  Netkort = 'netkort',
  M2M = 'm2m',
}

export const UTLANDAPAKKI = 'farsimi-utlandapakki';
export const URLAUSN = 'urlausn';
export const SIM_CARD = 'plast-sim';
export const E_SIM_CARD = 'e-sim';
export const PHONE_NUMBERS = '150395';
export const SINGLE_OFFERING = '140140';
export const COMPANY = '150156';
export const COMPANY_NETKORT = '150129';
export const COMPANY_M2M = '150127';
export type ExtraServices = 'farsimi-utlandapakki' | 'urlausn';

type SimCardPurchaseInfo = Pick<
  PurchaseInfoInput,
  'iccid' | 'numberType' | 'phoneNumber' | 'serviceCartItemId' | 'simCardType'
>;

type ServicePurchaseInfo = {
  service: Pick<
    ServiceRequestInput,
    | 'portInDate'
    | 'isUnregisteredPlan'
    | 'type'
    | 'phoneNumber'
    | 'isNewNumber'
    | 'user'
    | 'mobileSignupRightHolder'
  >;
  contract: Pick<SignupContractInput, 'cartItemId' | 'type'>;
  departmentId?: string;
  invoiceExplanation?: string;
  contractExtraPayerInfo?: ContractExtraPayerInfoInput;
};

type CompanyInfoInput = {
  departmentId?: string;
  invoiceExplanation?: string;
  contractExtraPayerInfo?: ContractExtraPayerInfoInput;
};

export const throwErrorWithMessage = (error: unknown, customMessage: string) => {
  if (error instanceof Error) {
    const errorMesage = `${customMessage}: ${error.message}`;
    // eslint-disable-next-line no-console
    console.log(errorMesage);
  }
};

export type MobileSignupProductsType = keyof typeof MobileSignupProducts;
const goldNumberVariant = 'simanumer-gull';
const normalNumberVariant = 'simanumer-venjulegt';
const netNumberVariant = 'simanumer-netnumer';

export default class MobileSignup {
  private client: ApolloClient<object>;
  private cart: Cart;
  private authentication: Authentication;
  constructor(
    { mobileSignup = {} },
    client: ApolloClient<object>,
    cart: Cart,
    cookies: string | object | null | undefined,
    authentication: Authentication,
  ) {
    this.cookies = new Cookies(cookies);
    makeObservable(this, {
      primaryVariant: observable,
      extraVariants: observable,
      secondaryVariant: observable,
      selectedSubscriptionType: observable,
      servicePurchaseInfo: observable,
      variantInfoRecord: observable,
      simCardPurchaseInfo: observable,
      updatingCartItem: observable,
      askriftMonthly: observable,
      frelsiMobileMonthly: observable,
      isOskrad: observable,
      isCompany: observable,
      selectedQuestionVariant: observable,
      selectedRequiredOption: observable,
      selectedProvisionedVariantOption: observable,
      steps: observable,
      isEligibleForNetNet: observable,
      activateOnAnotherDevice: observable,
      deviceType: observable,
      randomNetnumer: observable,
    });

    extendObservable(this, mobileSignup);
    this.client = client;
    this.cart = cart;
    this.authentication = authentication;
  }
  cookies;
  isEligibleForNetNet: boolean = false;
  activateOnAnotherDevice: boolean = false;
  isOskrad: boolean = false;
  isCompany: boolean = false;
  deviceType: string = '';
  primaryVariant: string = '';
  secondaryVariant: string = '';
  extraVariants: Array<string> = [];
  updatingCartItem: string | undefined = undefined;
  frelsiMobileInternet: (OptionVariant | undefined)[] = [];
  frelsiMobileKronur: (OptionVariant | undefined)[] = [];
  frelsiMobileMonthly: (OptionVariant | undefined)[] = [];
  askriftMonthly: (OptionVariant | undefined)[] = [];
  selectedRequiredOption: OptionVariant | undefined = undefined;
  selectedProvisionedVariantOption: ProvisionedSubscriptionVariant | undefined = undefined;
  phoneNumberVariants: Variant[] = [];
  randomNetnumer: string = '';
  variantInfoRecord: Record<
    string,
    {
      price: number;
      name: string;
    }
  > = {};
  foreignPacks: (OptionVariant | undefined)[] = [];
  selectedSubscriptionType: MobileSignupProducts | undefined = undefined;
  selectedQuestionVariant: string | undefined = undefined;
  steps: Array<SignupStep> = stepsData;

  servicePurchaseInfo: ServicePurchaseInfo = {
    departmentId: '',
    invoiceExplanation: '',
    contractExtraPayerInfo: {
      customerId: '',
      amount: undefined,
    },
    service: {
      portInDate: undefined,
      isUnregisteredPlan: false,
      type: ServiceRequestType.Mobile,
      phoneNumber: '',
      mobileSignupRightHolder: {
        nationalId: '',
        email: '',
      },
      isNewNumber: undefined,
      user: {
        customerId: '',
        email: '',
        name: '',
        nationalId: '',
        phoneNumber: '',
      },
    },
    contract: {
      cartItemId: '',
      type: ContractRequestType.New,
    },
  };

  simCardPurchaseInfo: SimCardPurchaseInfo = {
    iccid: '', // For Simcard
    numberType: undefined,
    serviceCartItemId: '',
    phoneNumber: '', // for portable number
    simCardType: undefined,
  };

  // Getters
  get hasUtlandapakki() {
    return !!this.extraVariants.find((item) => item.includes(UTLANDAPAKKI));
  }

  get isUpdatingSubscription() {
    return !!this.updatingCartItem;
  }

  get hasUrlausn() {
    return !!this.extraVariants.find((item) => item === URLAUSN);
  }

  get hasExtraServices() {
    return this.hasUrlausn || this.hasUtlandapakki;
  }

  hasPlasticSimCard = async () => {
    const cart = await this.cart.fetchCart();

    return !!cart?.items.find((item) => item.purchaseInfo?.simCardType === SimCardType.Plastic);
  };

  getVariantName = (variantId: string) => {
    return this.variantInfoRecord[variantId]?.name;
  };

  getVariantPrice = (variantId: string) => {
    return this.variantInfoRecord[variantId]?.price;
  };

  getRightHolder = async (cartItemId: string) => {
    const cart = await this.cart.fetchCart();
    const items = cart?.items;

    const purchaseInfo = items?.find((item) => item.id === cartItemId)?.purchaseInfo;
    if (
      purchaseInfo?.service?.__typename === 'MobileServiceRequest' &&
      !purchaseInfo?.service?.isNewNumber
    ) {
      return purchaseInfo?.service?.mobileSignupRightHolder?.nationalId;
    } else {
      return null;
    }
  };

  setIsOskrad = (value: boolean) => {
    this.isOskrad = value;
  };

  setIsCompany = (value: boolean) => {
    this.isCompany = value;
  };

  setRandomNetnumer = (value: string) => {
    this.randomNetnumer = value;
  };

  // Variant setters

  setSelectedQuestionVariant = (variant: string | undefined) => {
    this.selectedQuestionVariant = variant;
  };
  // Tell the signup which id user is updating in "Update inputs" Mode
  setUpdatingCartItemId = (cartItemId: string | undefined) => {
    this.updatingCartItem = cartItemId;
  };

  setPrimaryVariant = (variantId: string) => {
    this.primaryVariant = variantId;
  };

  setSecondaryVariant = (variantId: string) => {
    this.secondaryVariant = variantId;
  };

  setExtraVariant = (variant: string) => {
    this.extraVariants.push(variant);
  };

  removeExtraVariant = (variant: string) => {
    const filteredExtraVariants = this.extraVariants.filter((item) => item !== variant);
    this.extraVariants = filteredExtraVariants;
  };

  setIsEligibleForNetNet = (isEligable: boolean) => {
    this.isEligibleForNetNet = isEligable;
  };

  setIsActivatingOnAnotherDevice = (activateOnAnotherDevice: boolean) => {
    this.activateOnAnotherDevice = activateOnAnotherDevice;
  };

  setDeviceType = (deviceType: string) => {
    this.deviceType = deviceType;
  };

  // If user has utlandapakki and changes subscriptionType
  // We want to switch the pack to the correct one.
  switchUtlandaPakkiVariant = (variant: OptionVariant) => {
    const correctUtlandaPakki = this.foreignPacks.find((item) => item?.id !== variant.id);
    if (correctUtlandaPakki) {
      this.removeExtraVariant(variant.id);
      this.setExtraVariant(correctUtlandaPakki.id);
    }
  };

  validConditions: Record<MobileSignupProducts, MobileSignupProducts> = {
    [MobileSignupProducts.Askrift]: MobileSignupProducts.Frelsi,
    [MobileSignupProducts.Frelsi]: MobileSignupProducts.Askrift,
    [MobileSignupProducts.Stakt]: MobileSignupProducts.Askrift,
    [MobileSignupProducts.Fyrirtaeki]: MobileSignupProducts.Fyrirtaeki,
    [MobileSignupProducts.Netkort]: MobileSignupProducts.Netkort,
    [MobileSignupProducts.M2M]: MobileSignupProducts.M2M,
  };

  setSelectedSubscriptionType = (type: MobileSignupProducts) => {
    this.selectedSubscriptionType = type;

    const relatedType = this.validConditions[type];
    const currentForeignPack = this.foreignPacks.find(
      (item) => item && this.extraVariants.includes(item.id),
    );

    if (relatedType && currentForeignPack?.id.includes(relatedType)) {
      this.switchUtlandaPakkiVariant(currentForeignPack);
    }
    if (this.isOskrad) {
      if (type === MobileSignupProducts.Stakt || type === MobileSignupProducts.Frelsi) {
        this.setPrimaryVariant('frelsi-oskrad');
        return;
      }
    }
    if (type === MobileSignupProducts.Stakt) {
      this.setPrimaryVariant(MobileSignupProducts.Frelsi);
      return;
    }
    this.setPrimaryVariant(type);
  };

  setSelectedRequiredOption = (option: OptionVariant | undefined) => {
    this.selectedRequiredOption = option;
  };

  setSelectedProvisionedVariantOption = (variant: ProvisionedSubscriptionVariant | undefined) => {
    this.selectedProvisionedVariantOption = variant;
  };

  // PurchaseInfo setters

  setIccid = (iccid: string) => {
    if (this.servicePurchaseInfo.service) {
      this.simCardPurchaseInfo.iccid = iccid;
    }
  };

  setPortingNumber = (number: string) => {
    this.servicePurchaseInfo.service.isNewNumber = false;
    this.servicePurchaseInfo.service.phoneNumber = number;
    this.simCardPurchaseInfo.phoneNumber = number;
    this.simCardPurchaseInfo.numberType = undefined;
  };

  setSelectedNumer = (number: string, numberType: NumberType, isNetNumber?: boolean) => {
    const hasNormalNumberInCart = this.extraVariants.find(
      (item) => item === normalNumberVariant || item === netNumberVariant,
    );
    const hasGoldNumberInCart = this.extraVariants.find((item) => item === goldNumberVariant);

    if (number === this.simCardPurchaseInfo.phoneNumber) {
      this.removeExtraVariant(
        numberType === NumberType.Gold
          ? (hasGoldNumberInCart as string)
          : (hasNormalNumberInCart as string),
      );
      this.servicePurchaseInfo.service.isNewNumber = false;
      this.servicePurchaseInfo.service.phoneNumber = '';
      this.simCardPurchaseInfo.numberType = undefined;
      this.simCardPurchaseInfo.phoneNumber = '';
    }

    this.servicePurchaseInfo.service.isNewNumber = true;
    this.servicePurchaseInfo.service.phoneNumber = number;
    this.simCardPurchaseInfo.numberType = numberType;
    this.simCardPurchaseInfo.phoneNumber = number;

    if (numberType === NumberType.Gold) {
      if (!hasGoldNumberInCart) {
        this.setExtraVariant(goldNumberVariant);
      }
      this.removeExtraVariant(normalNumberVariant);
    } else {
      if (!hasNormalNumberInCart) {
        if (isNetNumber) {
          this.setExtraVariant(netNumberVariant);
        } else {
          this.setExtraVariant(normalNumberVariant);
        }
      }
      this.removeExtraVariant(goldNumberVariant);
    }
  };

  setSimCardType = (type: SimCardType) => {
    if (type === SimCardType.Plastic) {
      this.extraVariants.push(SIM_CARD);
      this.removeExtraVariant(E_SIM_CARD);
    }
    if (type === SimCardType.ESim) {
      this.extraVariants.push(E_SIM_CARD);
      this.removeExtraVariant(SIM_CARD);
    }
    this.simCardPurchaseInfo.simCardType = type;
  };

  setParentContractId = (parentId: string) => {
    if (this.servicePurchaseInfo.contract) {
      this.servicePurchaseInfo.contract.cartItemId = parentId;
    }
  };

  setUserPurchaseInfo = (user: UserInfoInput) => {
    if (this.servicePurchaseInfo.service?.user) {
      this.servicePurchaseInfo.service.user = user;
    }
  };

  setRightHolder = (rightHolder: PhoneNumberRightHolderInfoInput) => {
    if (this.servicePurchaseInfo.service?.mobileSignupRightHolder) {
      this.servicePurchaseInfo.service.mobileSignupRightHolder = rightHolder;
    }
  };

  // Fetches
  fetchAllProducts = async () => {
    await this.fetchFrelsiMobileProduct();
    await this.fetchAskriftMobileProduct();
    await this.fetchPhoneNumberVariants();
    await this.fetchAvailablePhoneNumbers();
    this.addToVariantInfoRecord();
  };

  fetchPhoneNumberVariants = async () => {
    if (this.phoneNumberVariants.length !== 0) return null;
    try {
      const { data } = await this.client.query<VariantProductByIdQuery | undefined>({
        query: VARIANT_PRODUCT_BY_ID,
        variables: {
          productId: PHONE_NUMBERS,
        },
      });

      runInAction(() => {
        const variants = data?.product?.variants;
        if (!variants?.length) null;
        else {
          this.phoneNumberVariants = variants as Variant[];
        }
      });
    } catch (e) {
      throwErrorWithMessage(e, 'Failed to fetch subscription data');
    }
  };

  fetchAvailablePhoneNumbers = async () => {
    if (this.randomNetnumer.length > 0) return null;
    try {
      const { data } = await this.client.query<AvailablePhoneNumbersQuery | undefined>({
        query: AVAILABLE_PHONE_NUMBERS,
        variables: {
          input: { count: 1, type: MsisdnType.Normal },
        },
      });

      runInAction(() => {
        const firstNumber =
          data?.availablePhoneNumbers?.length && data.availablePhoneNumbers.length > 0
            ? data?.availablePhoneNumbers[0]
            : null;
        if (!firstNumber) null;
        else {
          this.randomNetnumer = firstNumber.phoneNumber ?? '';
        }
      });
    } catch (e) {
      throwErrorWithMessage(e, 'Failed to fetch available phone numbers');
    }
  };

  fetchAskriftMobileProduct = async () => {
    if (this.askriftMonthly.length !== 0) return null;
    try {
      const { data } = await this.client.query<ProductByIdQuery | undefined>({
        query: PRODUCT_BY_ID,
        variables: {
          productId: '150149',
        },
      });

      runInAction(() => {
        const variants = data?.product?.variants;
        if (!variants?.length) null;
        else {
          const provisionedVariant = variants?.filter(
            (item) => item.__typename === 'ProvisionedSubscriptionVariant',
          ) as ProvisionedSubscriptionVariant[];

          this.variantInfoRecord[`${provisionedVariant[0].id}`] = {
            price: 0,
            name: provisionedVariant[0].shortName ?? '',
          };

          const subscriptionOptionsVariants = provisionedVariant[0].options?.find(
            (item) => item?.type === SubscriptionVariantOptionType.Required,
          )?.variants;

          const subscriptionForeignPack = provisionedVariant[0].options?.find(
            (item) =>
              item?.type === SubscriptionVariantOptionType.Optional &&
              item.variants[0].id.includes('utlanda'),
          )?.variants;
          if (subscriptionForeignPack) {
            this.foreignPacks.push(subscriptionForeignPack[0]);
          }
          this.askriftMonthly = subscriptionOptionsVariants || [];
        }
      });
    } catch (e) {
      throwErrorWithMessage(e, 'Failed to fetch subscription data');
    }
  };

  fetchFrelsiMobileProduct = async () => {
    if (
      this.frelsiMobileInternet.length !== 0 &&
      this.frelsiMobileKronur.length !== 0 &&
      this.frelsiMobileMonthly.length !== 0
    )
      return null;
    try {
      const { data } = await this.client.query<ProductByIdQuery | undefined>({
        query: PRODUCT_BY_ID,
        variables: {
          productId: '150151',
        },
      });
      runInAction(() => {
        const variants = data?.product?.variants;
        if (!variants?.length) null;
        else {
          const provisionedVariant = variants?.filter(isProvisionedSubscriptionVariant);

          this.variantInfoRecord[`${provisionedVariant[0].id}`] = {
            price: 0,
            name: provisionedVariant[0].shortName ?? '',
          };
          // Frelsi Monthly
          const frelsiSubscriptionType = provisionedVariant[0].options?.find(
            (item) => item?.type === SubscriptionVariantOptionType.Optional,
          )?.variants;
          this.frelsiMobileMonthly = frelsiSubscriptionType || [];

          // Frelsi Útlandapakki
          const frelsiForeignPack = provisionedVariant[0].options?.find(
            (item) =>
              item?.type === SubscriptionVariantOptionType.Optional &&
              item.variants[0].id.includes('utlanda'),
          )?.variants;

          if (frelsiForeignPack) {
            this.foreignPacks.push(frelsiForeignPack[0]);
          }

          // Frelsi Kronur
          const frelsiKronur = provisionedVariant[0].options?.filter(
            (item) =>
              item?.type === SubscriptionVariantOptionType.Optional &&
              item.variants[0].id.includes('afylling'),
          );

          this.frelsiMobileKronur = frelsiKronur?.map((item) => item?.variants[0]) || [];

          // Frelsi Internet pakki
          const frelsiInternet = provisionedVariant[0].options?.filter(
            (item) =>
              item?.type === SubscriptionVariantOptionType.Optional &&
              item.variants[0].id.includes('netid'),
          );
          this.frelsiMobileInternet = frelsiInternet?.map((item) => item?.variants[0]) || [];
        }
      });
    } catch (e) {
      throwErrorWithMessage(e, 'Failed to fetch frelsi data');
    }
  };

  addToVariantInfoRecord = () => {
    [
      ...this.askriftMonthly,
      ...this.frelsiMobileInternet,
      ...this.frelsiMobileKronur,
      ...this.frelsiMobileMonthly,
      ...this.phoneNumberVariants,
      ...this.foreignPacks,
    ].map((item) => {
      if (!item) return null;
      if (item.__typename === 'Variant') {
        this.variantInfoRecord[`${item.id}`] = {
          price: item.price,
          name: item.name,
        };
      }

      if (item.__typename === 'OptionVariant') {
        if (item.variantDetails?.monthlyCharge) {
          this.variantInfoRecord[`${item?.id}`] = {
            price: item?.variantDetails?.monthlyCharge,
            name: item.variantDetails.name ?? '',
          };
        } else if (item.variantDetails?.price) {
          this.variantInfoRecord[`${item?.id}`] = {
            price: item?.variantDetails?.price,
            name: item.variantDetails.name ?? '',
          };
        } else {
          this.variantInfoRecord[`${item?.id}`] = {
            price: 0,
            name: item?.variantDetails?.name ?? '',
          };
        }
      }
    });
  };

  addSubscriptionToCart = async (variantId: string) => {
    const subscriptionInput = {
      variantId,
      quantity: 1,
      purchaseInfo: {},
    } as CartItemInput;

    try {
      const res = await this.cart.addToCart('mobileSignup', subscriptionInput);
      if (!this.cart.activeCartId && res?.cart?.addToCart.cart?.id) {
        this.cart.setActiveCartId(res?.cart?.addToCart.cart?.id);
      }
    } catch (e) {
      throwErrorWithMessage(e, 'Failed to add subscription to cart');
    }
  };

  addServiceToCart = async (serviceVariant: string, parentId: string) => {
    if (parentId && serviceVariant) {
      try {
        this.setParentContractId(parentId);
        const servicenInput = {
          variantId: serviceVariant,
          quantity: 1,
          purchaseInfo: this.servicePurchaseInfo,
        } as CartItemInput;
        await this.cart.addToCart('mobileSignup', servicenInput);
      } catch (e) {
        throwErrorWithMessage(e, 'Failed to add service to cart');
      }
    }
  };

  addExtraServicesToCart = async (serviceCartItemId: string) => {
    if (!this.extraVariants.length) return null;
    try {
      const promises = this.extraVariants.map(async (variantId) => {
        let extraServiceInput;
        if (variantId === URLAUSN) return null;
        if (variantId === UTLANDAPAKKI) {
          extraServiceInput = {
            variantId,
            quantity: 1,
            purchaseInfo: {
              serviceCartItemId,
            },
          } as CartItemInput;
        } else {
          extraServiceInput = {
            variantId,
            quantity: 1,
            purchaseInfo: {
              ...this.simCardPurchaseInfo,
              serviceCartItemId,
            },
          } as CartItemInput;
        }

        // Return the promise from addToCart
        return this.cart.addToCart('mobileSignup', extraServiceInput);
      });

      // Wait for all promises to resolve
      await Promise.all(promises);
    } catch (e) {
      throwErrorWithMessage(e, 'Failed to add extra service to cart');
    }
  };

  addAllToCart = async () => {
    // Check if user has selected with home fiber variant before adding to cart
    // Auth step is behind the select subscription so we don't know for sure
    // if the customer has really has fiber.
    // Scroll to Subscription step if use doesn't have fiber and remove the fiber mobile variant

    await this.addSubscriptionToCart(this.primaryVariant);

    const parentId: string | undefined = await this.findParentSubscription(this.primaryVariant);
    if (parentId) {
      await this.addServiceToCart(this.secondaryVariant, parentId);
      const serviceItem = await this.findServiceItem(parentId);
      if (serviceItem) {
        await this.addExtraServicesToCart(serviceItem.id);
      }
    }
  };

  addContactToCart = async (user: ContactInfoInput) => {
    const cart = await this.cart.fetchCart();
    const { salesNumber } = this.cart;
    const contactInfo = {
      email: user?.email ?? cart?.contact?.email,
      msisdn: user?.msisdn ?? cart?.contact?.msisdn,
      ssn: user?.ssn ?? cart?.contact?.ssn,
      name: user?.name ?? cart?.contact?.name,
      address: user?.address ?? cart?.contact?.address,
      zip: user?.zip ?? cart?.contact?.zip,
      ...(user?.id && { id: user.id }),
      ...(salesNumber && { salesNumber: salesNumber }),
    };
    if (!(cart?.contact?.id && !user?.id)) {
      try {
        await this.cart.updateCartContact(contactInfo);
      } catch (e) {
        throwErrorWithMessage(e, 'Failed to update contact on cart');
      }
    }
  };

  updateUserInfo = async (user: UserInfoInput, cartItemId: string) => {
    try {
      if (cartItemId) {
        const mobileServiceItem = await this.findServiceItem(cartItemId);
        const serviceItem = cloneDeep(mobileServiceItem);
        if (hasMobileServiceRequest(serviceItem)) {
          serviceItem.purchaseInfo.service.user.name = user.name;
          serviceItem.purchaseInfo.service.user.nationalId = user.nationalId;
          serviceItem.purchaseInfo.service.user.email = user.email;
          serviceItem.purchaseInfo.service.user.customerId = user.customerId ?? null;
          serviceItem.purchaseInfo.service.user.phoneNumber = user.phoneNumber ?? null;
        }
        if (serviceItem?.id && serviceItem?.variantId && hasValidPurchaseInfo(serviceItem)) {
          await this.cart.updateCartItem({
            id: serviceItem?.id,
            variantId: serviceItem?.variantId,
            quantity: serviceItem?.quantity,
            purchaseInfo: serviceItem?.purchaseInfo,
          });
        }
      }
    } catch (error) {
      throwErrorWithMessage(error, 'Ekki tókst að vista notanda');
    }
  };

  updateRightHolder = async (rightHolder: PhoneNumberRightHolderInfoInput, cartItemId: string) => {
    try {
      if (cartItemId) {
        const mobileServiceItem = await this.findServiceItem(cartItemId);
        const serviceItem = cloneDeep(mobileServiceItem);
        if (hasMobileServiceRequest(serviceItem)) {
          serviceItem.purchaseInfo.service.user.nationalId = rightHolder.nationalId;
          serviceItem.purchaseInfo.service.user.email = rightHolder.email;
        }
        if (serviceItem?.id && serviceItem?.variantId && hasValidPurchaseInfo(serviceItem)) {
          await this.cart.updateCartItem({
            id: serviceItem?.id,
            variantId: serviceItem?.variantId,
            quantity: serviceItem?.quantity,
            purchaseInfo: serviceItem?.purchaseInfo,
          });
        }
      }
    } catch (error) {
      throwErrorWithMessage(error, 'Ekki tókst að vista rétthafa');
    }
  };

  updateCompanyInfo = async (companyInfo: CompanyInfoInput, cartItemId: string) => {
    try {
      if (cartItemId) {
        const getContractItem = await this.findContractItem(cartItemId);
        const contractItem = cloneDeep(getContractItem);
        const signupContract = {
          invoiceExplanation: companyInfo?.invoiceExplanation,
          contractExtraPayerInfo: {
            amount: companyInfo?.contractExtraPayerInfo?.amount,
            customerId: companyInfo?.contractExtraPayerInfo?.customerId,
          },
          departmentId: companyInfo?.departmentId,
          type: ContractRequestType.New,
        };

        const purchaseInfo: PurchaseInfo = {
          ...signupContract,
        };
        if (contractItem?.id && contractItem?.purchaseInfo) {
          await this.cart.updateCartItem({
            id: contractItem?.id,
            variantId: contractItem?.variantId,
            quantity: contractItem?.quantity,
            purchaseInfo: purchaseInfo as PurchaseInfoInput,
          });
        }
      }
    } catch (error) {
      throwErrorWithMessage(error, 'Ekki tókst að vista breytingar');
    }
  };

  // Helpers

  // Fill out purchaseInfo based on subscription
  fillPurchaseInfo = async (cartItemId: string) => {
    const cart = await this.cart.fetchCart();
    const serviceItem = await this.findServiceItem(cartItemId);
    const serviceCartItemId = serviceItem?.id;
    const serviceItemPurchaseInfo = serviceItem?.purchaseInfo;
    if (serviceItemPurchaseInfo?.service) {
      const service = serviceItemPurchaseInfo.service;
      const contract = serviceItemPurchaseInfo.contract;
      if (
        service.__typename === 'MobileServiceRequest' &&
        contract?.__typename === 'SignupContract'
      ) {
        this.servicePurchaseInfo = {
          service: {
            phoneNumber: service.phoneNumber,
            isNewNumber: service.isNewNumber,
            portInDate: service.portInDate,
            type: ServiceRequestType.Mobile,
            mobileSignupRightHolder: {
              nationalId: service.mobileSignupRightHolder?.nationalId ?? '',
              email: service.mobileSignupRightHolder?.email ?? '',
            },
            user: {
              customerId: service.user.customerId || '',
              email: service.user.email || '',
              name: service.user.name || '',
              nationalId: service.user.nationalId || '',
              phoneNumber: service.user.phoneNumber || '',
            },
          },
          contract: {
            cartItemId: contract.cartItemId,
            type: ContractRequestType.New,
          },
        };
      }
    }

    if (serviceCartItemId) {
      const simCardPurchaseInfo = cart?.items.filter((item) => {
        return item.purchaseInfo?.serviceCartItemId === serviceCartItemId;
      });

      simCardPurchaseInfo?.map((item) => {
        if (item.variantId === SIM_CARD || item.variantId === E_SIM_CARD) {
          this.simCardPurchaseInfo = {
            ...this.simCardPurchaseInfo,
            iccid: item.purchaseInfo?.iccid || '',
            serviceCartItemId: item.purchaseInfo?.serviceCartItemId || '',
            simCardType: item.purchaseInfo?.simCardType || undefined,
          };
        }

        if (item.variantId.includes('simanumer')) {
          this.simCardPurchaseInfo = {
            ...this.simCardPurchaseInfo,
            phoneNumber: item.purchaseInfo?.phoneNumber || '',
            numberType: item.purchaseInfo?.numberType || undefined,
          };
        }
      });
    }
  };

  // Find Parent Subscription
  // There can be multiple 'askrift-25gb' for example
  // This searches for subscription that doesn't have
  // a service depending on it already.
  findParentSubscription = async (subscriptionId: string) => {
    const cart = await this.cart.fetchCart();
    const subscription = cart?.items.filter((item) => item.variantId === subscriptionId);
    if (subscription && cart?.items.length === 1) return subscription[0].id;

    const idThatDoesntDepend = subscription?.find((subscriptionItem) => {
      return !cart?.items.some((serviceItem) => {
        if (!serviceItem.purchaseInfo?.contract) return false;
        // Return false if its subscription
        else {
          const check =
            serviceItem.purchaseInfo.contract.__typename === 'SignupContract' &&
            serviceItem.purchaseInfo.contract.cartItemId === subscriptionItem.id;
          return check;
        }
      });
    });

    return idThatDoesntDepend?.id;
  };

  // FindServiceItem based on Subscription cartItemId
  // We need this to point extra services to the
  findServiceItem = async (cartItemId: string) => {
    const cart = await this.cart.fetchCart();
    const serviceItem = cart?.items.find((item) => {
      if (item.purchaseInfo?.contract?.__typename === 'SignupContract') {
        return item.purchaseInfo.contract.cartItemId === cartItemId;
      }
    });
    if (serviceItem) {
      return serviceItem as CartItem;
    }
  };

  findContractItem = async (cartItemId: string) => {
    const cart = await this.cart.fetchCart();
    const contractItem = cart?.items.find((item) => {
      return item?.id === cartItemId;
    });
    if (contractItem) {
      return contractItem as CartItem;
    }
  };
  // Function to fetch a phoneNumber when user doesn't have a
  // a primary number registered in our db.
  getPortInNumberFromPayer = async (nationalId: string) => {
    const cart = await this.cart.fetchCart();

    const newPrimaryPhoneNumber = cart?.items.find((item) => {
      if (item.purchaseInfo?.service?.__typename === 'MobileServiceRequest') {
        return (
          item.purchaseInfo.service.mobileSignupRightHolder?.nationalId === nationalId &&
          !item.purchaseInfo.service.isNewNumber
        );
      }
    });

    return (
      newPrimaryPhoneNumber?.purchaseInfo?.service?.__typename === 'MobileServiceRequest' &&
      newPrimaryPhoneNumber.purchaseInfo.service.phoneNumber
    );
  };

  reset = () => {
    this.selectedSubscriptionType = undefined;
    this.setUpdatingCartItemId(undefined);
    this.primaryVariant = '';
    this.secondaryVariant = '';
    this.extraVariants = [];
    this.simCardPurchaseInfo = {
      iccid: '', // For Simcard
      numberType: undefined,
      serviceCartItemId: '',
      phoneNumber: '', // for portable number
      simCardType: undefined,
    };

    this.servicePurchaseInfo = {
      service: {
        portInDate: undefined,
        isUnregisteredPlan: false,
        type: ServiceRequestType.Mobile,
        phoneNumber: '',
        mobileSignupRightHolder: {
          nationalId: '',
          email: '',
        },
        isNewNumber: undefined,
        user: {
          customerId: '',
          email: '',
          name: '',
          nationalId: '',
          phoneNumber: '',
        },
      },
      contract: {
        cartItemId: '',
        type: ContractRequestType.New,
      },
    };
  };

  // When user is in "Update Inputs" Mode. We are using this to
  // update the cart.
  updateItems = async (cartItemId: string) => {
    const cart = await this.cart.fetchCart();
    const subscription = cart?.items.find((item) => item.id === cartItemId);
    if (subscription?.variantId !== this.primaryVariant) {
      await this.updateSubscription(cartItemId);
    }
    await this.updateService(cartItemId);
    await this.updateExtraServices(cartItemId);
  };

  updateSubscription = async (cartItemId: string) => {
    try {
      await this.cart.updateCartItem({
        id: cartItemId,
        quantity: 1,
        variantId: this.primaryVariant,
      });
    } catch (e) {
      throwErrorWithMessage(e, `failed to update subscription: ${cartItemId}`);
    }
  };

  updateService = async (cartItemId: string) => {
    const serviceItem = await this.findServiceItem(cartItemId);

    try {
      await this.cart.updateCartItem({
        id: serviceItem?.id,
        variantId: this.secondaryVariant,
        purchaseInfo: this.servicePurchaseInfo,
        quantity: 1,
      });
    } catch (e) {
      throwErrorWithMessage(e, `Failed to update service: ${serviceItem?.variantId}`);
    }
  };

  updateExtraServices = async (cartItemId: string) => {
    const cart = await this.cart.fetchCart();
    const serviceItem = await this.findServiceItem(cartItemId);
    const serviceCartItemId = serviceItem?.id;
    const allRemovedServices = cart?.items.filter((item) => {
      return (
        item.purchaseInfo?.serviceCartItemId === serviceCartItemId &&
        !this.extraVariants.includes(item.variantId)
      );
    });

    if (allRemovedServices) {
      const removeVariantIds = allRemovedServices.map((item) => item.id);
      await this.removeExtraServices(removeVariantIds);
    }
    try {
      const updateCartItems = cart?.items.filter(
        (item) =>
          item.purchaseInfo?.serviceCartItemId === serviceCartItemId &&
          this.extraVariants.includes(item.variantId),
      );
      const promises = updateCartItems?.map(async (item) => {
        let extraServiceInput;
        if (item.variantId === URLAUSN) return null;
        if (item.variantId.includes(UTLANDAPAKKI)) {
          extraServiceInput = {
            variantId: item.variantId,
            quantity: 1,
            purchaseInfo: {
              serviceCartItemId,
            },
          } as CartItemInput;
        } else if (item.variantId.includes('simanumer')) {
          extraServiceInput = {
            id: item.id,
            variantId: item.variantId,
            quantity: 1,
            purchaseInfo: {
              phoneNumber: this.simCardPurchaseInfo.phoneNumber,
              numberType: this.simCardPurchaseInfo.numberType,
            },
          } as CartItemInput;
        } else if (item.variantId === E_SIM_CARD || item.variantId === SIM_CARD) {
          extraServiceInput = {
            id: item.id,
            variantId: item.variantId,
            quantity: 1,
            purchaseInfo: {
              iccid: this.simCardPurchaseInfo.iccid,
              simCardType: this.simCardPurchaseInfo.simCardType,
              serviceCartItemId,
            },
          } as CartItemInput;
        }

        // Return the promise from addToCart
        if (extraServiceInput) {
          return this.cart.updateCartItem(extraServiceInput);
        }
      });

      // Wait for all promises to resolve
      if (promises) {
        await Promise.all(promises);
      }
    } catch (e) {
      throwErrorWithMessage(e, 'Failed to add extra service to cart');
    }
  };

  storeInCookie = () => {
    this.cookies.set(
      'state',
      {
        subscriptionType: this.selectedSubscriptionType,
        secondary: this.secondaryVariant,
        primary: this.primaryVariant,
      },
      { path: '/' },
    );
  };

  getFromCookie = () => {
    const state = this.cookies.get('state');

    if (!state) {
      return false;
    } else {
      this.selectedSubscriptionType = state.subscriptionType;
      this.primaryVariant = state.primary;
      this.secondaryVariant = state.secondary;
      // Finish the first three steps when user is
      // coming back froum Auth
      if (!this.authentication.isStaff) {
        this.steps[0].isComplete = true;
        this.steps[1].isComplete = true;
        this.steps[2].isComplete = true;
      }

      return true;
    }
  };

  clearCookie = () => {
    this.cookies.remove('state', { path: '/' });
  };

  initSteps = (isCompany: boolean) => {
    const { salesNumber, customerId } = this.cart;
    const hasSalesNumber = !!salesNumber;
    const hasPayer = !!customerId;
    const isStaff = this.authentication.isStaff;

    const baseSteps = isCompany ? companyStepsData : stepsData;
    const withAlltSamanSteps = [...alltSamanOfferStep, ...baseSteps];
    const staffBaseSteps = isCompany ? companyStaffStepsData : staffStepsData;
    const staffWithAlltSamanSteps = isCompany
      ? [...staffSteps, ...companyStepsData]
      : [...staffSteps, ...alltSamanOfferStep, ...stepsData];

    if (isStaff && !hasSalesNumber && !hasPayer) {
      this.steps = hasPayer ? staffBaseSteps : staffWithAlltSamanSteps;
    } else {
      this.steps = hasPayer ? baseSteps : withAlltSamanSteps;
    }
  };

  removeExtraServices = async (cartItems: string[]) => {
    if (cartItems) {
      try {
        const promises = cartItems?.map(async (item) => {
          // Return the promise from addToCart
          return this.cart.removeFromCart(item);
        });
        // Wait for all promises to resolve
        await Promise.all(promises);
      } catch (e) {
        throwErrorWithMessage(e, 'Failed to remove extra service');
      }
    }
  };
}
