import {
    createHandlerChainRunnerOptionalParameter,
    Handler,
    makeHandlerChainAwaitable,
} from "@insite/client-framework/HandlerCreator";
import { Cart } from "@insite/client-framework/Services/CartService";
import { FulfillmentMethod } from "@insite/client-framework/Services/SessionService";
import validateAddress, { AddressErrors } from "@insite/client-framework/Store/CommonHandlers/ValidateAddress";
import { getAddressFieldsDataView } from "@insite/client-framework/Store/Data/AddressFields/AddressFieldsSelector";
import { getBillToState } from "@insite/client-framework/Store/Data/BillTos/BillTosSelectors";
import { getCartState, getCurrentCartState } from "@insite/client-framework/Store/Data/Carts/CartsSelector";
import { getCurrentCountries } from "@insite/client-framework/Store/Data/Countries/CountriesSelectors";
import { getShipToState } from "@insite/client-framework/Store/Data/ShipTos/ShipTosSelectors";
import { BillToModel, CountryModel, ShipToModel } from "@insite/client-framework/Types/ApiModels";
import cloneDeep from "lodash/cloneDeep";

type HandlerType = Handler<
    {},
    {
        cart: Cart;
        billTo: BillToModel;
        shipTo?: ShipToModel;
        country?: CountryModel;
    }
>;

export const SetCart: HandlerType = props => {
    const state = props.getState();
    const { cartId } = state.pages.onePageCheckout;
    const cart = cartId ? getCartState(state, cartId).value : getCurrentCartState(state).value;
    if (!cart) {
        throw new Error("There was no current cart and we are trying to use it to set initial values");
    }

    props.cart = cart;
};

export const SetBillTo: HandlerType = props => {
    if (!props.cart.billToId) {
        return;
    }

    const billTo = getBillToState(props.getState(), props.cart.billToId).value;
    if (!billTo) {
        throw new Error("The bill to for the current cart was not loaded.");
    }

    props.billTo = cloneDeep(billTo);
};

export const SetShipTo: HandlerType = props => {
    if (!props.cart.shipToId) {
        return;
    }

    const shipTo = getShipToState(props.getState(), props.cart.shipToId).value;
    if (!shipTo) {
        throw new Error("The ship to for the current cart was not loaded.");
    }

    props.shipTo = cloneDeep(shipTo);
};

export const GetCountryForBillToAndShipTo: HandlerType = props => {
    const countries = getCurrentCountries(props.getState());
    if (!countries) {
        throw new Error("There were no countries loaded yet");
    }

    if (countries.length === 1) {
        props.country = countries[0];
    }
};

export const DispatchSetInitialValues: HandlerType = props => {
    props.dispatch({
        type: "Pages/OnePageCheckout/SetInitialValues",
        cart: props.cart,
        shipTo: props.shipTo,
        billTo: props.billTo,
        country: props.country,
    });
};

/** @deprecated Not needed anymore */
export const LoadShipTos: HandlerType = async props => {};

export const ValidateBillingAddress: HandlerType = async ({ dispatch, getState, billTo }) => {
    const state = getState();
    const billingAddressFormState = state.pages.onePageCheckout.billingAddressFormState;
    if (!billingAddressFormState) {
        return;
    }

    let addressFieldsDataView = getAddressFieldsDataView(state);
    if (!addressFieldsDataView.value) {
        addressFieldsDataView = getAddressFieldsDataView(state);
    }

    const awaitableValidate = makeHandlerChainAwaitable<Parameters<typeof validateAddress>[0], AddressErrors>(
        validateAddress,
    );
    const billingAddressErrors = await awaitableValidate({
        address: billTo,
        validationRules: billTo.validation!,
        addressFieldDisplayCollection: addressFieldsDataView.value!.billToAddressFields,
    })(dispatch, getState);
    const isBillingAddressValid = Object.keys(billingAddressErrors).length === 0;

    dispatch({
        type: "Pages/OnePageCheckout/SetIsBillingAddressUpdateRequired",
        isBillingAddressUpdateRequired: !isBillingAddressValid,
    });
};

export const ValidateShippingAddress: HandlerType = async ({ dispatch, getState, shipTo }) => {
    const state = getState();
    const session = state.context.session;
    if (!shipTo || session.fulfillmentMethod === FulfillmentMethod.PickUp) {
        return;
    }

    let addressFieldsDataView = getAddressFieldsDataView(state);
    if (!addressFieldsDataView.value) {
        addressFieldsDataView = getAddressFieldsDataView(state);
    }

    const awaitableValidate = makeHandlerChainAwaitable<Parameters<typeof validateAddress>[0], AddressErrors>(
        validateAddress,
    );
    const shippingAddressErrors = await awaitableValidate({
        address: shipTo,
        validationRules: shipTo.validation!,
        addressFieldDisplayCollection: addressFieldsDataView.value!.shipToAddressFields,
    })(dispatch, getState);
    const isShippingAddressValid = Object.keys(shippingAddressErrors).length === 0;

    dispatch({
        type: "Pages/OnePageCheckout/SetIsShippingAddressUpdateRequired",
        isShippingAddressUpdateRequired: !isShippingAddressValid,
    });
};

export const SetPaymentDetailsState: HandlerType = ({ dispatch, getState, shipTo }) => {
    const { isShippingAddressUpdateRequired, isBillingAddressUpdateRequired, paymentDetailsState } =
        getState().pages.onePageCheckout;
    if (paymentDetailsState === "complete") {
        return;
    }

    const newPaymentDetailsState =
        !shipTo || isShippingAddressUpdateRequired || isBillingAddressUpdateRequired ? "waiting" : "active";

    dispatch({
        type: "Pages/OnePageCheckout/SetPaymentDetailsState",
        paymentDetailsState: newPaymentDetailsState,
    });
};

export const chain = [
    SetCart,
    SetBillTo,
    SetShipTo,
    GetCountryForBillToAndShipTo,
    DispatchSetInitialValues,
    ValidateBillingAddress,
    ValidateShippingAddress,
    SetPaymentDetailsState,
];

const setInitialValues = createHandlerChainRunnerOptionalParameter(chain, {}, "SetInitialValues");
export default setInitialValues;
