text: import pick from 'lodash/pick';
  import config from '../../config';
  import { initiatePrivileged, transitionPrivileged } from '../../util/api';
  import { denormalisedResponseEntities } from '../../util/data';
  import { storableError } from '../../util/errors';
  import {
    TRANSITION_REQUEST_PAYMENT,
    TRANSITION_REQUEST_PAYMENT_AFTER_ENQUIRY,
    TRANSITION_CONFIRM_PAYMENT,
    isPrivileged,
  } from '../../util/transaction';
  import * as log from '../../util/log';
  import { fetchCurrentUserHasOrdersSuccess, fetchCurrentUser } from '../../ducks/user.duck';
  
  // ================ Action types ================ //
  
  export const SET_INITIAL_VALUES = 'app/CheckoutPage/SET_INITIAL_VALUES';
  
  export const INITIATE_ORDER_REQUEST = 'app/CheckoutPage/INITIATE_ORDER_REQUEST';
  export const INITIATE_ORDER_SUCCESS = 'app/CheckoutPage/INITIATE_ORDER_SUCCESS';
  export const INITIATE_ORDER_ERROR = 'app/CheckoutPage/INITIATE_ORDER_ERROR';
  
  export const CONFIRM_PAYMENT_REQUEST = 'app/CheckoutPage/CONFIRM_PAYMENT_REQUEST';
  export const CONFIRM_PAYMENT_SUCCESS = 'app/CheckoutPage/CONFIRM_PAYMENT_SUCCESS';
  export const CONFIRM_PAYMENT_ERROR = 'app/CheckoutPage/CONFIRM_PAYMENT_ERROR';
  
  export const SPECULATE_TRANSACTION_REQUEST = 'app/ListingPage/SPECULATE_TRANSACTION_REQUEST';
  export const SPECULATE_TRANSACTION_SUCCESS = 'app/ListingPage/SPECULATE_TRANSACTION_SUCCESS';
  export const SPECULATE_TRANSACTION_ERROR = 'app/ListingPage/SPECULATE_TRANSACTION_ERROR';
  
  export const STRIPE_CUSTOMER_REQUEST = 'app/CheckoutPage/STRIPE_CUSTOMER_REQUEST';
  export const STRIPE_CUSTOMER_SUCCESS = 'app/CheckoutPage/STRIPE_CUSTOMER_SUCCESS';
  export const STRIPE_CUSTOMER_ERROR = 'app/CheckoutPage/STRIPE_CUSTOMER_ERROR';
  
  // ================ Reducer ================ //
  
  const initialState = {
    listing: null,
    offer: null,
    shipping: null,
    bookingData: null,
    bookingDates: null,
    speculateTransactionInProgress: false,
    speculateTransactionError: null,
    speculatedTransaction: null,
    transaction: null,
    initiateOrderError: null,
    confirmPaymentError: null,
    stripeCustomerFetched: false,
  };
  
  export default function checkoutPageReducer(state = initialState, action = {}) {
    const { type, payload } = action;
    switch (type) {
      case SET_INITIAL_VALUES:
        return { ...initialState, ...payload };
  
      case SPECULATE_TRANSACTION_REQUEST:
        return {
          ...state,
          speculateTransactionInProgress: true,
          speculateTransactionError: null,
          speculatedTransaction: null,
        };
      case SPECULATE_TRANSACTION_SUCCESS:
        return {
          ...state,
          speculateTransactionInProgress: false,
          speculatedTransaction: payload.transaction,
        };
      case SPECULATE_TRANSACTION_ERROR:
        console.error(payload); // eslint-disable-line no-console
        return {
          ...state,
          speculateTransactionInProgress: false,
          speculateTransactionError: payload,
        };
  
      case INITIATE_ORDER_REQUEST:
        return { ...state, initiateOrderError: null };
      case INITIATE_ORDER_SUCCESS:
        return { ...state, transaction: payload };
      case INITIATE_ORDER_ERROR:
        console.error(payload); // eslint-disable-line no-console
        return { ...state, initiateOrderError: payload };
  
      case CONFIRM_PAYMENT_REQUEST:
        return { ...state, confirmPaymentError: null };
      case CONFIRM_PAYMENT_SUCCESS:
        return state;
      case CONFIRM_PAYMENT_ERROR:
        console.error(payload); // eslint-disable-line no-console
        return { ...state, confirmPaymentError: payload };
  
      case STRIPE_CUSTOMER_REQUEST:
        return { ...state, stripeCustomerFetched: false };
      case STRIPE_CUSTOMER_SUCCESS:
        return { ...state, stripeCustomerFetched: true };
      case STRIPE_CUSTOMER_ERROR:
        console.error(payload); // eslint-disable-line no-console
        return { ...state, stripeCustomerFetchError: payload };
  
      default:
        return state;
    }
  }
  
  // ================ Selectors ================ //
  
  // ================ Action creators ================ //
  
  export const setInitialValues = initialValues => ({
    type: SET_INITIAL_VALUES,
    payload: pick(initialValues, Object.keys(initialState)),
  });
  
  const initiateOrderRequest = () => ({ type: INITIATE_ORDER_REQUEST });
  
  const initiateOrderSuccess = order => ({
    type: INITIATE_ORDER_SUCCESS,
    payload: order,
  });
  
  const initiateOrderError = e => ({
    type: INITIATE_ORDER_ERROR,
    error: true,
    payload: e,
  });
  
  const confirmPaymentRequest = () => ({ type: CONFIRM_PAYMENT_REQUEST });
  
  const confirmPaymentSuccess = orderId => ({
    type: CONFIRM_PAYMENT_SUCCESS,
    payload: orderId,
  });
  
  const confirmPaymentError = e => ({
    type: CONFIRM_PAYMENT_ERROR,
    error: true,
    payload: e,
  });
  
  export const speculateTransactionRequest = () => ({ type: SPECULATE_TRANSACTION_REQUEST });
  
  export const speculateTransactionSuccess = transaction => ({
    type: SPECULATE_TRANSACTION_SUCCESS,
    payload: { transaction },
  });
  
  export const speculateTransactionError = e => ({
    type: SPECULATE_TRANSACTION_ERROR,
    error: true,
    payload: e,
  });
  
  export const stripeCustomerRequest = () => ({ type: STRIPE_CUSTOMER_REQUEST });
  export const stripeCustomerSuccess = () => ({ type: STRIPE_CUSTOMER_SUCCESS });
  export const stripeCustomerError = e => ({
    type: STRIPE_CUSTOMER_ERROR,
    error: true,
    payload: e,
  });
  
  /* ================ Thunks ================ */
  
  export const initiateOrder = (orderParams, transactionId) => (dispatch, getState, sdk) => {
    dispatch(initiateOrderRequest());
  
    // If we already have a transaction ID, we should transition, not
    // initiate.
    const isTransition = !!transactionId;
    const transition = isTransition
      ? TRANSITION_REQUEST_PAYMENT_AFTER_ENQUIRY
      : TRANSITION_REQUEST_PAYMENT;
    const isPrivilegedTransition = isPrivileged(transition);
  
    const bookingData = {
      offer: orderParams.offer,
      lineItems: orderParams.lineItems
    };
  
    const bodyParams = isTransition
      ? {
          id: transactionId,
          transition,
          params: orderParams,
        }
      : {
          processAlias: config.bookingProcessAlias,
          transition,
          params: orderParams,
        };
    const queryParams = {
      include: ['booking', 'provider'],
      expand: true,
    };
  
    const handleSucces = response => {
      const entities = denormalisedResponseEntities(response);
      const order = entities[0];
      dispatch(initiateOrderSuccess(order));
      dispatch(fetchCurrentUserHasOrdersSuccess(true));
      return order;
    };
  
    const handleError = e => {
      dispatch(initiateOrderError(storableError(e)));
      const transactionIdMaybe = transactionId ? { transactionId: transactionId.uuid } : {};
      log.error(e, 'initiate-order-failed', {
        ...transactionIdMaybe,
        listingId: orderParams.listingId.uuid,
        bookingStart: orderParams.bookingStart,
        bookingEnd: orderParams.bookingEnd,
      });
      throw e;
    };
  
    if (isTransition && isPrivilegedTransition) {
      // transition privileged
      return transitionPrivileged({ isSpeculative: false, bookingData, bodyParams, queryParams })
        .then(handleSucces)
        .catch(handleError);
    } else if (isTransition) {
      // transition non-privileged
      return sdk.transactions
        .transition(bodyParams, queryParams)
        .then(handleSucces)
        .catch(handleError);
    } else if (isPrivilegedTransition) {
      // initiate privileged
      return initiatePrivileged({ isSpeculative: false, bookingData, bodyParams, queryParams })
        .then(handleSucces)
        .catch(handleError);
    } else {
      // initiate non-privileged
      return sdk.transactions
        .initiate(bodyParams, queryParams)
        .then(handleSucces)
        .catch(handleError);
    }
  };
  
  export const confirmPayment = orderParams => (dispatch, getState, sdk) => {
    dispatch(confirmPaymentRequest());
  
    const bodyParams = {
      id: orderParams.transactionId,
      transition: TRANSITION_CONFIRM_PAYMENT,
      params: {},
    };
  
    return sdk.transactions
      .transition(bodyParams)
      .then(response => {
        const order = response.data.data;
        dispatch(confirmPaymentSuccess(order.id));
        return order;
      })
      .catch(e => {
        dispatch(confirmPaymentError(storableError(e)));
        const transactionIdMaybe = orderParams.transactionId
          ? { transactionId: orderParams.transactionId.uuid }
          : {};
        log.error(e, 'initiate-order-failed', {
          ...transactionIdMaybe,
        });
        throw e;
      });
  };
  
  export const sendMessage = params => (dispatch, getState, sdk) => {
    const message = params.message;
    const orderId = params.id;
  
    if (message) {
      return sdk.messages
        .send({ transactionId: orderId, content: message })
        .then(() => {
          return { orderId, messageSuccess: true };
        })
        .catch(e => {
          log.error(e, 'initial-message-send-failed', { txId: orderId });
          return { orderId, messageSuccess: false };
        });
    } else {
      return Promise.resolve({ orderId, messageSuccess: true });
    }
  };
  
  /**
   * Initiate or transition the speculative transaction with the given
   * booking details
   *
   * The API allows us to do speculative transaction initiation and
   * transitions. This way we can create a test transaction and get the
   * actual pricing information as if the transaction had been started,
   * without affecting the actual data.
   *
   * We store this speculative transaction in the page store and use the
   * pricing info for the booking breakdown to get a proper estimate for
   * the price with the chosen information.
   */
  export const speculateTransaction = (orderParams, transactionId) => (dispatch, getState, sdk) => {
    dispatch(speculateTransactionRequest());
  
    // If we already have a transaction ID, we should transition, not
    // initiate.
    const isTransition = !!transactionId;
    const transition = isTransition
      ? TRANSITION_REQUEST_PAYMENT_AFTER_ENQUIRY
      : TRANSITION_REQUEST_PAYMENT;
    const isPrivilegedTransition = isPrivileged(transition);
  
    const bookingData = {
      offer: orderParams.offer,
      lineItems: orderParams.lineItems
    };
  
    const params = {
      ...orderParams,
      cardToken: 'CheckoutPage_speculative_card_token',
    };
  
    const bodyParams = isTransition
      ? {
          id: transactionId,
          transition,
          params,
        }
      : {
          processAlias: config.bookingProcessAlias,
          transition,
          params,
        };
  
    const queryParams = {
      include: ['booking', 'provider'],
      expand: true,
    };
  
    const handleSuccess = response => {
      const entities = denormalisedResponseEntities(response);
      if (entities.length !== 1) {
        throw new Error('Expected a resource in the speculate response');
      }
      const tx = entities[0];
      dispatch(speculateTransactionSuccess(tx));
    };
  
    const handleError = e => {
      // const { listingId, bookingStart, bookingEnd } = params;
      const { listingId } = params;
  
      log.error(e, 'speculate-transaction-failed', {
        listingId: listingId.uuid,
      });
      return dispatch(speculateTransactionError(storableError(e)));
    };
  
    if (isTransition && isPrivilegedTransition) {
      // transition privileged
      return transitionPrivileged({ isSpeculative: true, bookingData, bodyParams, queryParams })
        .then(handleSuccess)
        .catch(handleError);
    } else if (isTransition) {
      // transition non-privileged
      return sdk.transactions
        .transitionSpeculative(bodyParams, queryParams)
        .then(handleSuccess)
        .catch(handleError);
    } else if (isPrivilegedTransition) {
      // initiate privileged
      return initiatePrivileged({ isSpeculative: true, bookingData, bodyParams, queryParams })
        .then(handleSuccess)
        .catch(handleError);
    } else {
      // initiate non-privileged
      return sdk.transactions
        .initiateSpeculative(bodyParams, queryParams)
        .then(handleSuccess)
        .catch(handleError);
    }
  };
  
  // StripeCustomer is a relantionship to currentUser
  // We need to fetch currentUser with correct params to include relationship
  export const stripeCustomer = () => (dispatch, getState, sdk) => {
    dispatch(stripeCustomerRequest());
  
    return dispatch(fetchCurrentUser({ include: ['stripeCustomer.defaultPaymentMethod'] }))
      .then(response => {
        dispatch(stripeCustomerSuccess());
      })
      .catch(e => {
        dispatch(stripeCustomerError(storableError(e)));
      });
  };
  