import { Cookies } from 'react-cookie';
import { ApolloClient } from '@apollo/client';
import { ADD_MOBILE_TO_CART } from 'graphql/queries/cart/addToCart';
import { MOBILE_CART_QUERY } from 'graphql/queries/cart/getCart';
import { REMOVE_FROM_CART } from 'graphql/queries/cart/removeFromCart';
import { ADD_SHIPPING_METHOD } from 'graphql/queries/cart/shippingMethods';
import { UPDATE_CART_CONTACT, UPDATE_CART_ITEM } from 'graphql/queries/cart/updateCart';
import { identity, pickBy } from 'lodash';
import { action, extendObservable, makeObservable, observable } from 'mobx';
import {
  AddMobileSignupToCartMutation,
  AddToCartInput,
  CartItemInput,
  ContactInfoInput,
  MobileCartQuery,
  RemoveFromCartInput,
  RemoveFromCartMutation,
  ServiceRequestType,
  ShippingMethodInput,
  ShippingType,
} from 'typings/graphql';

import Authentication from './authentication';
import { throwErrorWithMessage } from './mobileSignup';

type AddToCartType = 'mobileSignup';

export default class Cart {
  private client: ApolloClient<object>;
  private authentication: Authentication;

  constructor(
    { cart = {} },
    client: ApolloClient<object>,
    authentication: Authentication,
    cookies: string | object | null | undefined,
  ) {
    this.cookies = new Cookies(cookies);
    makeObservable(this, {
      setActiveCartId: action,
      activeCartId: observable,
      shipmentOptions: observable,
      salesNumber: observable,
      customerId: observable,
      nationalId: observable,
      customerName: observable,
      customerAddress: observable,
      addToCart: action,
    });
    extendObservable(this, cart);
    this.client = client;
    this.authentication = authentication;
  }
  cookies;
  activeCartId: string = '';
  salesNumber: string = '';
  customerId: string = '';
  nationalId: string = '';
  customerName: string = '';
  customerAddress: string = '';

  // ShippingMethods
  shipmentOptions: ShippingMethodInput = {
    shippingType: ShippingType.Pickup,
    id: '',
    shippingDetails: {
      address: '',
      zip: '',
      dropp: '',
      heimsending: '',
      postbox: '',
      storename: 'lagmuli',
    },
  };

  setSalesNumber = (salesNumber: string) => {
    this.salesNumber = salesNumber;
  };
  setCustomerId = (customerId: string) => {
    this.customerId = customerId;
  };

  setCustomerName = (name: string) => {
    this.customerName = name;
  };

  setCustomerAddress = (address: string) => {
    this.customerAddress = address;
  };

  setNationalId = (nationalId: string) => {
    this.nationalId = nationalId;
  };
  setShippingMethods = (shipment: ShippingMethodInput) => {
    this.shipmentOptions = shipment;
  };

  setActiveCartId(cartId: string) {
    this.activeCartId = cartId;

    if (this.authentication.isStaff) {
      this.cookies.remove('cartId', { path: '/' });
      return;
    }

    if (!cartId || cartId === '') {
      this.cookies.remove('cartId', { path: '/' });
    } else {
      const expiryDate = new Date();
      expiryDate.setHours(expiryDate.getHours() + 12);

      this.cookies.set('cartId', cartId, {
        path: '/',
        expires: expiryDate,
        secure: true,
      });
    }
  }

  fetchCart = async () => {
    if (this.activeCartId) {
      try {
        const { data } = await this.client.query<MobileCartQuery>({
          query: MOBILE_CART_QUERY,
          variables: { input: { cartId: this.activeCartId } },
        });
        return data?.cart;
      } catch (error) {
        throwErrorWithMessage(error, 'Could not fetch cart');
      }
    }
  };

  addToCart = async (type: AddToCartType, item: CartItemInput) => {
    try {
      const input: AddToCartInput = {
        item,
        customer: {
          ssn: this.nationalId,
        },
        cartId: this.activeCartId ?? '',
      };
      let res;

      switch (type) {
        case 'mobileSignup':
          res = await this.client.mutate<AddMobileSignupToCartMutation>({
            mutation: ADD_MOBILE_TO_CART,
            variables: { input },
          });
      }

      const { data } = res;
      if (!this.activeCartId && data?.addToCart.cart?.id) {
        this.setActiveCartId(this.activeCartId);
      }
      return { cart: data };
    } catch (e) {
      throwErrorWithMessage(e, 'Failed to add to cart');
    }
  };

  removeFromCart = async (cartItemId: string) => {
    if (cartItemId) {
      try {
        const input: RemoveFromCartInput = {
          cartId: this.activeCartId,
          itemId: cartItemId,
        };
        const res = await this.client.mutate<RemoveFromCartMutation>({
          mutation: REMOVE_FROM_CART,
          variables: { input },
        });

        const { data } = res;

        if (data?.removeFromCart?.cart?.id) {
          this.activeCartId = data?.removeFromCart.cart.id;
        }
      } catch (e) {
        throwErrorWithMessage(e, 'Failed to add to cart');
      }
    }
  };

  updateCartItem = async (item: CartItemInput) => {
    if (item?.purchaseInfo) {
      item.purchaseInfo = pickBy(item.purchaseInfo, identity);
      if (item.purchaseInfo.__typename) {
        delete item.purchaseInfo.__typename;
      }
      if (item.purchaseInfo?.contractExtraPayerInfo) {
        item.purchaseInfo.contractExtraPayerInfo = pickBy(
          item.purchaseInfo.contractExtraPayerInfo,
          identity,
        );
        if (item.purchaseInfo.contractExtraPayerInfo.__typename) {
          delete item.purchaseInfo.contractExtraPayerInfo.__typename;
        }
      }
      if (item?.purchaseInfo?.contract) {
        item.purchaseInfo.contract = pickBy(item.purchaseInfo.contract, identity);
        if (item.purchaseInfo.contract.__typename) {
          delete item.purchaseInfo.contract.__typename;
        }
      }
      if (item?.purchaseInfo?.service) {
        if (item.purchaseInfo.service.type !== ServiceRequestType.Mobile) {
          item.purchaseInfo.service = pickBy(item.purchaseInfo.service, identity);
        }
        if (item.purchaseInfo.service.__typename) {
          delete item.purchaseInfo.service.__typename;
        }
      }
      if (item?.purchaseInfo?.service?.user) {
        item.purchaseInfo.service.user = pickBy(item.purchaseInfo.service.user, identity);
        if (item.purchaseInfo.service.user?.__typename) {
          delete item.purchaseInfo.service.user.__typename;
        }
      }
      if (item?.purchaseInfo?.service?.mobileSignupRightHolder) {
        item.purchaseInfo.service.mobileSignupRightHolder = pickBy(
          item.purchaseInfo.service.mobileSignupRightHolder,
          identity,
        );
        if (item.purchaseInfo.service.mobileSignupRightHolder?.__typename) {
          delete item.purchaseInfo.service.mobileSignupRightHolder.__typename;
        }
      }
      if (item?.purchaseInfo?.service?.appointment) {
        item.purchaseInfo.service.appointment = pickBy(
          item.purchaseInfo.service.appointment,
          identity,
        );
        if (item?.purchaseInfo?.service?.appointment?.__typename) {
          delete item.purchaseInfo.service.appointment.__typename;
        }
      }
      if (item?.purchaseInfo?.service?.refillInfo) {
        item.purchaseInfo.service.refillInfo = pickBy(item.purchaseInfo.rental, identity);
        if (item?.purchaseInfo?.service?.refillInfo?.__typename) {
          delete item.purchaseInfo.service?.refillInfo?.__typename;
        }
      }
      if (item?.purchaseInfo?.rental) {
        item.purchaseInfo.rental = pickBy(item.purchaseInfo.rental, identity);
        if (item?.purchaseInfo?.rental?.__typename) {
          delete item.purchaseInfo.rental.__typename;
        }
      }
    }

    try {
      const res = await this.client.mutate({
        mutation: UPDATE_CART_ITEM,
        variables: { input: { cartId: this.activeCartId, item } },
        update: (_, { data }) => {
          this.client.writeQuery({
            query: UPDATE_CART_ITEM,
            variables: { input: { cartId: this.activeCartId } },
            data,
          });
        },
      });

      const {
        data: {
          updateCartItem: { cart },
        },
      } = res;

      this.activeCartId = cart.id;
    } catch (e) {
      throwErrorWithMessage(e, 'Failed to update cart');
    }
  };
  updateCartContact = async (contactInfo: ContactInfoInput) => {
    try {
      await this.client.mutate({
        mutation: UPDATE_CART_CONTACT,
        variables: { input: { cartId: this.activeCartId, contactInfo: contactInfo } },
        update: (_, { data }) => {
          this.client.writeQuery({
            query: UPDATE_CART_CONTACT,
            variables: { input: { cartId: this.activeCartId, contactInfo: contactInfo } },
            data,
          });
        },
      });
    } catch (e) {
      throwErrorWithMessage(e, 'Failed to update cart contact');
    }
  };

  addShippingMethod = async () => {
    try {
      const res = await this.client.mutate({
        mutation: ADD_SHIPPING_METHOD,
        variables: { input: { cartId: this.activeCartId, shippingMethod: this.shipmentOptions } },
      });

      const {
        data: {
          addShippingMethod: { cart },
        },
      } = res;

      this.activeCartId = cart.id;
    } catch (e) {
      throwErrorWithMessage(e, 'Failed to update cart contact');
    }
  };
}
