import { createReducer, on } from '@ngrx/store';
import {
  initialCheckoutState,
  storeCheckoutAdapter,
} from './domain-checkout.state';

import * as DomainCheckoutActions from './domain-checkout.actions';
import { Dictionary } from '@ngrx/entity';
import { CheckoutEntity } from './defs/checkout.entity';
import { isNullOrUndefined } from '@utils/common';

const _domainCheckoutReducer = createReducer(
  initialCheckoutState,
  on(
    DomainCheckoutActions.setShoppingCart,
    (s, { processId, shoppingCart }) => {
      const entity = getOrCreateCheckoutEntity({
        processId,
        entities: s.entities,
      });

      const addedShoppingCartItems =
        shoppingCart?.items
          ?.filter(
            (item) =>
              !entity.shoppingCart?.items?.find((i) => i.id === item.id),
          )
          ?.map((item) => item.data) ?? [];

      entity.shoppingCart = shoppingCart;

      entity.itemAvailabilityTimestamp = entity.itemAvailabilityTimestamp
        ? { ...entity.itemAvailabilityTimestamp }
        : {};

      const now = Date.now();

      for (const shoppingCartItem of addedShoppingCartItems) {
        if (shoppingCartItem.features?.orderType) {
          entity.itemAvailabilityTimestamp[
            `${shoppingCartItem.id}_${shoppingCartItem.features.orderType}`
          ] = now;
        }
      }

      return storeCheckoutAdapter.setOne(entity, s);
    },
  ),
  on(
    DomainCheckoutActions.setShoppingCartByShoppingCartId,
    (s, { shoppingCartId, shoppingCart }) => {
      let entity = getCheckoutEntityByShoppingCartId({
        shoppingCartId,
        entities: s.entities,
      });

      if (!entity) {
        // No entity found for this shoppingCartId, cannot update
        return s;
      }

      entity = { ...entity, shoppingCart };

      return storeCheckoutAdapter.setOne(entity, s);
    },
  ),
  on(DomainCheckoutActions.setCheckout, (s, { processId, checkout }) => {
    const entity = getOrCreateCheckoutEntity({
      processId,
      entities: s.entities,
    });
    entity.checkout = checkout;
    return storeCheckoutAdapter.setOne(entity, s);
  }),
  on(
    DomainCheckoutActions.setBuyerCommunicationDetails,
    (s, { processId, email, mobile }) => {
      const entity = getOrCreateCheckoutEntity({
        processId,
        entities: s.entities,
      });
      const communicationDetails = { ...entity.buyer.communicationDetails };
      communicationDetails.email = email || communicationDetails.email;
      communicationDetails.mobile = mobile || communicationDetails.mobile;
      entity.buyer = {
        ...entity.buyer,
        communicationDetails,
      };
      return storeCheckoutAdapter.setOne(entity, s);
    },
  ),
  on(
    DomainCheckoutActions.setNotificationChannels,
    (s, { processId, notificationChannels }) => {
      const entity = getOrCreateCheckoutEntity({
        processId,
        entities: s.entities,
      });
      return storeCheckoutAdapter.setOne(
        { ...entity, notificationChannels },
        s,
      );
    },
  ),
  on(
    DomainCheckoutActions.setCheckoutDestination,
    (s, { processId, destination }) => {
      const entity = getOrCreateCheckoutEntity({
        processId,
        entities: s.entities,
      });
      entity.checkout = {
        ...entity.checkout,
        destinations: entity.checkout.destinations.map((dest) => {
          if (dest.id === destination.id) {
            return { ...dest, ...destination };
          }
          return { ...dest };
        }),
      };
      return storeCheckoutAdapter.setOne(entity, s);
    },
  ),
  on(
    DomainCheckoutActions.setShippingAddress,
    (s, { processId, shippingAddress }) => {
      const entity = getOrCreateCheckoutEntity({
        processId,
        entities: s.entities,
      });
      entity.shippingAddress = shippingAddress;
      return storeCheckoutAdapter.setOne(entity, s);
    },
  ),
  on(DomainCheckoutActions.setBuyer, (s, { processId, buyer }) => {
    const entity = getOrCreateCheckoutEntity({
      processId,
      entities: s.entities,
    });
    entity.buyer = buyer;
    return storeCheckoutAdapter.setOne(entity, s);
  }),
  on(DomainCheckoutActions.setPayer, (s, { processId, payer }) => {
    const entity = getOrCreateCheckoutEntity({
      processId,
      entities: s.entities,
    });
    entity.payer = payer;
    return storeCheckoutAdapter.setOne(entity, s);
  }),
  on(
    DomainCheckoutActions.setSpecialComment,
    (s, { processId, agentComment }) => {
      const entity = getOrCreateCheckoutEntity({
        processId,
        entities: s.entities,
      });
      entity.specialComment = agentComment;
      return storeCheckoutAdapter.setOne(entity, s);
    },
  ),
  on(DomainCheckoutActions.removeCheckoutWithProcessId, (s, { processId }) => {
    return storeCheckoutAdapter.removeOne(processId, s);
  }),
  on(DomainCheckoutActions.setOrders, (s, { orders }) => ({
    ...s,
    orders: [...s.orders, ...orders],
  })),
  on(DomainCheckoutActions.updateOrderItem, (s, { item }) => {
    const orders = [...s.orders];

    const orderToUpdate = orders?.find((order) =>
      order.items?.find((i) => i.id === item?.id),
    );
    const orderToUpdateIndex = orders?.indexOf(orderToUpdate);

    const orderItemToUpdate = orderToUpdate?.items?.find(
      (i) => i.id === item?.id,
    );
    const orderItemToUpdateIndex =
      orderToUpdate?.items?.indexOf(orderItemToUpdate);

    const items = [...(orderToUpdate?.items ?? [])];
    items[orderItemToUpdateIndex] = item;

    orders[orderToUpdateIndex] = {
      ...orderToUpdate,
      items: [...items],
    };

    return { ...s, orders: [...orders] };
  }),
  on(DomainCheckoutActions.removeAllOrders, (s) => ({
    ...s,
    orders: [],
  })),
  on(DomainCheckoutActions.setOlaError, (s, { processId, olaErrorIds }) => {
    const entity = getOrCreateCheckoutEntity({
      processId,
      entities: s.entities,
    });
    entity.olaErrorIds = olaErrorIds;
    return storeCheckoutAdapter.setOne(entity, s);
  }),
  on(DomainCheckoutActions.setCustomer, (s, { processId, customer }) => {
    const entity = getOrCreateCheckoutEntity({
      processId,
      entities: s.entities,
    });
    entity.customer = customer;
    return storeCheckoutAdapter.setOne(entity, s);
  }),
  on(
    DomainCheckoutActions.addShoppingCartItemAvailabilityToHistory,
    (s, { processId, shoppingCartItemId, availability }) => {
      const entity = getOrCreateCheckoutEntity({
        processId,
        entities: s.entities,
      });

      const itemAvailabilityTimestamp = entity?.itemAvailabilityTimestamp
        ? { ...entity?.itemAvailabilityTimestamp }
        : {};

      const item = entity?.shoppingCart?.items?.find(
        (i) => i.id === shoppingCartItemId,
      )?.data;

      if (!item?.features?.orderType) return s;

      itemAvailabilityTimestamp[`${item.id}_${item?.features?.orderType}`] =
        Date.now();

      entity.itemAvailabilityTimestamp = itemAvailabilityTimestamp;

      return storeCheckoutAdapter.setOne(entity, s);
    },
  ),
  on(
    DomainCheckoutActions.addShoppingCartItemAvailabilityToHistoryByShoppingCartId,
    (s, { shoppingCartId, shoppingCartItemId, availability }) => {
      const entity = getCheckoutEntityByShoppingCartId({
        shoppingCartId,
        entities: s.entities,
      });

      const itemAvailabilityTimestamp = entity?.itemAvailabilityTimestamp
        ? { ...entity?.itemAvailabilityTimestamp }
        : {};

      const item = entity?.shoppingCart?.items?.find(
        (i) => i.id === shoppingCartItemId,
      )?.data;

      if (!item?.features?.orderType) return s;

      itemAvailabilityTimestamp[`${item.id}_${item?.features?.orderType}`] =
        Date.now();

      entity.itemAvailabilityTimestamp = itemAvailabilityTimestamp;

      return storeCheckoutAdapter.setOne(entity, s);
    },
  ),
);

export function domainCheckoutReducer(state, action) {
  return _domainCheckoutReducer(state, action);
}

function getOrCreateCheckoutEntity({
  entities,
  processId,
}: {
  entities: Dictionary<CheckoutEntity>;
  processId: number;
}): CheckoutEntity {
  const entity = entities[processId];

  if (isNullOrUndefined(entity)) {
    return {
      processId,
      checkout: undefined,
      shoppingCart: undefined,
      shippingAddress: undefined,
      orders: [],
      payer: undefined,
      buyer: undefined,
      specialComment: '',
      notificationChannels: 0,
      olaErrorIds: [],
      customer: undefined,
      // availabilityHistory: [],
      itemAvailabilityTimestamp: {},
    };
  }

  return { ...entity };
}

function getCheckoutEntityByShoppingCartId({
  entities,
  shoppingCartId,
}: {
  entities: Dictionary<CheckoutEntity>;
  shoppingCartId: number;
}): CheckoutEntity {
  return Object.values(entities).find(
    (entity) => entity.shoppingCart?.id === shoppingCartId,
  );
}
