import { scrollToFirstFormError } from "@insite/client-framework/Common/CustomerSelectors";
import StyledWrapper from "@insite/client-framework/Common/StyledWrapper";
import { makeHandlerChainAwaitable } from "@insite/client-framework/HandlerCreator";
import { FulfillmentMethod } from "@insite/client-framework/Services/SessionService";
import ApplicationState from "@insite/client-framework/Store/ApplicationState";
import setAddressBookOverlayIsOpen from "@insite/client-framework/Store/Components/AddressBookOverlay/Handlers/SetAddressBookOverlayIsOpen";
import setStorePickupOverlayIsOpen from "@insite/client-framework/Store/Components/StorePickupOverlay/Handlers/SetStorePickupOverlayIsOpen";
import { getCurrentUserIsGuest, getSession } from "@insite/client-framework/Store/Context/ContextSelectors";
import updatePickUpWarehouse from "@insite/client-framework/Store/Context/Handlers/UpdatePickUpWarehouse";
import { getAddressFieldsDataView } from "@insite/client-framework/Store/Data/AddressFields/AddressFieldsSelector";
import { getBillToState } from "@insite/client-framework/Store/Data/BillTos/BillTosSelectors";
import loadBillTo from "@insite/client-framework/Store/Data/BillTos/Handlers/LoadBillTo";
import { getCartState, getCurrentCartState } from "@insite/client-framework/Store/Data/Carts/CartsSelector";
import updatePickUpWarehouseForCart from "@insite/client-framework/Store/Data/Carts/Handlers/UpdatePickUpWarehouse";
import { getCurrentCountries } from "@insite/client-framework/Store/Data/Countries/CountriesSelectors";
import loadShipTo from "@insite/client-framework/Store/Data/ShipTos/Handlers/LoadShipTo";
import loadShipTos from "@insite/client-framework/Store/Data/ShipTos/Handlers/LoadShipTos";
import { getShipTosDataView, getShipToState } from "@insite/client-framework/Store/Data/ShipTos/ShipTosSelectors";
import saveShipping from "@insite/client-framework/Store/Pages/OnePageCheckout/Handlers/SaveShipping";
import setIsBillingAddressTheSameAsShipping from "@insite/client-framework/Store/Pages/OnePageCheckout/Handlers/SetIsBillingAddressTheSameAsShipping";
import setPaymentDetailsState from "@insite/client-framework/Store/Pages/OnePageCheckout/Handlers/SetPaymentDetailsState";
import translate from "@insite/client-framework/Translate";
import { WarehouseModel } from "@insite/client-framework/Types/ApiModels";
import WidgetModule from "@insite/client-framework/Types/WidgetModule";
import WidgetProps from "@insite/client-framework/Types/WidgetProps";
import AddressInfoDisplay, { AddressInfoDisplayStyles } from "@insite/content-library/Components/AddressInfoDisplay";
import FindLocationModal, { FindLocationModalStyles } from "@insite/content-library/Components/FindLocationModal";
import { OnePageCheckoutFormContext } from "@insite/content-library/Pages/OnePageCheckoutPage";
import BillingAddressForm, {
    BillingAddressFormStyles,
} from "@insite/content-library/Widgets/OnePageCheckout/BillingAddressForm";
import BillToSelectorModal, {
    BillToSelectorModalStyles,
} from "@insite/content-library/Widgets/OnePageCheckout/BillToSelectorModal";
import ShippingAddressForm, {
    ShippingAddressFormStyles,
} from "@insite/content-library/Widgets/OnePageCheckout/ShippingAddressForm";
import ShipToSelectorModal, {
    ShipToSelectorModalStyles,
} from "@insite/content-library/Widgets/OnePageCheckout/ShipToSelectorModal";
import Button, { ButtonPresentationProps } from "@insite/mobius/Button";
import Checkbox, { CheckboxPresentationProps, CheckboxProps } from "@insite/mobius/Checkbox";
import { BaseTheme } from "@insite/mobius/globals/baseTheme";
import GridContainer, { GridContainerProps } from "@insite/mobius/GridContainer";
import GridItem, { GridItemProps } from "@insite/mobius/GridItem";
import Link, { LinkPresentationProps } from "@insite/mobius/Link";
import LoadingOverlay from "@insite/mobius/LoadingOverlay";
import LoadingSpinner from "@insite/mobius/LoadingSpinner";
import ToasterContext from "@insite/mobius/Toast/ToasterContext";
import Typography, { TypographyPresentationProps } from "@insite/mobius/Typography";
import InjectableCss from "@insite/mobius/utilities/InjectableCss";
import VisuallyHidden from "@insite/mobius/VisuallyHidden";
import React, { useContext, useEffect, useState } from "react";
import { connect, ResolveThunks } from "react-redux";
import { css, ThemeProps, withTheme } from "styled-components";

const mapStateToProps = (state: ApplicationState) => {
    const { cartId } = state.pages.onePageCheckout;
    const cartState = cartId ? getCartState(state, cartId) : getCurrentCartState(state);
    const { value: cart } = cartState;
    const shipTosDataView = cart
        ? getShipTosDataView(state, { billToId: cart.billToId, expand: ["validation"], exclude: ["showAll"] })
        : undefined;
    const session = getSession(state);
    const fulfillmentMethod = cart?.fulfillmentMethod || session.fulfillmentMethod;
    const pickUpWarehouse = cart?.defaultWarehouse || session.pickUpWarehouse;
    let newAddress;
    if (cart && cart.cartLines && shipTosDataView && shipTosDataView.value) {
        const shipTos = shipTosDataView.value;
        newAddress = shipTos!.find(s => s.isNew && !s.oneTimeAddress);
    }
    return {
        isShippingAddressUpdateRequired: state.pages.onePageCheckout.isShippingAddressUpdateRequired,
        isBillingAddressUpdateRequired: state.pages.onePageCheckout.isBillingAddressUpdateRequired,
        isBillingAddressTheSameAsShipping: state.pages.onePageCheckout.isBillingAddressTheSameAsShipping,
        isSavingShipping: state.pages.onePageCheckout.isSavingShipping,
        cart,
        shipToState: getShipToState(state, cart?.shipToId),
        billToState: getBillToState(state, cart?.billToId),
        currentUserIsGuest: getCurrentUserIsGuest(state),
        countries: getCurrentCountries(state),
        shipTosDataView: cart
            ? getShipTosDataView(state, { billToId: cart.billToId, expand: ["validation"], exclude: ["showAll"] })
            : undefined,
        shipToAddressFields: getAddressFieldsDataView(state).value?.shipToAddressFields,
        billToAddressFields: getAddressFieldsDataView(state).value?.billToAddressFields,
        fulfillmentMethod,
        pickUpWarehouse,
        newAddress,
        updateCartInsteadOfContext: cart?.isAwaitingApproval,
    };
};

const mapDispatchToProps = {
    loadShipTos,
    loadShipTo,
    loadBillTo,
    setIsBillingAddressTheSameAsShipping,
    setAddressBookOverlayIsOpen,
    setStorePickupOverlayIsOpen,
    saveShipping,
    setPaymentDetailsState,
    updatePickUpWarehouse: makeHandlerChainAwaitable(updatePickUpWarehouse),
    updatePickUpWarehouseForCart: makeHandlerChainAwaitable(updatePickUpWarehouseForCart),
};

type Props = WidgetProps &
    ReturnType<typeof mapStateToProps> &
    ResolveThunks<typeof mapDispatchToProps> &
    ThemeProps<BaseTheme>;

export interface OnePageCheckoutShippingSectionStyles {
    loadingWrapper?: InjectableCss;
    container?: GridContainerProps;
    titleGridItem?: GridItemProps;
    titleText?: TypographyPresentationProps;
    titleEditGridItem?: GridItemProps;
    titleEditLink?: LinkPresentationProps;
    pickUpGridItem?: GridItemProps;
    headingGridItem?: GridItemProps;
    heading?: TypographyPresentationProps;
    editLink?: LinkPresentationProps;
    shipToSelectorModal?: ShipToSelectorModalStyles;
    billToSelectorModal?: BillToSelectorModalStyles;
    shipToGridItem?: GridItemProps;
    shippingAddressForm?: ShippingAddressFormStyles;
    addressInfoDisplay?: AddressInfoDisplayStyles;
    useShippingAddressGridItem?: GridItemProps;
    useShippingAddressCheckbox?: CheckboxPresentationProps;
    billToGridItem?: GridItemProps;
    billingAddressForm?: BillingAddressFormStyles;
    saveButtonGridItem?: GridItemProps;
    saveButton?: ButtonPresentationProps;
    findLocationModal?: FindLocationModalStyles;
}

export const onePageCheckoutShippingSectionStyles: OnePageCheckoutShippingSectionStyles = {
    loadingWrapper: {
        css: css`
            display: flex;
            height: 450px;
            justify-content: center;
            align-items: center;
        `,
    },
    container: { gap: 10 },
    titleGridItem: { width: 10 },
    titleText: {
        variant: "h5",
        css: css`
            margin: 0;
        `,
    },
    titleEditGridItem: {
        width: 2,
        css: css`
            justify-content: end;
            align-items: center;
        `,
    },
    pickUpGridItem: {
        width: 12,
        css: css`
            padding-bottom: 24px;
        `,
    },
    headingGridItem: {
        width: 12,
        css: css`
            justify-content: space-between;
            padding: 15px 5px 5px 5px;
        `,
    },
    heading: { weight: 600 },
    shipToGridItem: { width: 12 },
    useShippingAddressGridItem: {
        width: 12,
        css: css`
            padding: 20px 5px;
        `,
    },
    billToGridItem: { width: 12 },
    saveButtonGridItem: { width: 12 },
    saveButton: {
        css: css`
            margin-top: 10px;
        `,
    },
};

const styles = onePageCheckoutShippingSectionStyles;

const OnePageCheckoutShippingSection = ({
    isShippingAddressUpdateRequired,
    isBillingAddressUpdateRequired,
    isBillingAddressTheSameAsShipping,
    isSavingShipping,
    cart,
    shipToState,
    billToState,
    currentUserIsGuest,
    countries,
    shipTosDataView,
    shipToAddressFields,
    billToAddressFields,
    fulfillmentMethod,
    pickUpWarehouse,
    newAddress,
    theme,
    updateCartInsteadOfContext,
    loadShipTos,
    loadShipTo,
    loadBillTo,
    setIsBillingAddressTheSameAsShipping,
    setAddressBookOverlayIsOpen,
    setStorePickupOverlayIsOpen,
    saveShipping,
    setPaymentDetailsState,
    updatePickUpWarehouse,
    updatePickUpWarehouseForCart,
}: Props) => {
    const context = useContext(OnePageCheckoutFormContext);
    const toasterContext = useContext(ToasterContext);

    const [isEditing, setIsEditing] = useState(false);
    const [isShipToSelectorModalOpen, setIsShipToSelectorModalOpen] = useState(false);
    const [isBillToSelectorModalOpen, setIsBillToSelectorModalOpen] = useState(false);
    const [isFindLocationOpen, setIsFindLocationOpen] = useState(false);

    useEffect(() => {
        if ((isShippingAddressUpdateRequired || isBillingAddressUpdateRequired) && shipToState.value) {
            setIsEditing(true);
        }
    }, [isShippingAddressUpdateRequired, isBillingAddressUpdateRequired, shipToState.value]);

    const shipTo = shipToState.value;
    const billTo = billToState.value;

    useEffect(() => {
        if (cart && shipTosDataView && !shipTosDataView.value && !shipTosDataView.isLoading) {
            loadShipTos({ billToId: cart.billToId, expand: ["validation"], exclude: ["showAll"] });
        }
    });

    useEffect(() => {
        if (!shipToState.value && !shipToState.isLoading && cart && cart.billToId && cart.shipToId) {
            loadShipTo({ billToId: cart.billToId, shipToId: cart.shipToId });
        }
    }, [shipToState]);

    useEffect(() => {
        if (!billToState.value && !billToState.isLoading && cart && cart.billToId) {
            loadBillTo({ billToId: cart.billToId });
        }
    }, [billToState]);

    if (!cart) {
        return (
            <StyledWrapper {...styles.loadingWrapper}>
                <LoadingSpinner />
            </StyledWrapper>
        );
    }

    if (!countries || !billTo || (!shipTo && !newAddress)) {
        return null;
    }

    const useShippingAddressChangeHandler: CheckboxProps["onChange"] = (_, value) => {
        setIsBillingAddressTheSameAsShipping({ isBillingAddressTheSameAsShipping: value });
    };

    const editShippingAddressClickHandler = () => {
        theme.addressBook?.useOverlay
            ? setAddressBookOverlayIsOpen({ isOpen: true, isBillTo: false })
            : setIsShipToSelectorModalOpen(true);
    };

    const shipToSelectorModalCloseHandler = () => {
        setIsShipToSelectorModalOpen(false);
    };

    const editBillingAddressClickHandler = () => {
        theme.addressBook?.useOverlay
            ? setAddressBookOverlayIsOpen({ isOpen: true, isBillTo: true })
            : setIsBillToSelectorModalOpen(true);
    };

    const billToSelectorModalCloseHandler = () => {
        setIsBillToSelectorModalOpen(false);
    };

    const editPickUpWarehouseClickHandler = () => {
        if (theme.storePickup?.useOverlay) {
            setStorePickupOverlayIsOpen({ isOpen: true });
            return;
        }

        setIsFindLocationOpen(true);
    };

    const handleFindLocationModalClose = () => setIsFindLocationOpen(false);
    const handleWarehouseSelected = (warehouse: WarehouseModel) => {
        handlePickUpAddressChange(warehouse);
        setIsFindLocationOpen(false);
    };

    const handlePickUpAddressChange = async (pickUpWarehouse: WarehouseModel) => {
        if (updateCartInsteadOfContext && cart) {
            await updatePickUpWarehouseForCart({ cart, pickUpWarehouse });
        } else {
            await updatePickUpWarehouse({ pickUpWarehouse });
        }

        toasterContext.addToast({
            body: translate("Pick Up Address Updated"),
            messageType: "success",
        });
    };

    const titleEditClickHandler = () => {
        if (currentUserIsGuest) {
            setIsEditing(true);
        } else if (fulfillmentMethod === FulfillmentMethod.PickUp) {
            theme.addressBook?.useOverlay
                ? setAddressBookOverlayIsOpen({ isOpen: true, isBillTo: true })
                : setIsBillToSelectorModalOpen(true);
        }
    };

    const saveClickHandler = async () => {
        context.formSubmitAttempted = true;

        const validators: (() => Promise<boolean>)[] = [];
        Object.keys(context.validators).forEach(key => {
            const validator = context.validators[key];
            if (validator) {
                validators.push(validator);
            }
        });
        const results = await Promise.all(validators.map(v => v()));

        if (results.every(o => o)) {
            saveShipping({
                onSuccess: () => {
                    setIsEditing(false);
                    setPaymentDetailsState({ paymentDetailsState: "active" });
                },
            });
        } else {
            scrollToFirstFormError();
        }
    };

    return (
        <LoadingOverlay loading={isSavingShipping}>
            <GridContainer {...styles.container} data-test-selector="shippingAndBillingSection">
                {fulfillmentMethod === FulfillmentMethod.PickUp && pickUpWarehouse && (
                    <>
                        <GridItem {...styles.titleGridItem}>
                            <Typography as="h2" {...styles.titleText}>
                                {translate("Pick Up Address")}
                            </Typography>
                        </GridItem>
                        <GridItem {...styles.titleEditGridItem}>
                            <Link {...styles.titleEditLink} onClick={editPickUpWarehouseClickHandler}>
                                {translate("Edit")}
                            </Link>
                        </GridItem>
                        <GridItem {...styles.pickUpGridItem}>
                            <AddressInfoDisplay
                                {...pickUpWarehouse}
                                companyName={pickUpWarehouse.description || pickUpWarehouse.name}
                                extendedStyles={styles.addressInfoDisplay}
                            />
                        </GridItem>
                    </>
                )}
                <GridItem {...styles.titleGridItem}>
                    <Typography as="h2" {...styles.titleText}>
                        {fulfillmentMethod === FulfillmentMethod.Ship
                            ? translate("Shipping")
                            : translate("Billing Address")}
                    </Typography>
                </GridItem>
                {isEditing && (
                    <>
                        {fulfillmentMethod === FulfillmentMethod.Ship && shipToAddressFields && (
                            <GridItem {...styles.shipToGridItem}>
                                <ShippingAddressForm
                                    countries={countries}
                                    fieldDisplay={shipToAddressFields}
                                    extendedStyles={styles.shippingAddressForm}
                                />
                            </GridItem>
                        )}
                        {fulfillmentMethod === FulfillmentMethod.Ship && (
                            <GridItem {...styles.useShippingAddressGridItem}>
                                <Checkbox
                                    {...styles.useShippingAddressCheckbox}
                                    onChange={useShippingAddressChangeHandler}
                                    checked={isBillingAddressTheSameAsShipping}
                                    data-test-selector="useShippingAddressCheckbox"
                                >
                                    {translate("Billing address is the same as shipping")}
                                </Checkbox>
                            </GridItem>
                        )}
                        {billToAddressFields &&
                            (!isBillingAddressTheSameAsShipping || fulfillmentMethod === FulfillmentMethod.PickUp) && (
                                <GridItem {...styles.billToGridItem}>
                                    <BillingAddressForm
                                        countries={countries}
                                        fieldDisplay={billToAddressFields}
                                        extendedStyles={styles.billingAddressForm}
                                    />
                                </GridItem>
                            )}
                        <GridItem {...styles.saveButtonGridItem}>
                            <Button
                                {...styles.saveButton}
                                onClick={saveClickHandler}
                                data-test-selector="saveAndContinueButton"
                            >
                                {translate("Save & Continue")}
                            </Button>
                        </GridItem>
                    </>
                )}
                {!isEditing && (
                    <>
                        {(currentUserIsGuest || fulfillmentMethod === FulfillmentMethod.PickUp) && (
                            <GridItem {...styles.titleEditGridItem}>
                                <Link
                                    {...styles.titleEditLink}
                                    onClick={titleEditClickHandler}
                                    data-test-selector="editPickupShippingAddressButton"
                                >
                                    {translate("Edit")}
                                </Link>
                            </GridItem>
                        )}
                        {fulfillmentMethod === FulfillmentMethod.Ship && (shipTo || newAddress) && (
                            <>
                                <GridItem {...styles.headingGridItem}>
                                    <Typography {...styles.heading}>{translate("Shipping Address")}</Typography>
                                    {!currentUserIsGuest && (
                                        <>
                                            <Link
                                                {...styles.editLink}
                                                onClick={editShippingAddressClickHandler}
                                                data-test-selector="editShippingAddressButton"
                                            >
                                                {translate("Edit")}
                                                <VisuallyHidden>{translate("Shipping Address")}</VisuallyHidden>
                                            </Link>
                                            <ShipToSelectorModal
                                                isOpen={isShipToSelectorModalOpen}
                                                onModalClose={shipToSelectorModalCloseHandler}
                                                extendedStyles={styles.shipToSelectorModal}
                                            />
                                        </>
                                    )}
                                </GridItem>
                                <GridItem {...styles.shipToGridItem} data-test-selector="ShippingAddress">
                                    {shipTo && (
                                        <AddressInfoDisplay
                                            {...shipTo}
                                            state={shipTo.state ? shipTo.state.abbreviation : undefined}
                                            country={shipTo.country ? shipTo.country.abbreviation : undefined}
                                            extendedStyles={styles.addressInfoDisplay}
                                        />
                                    )}
                                </GridItem>
                            </>
                        )}
                        {billTo && (
                            <>
                                {fulfillmentMethod === FulfillmentMethod.Ship && (
                                    <GridItem {...styles.headingGridItem}>
                                        <Typography {...styles.heading}>{translate("Billing Address")}</Typography>
                                        {!currentUserIsGuest && (
                                            <>
                                                <Link
                                                    {...styles.editLink}
                                                    onClick={editBillingAddressClickHandler}
                                                    data-test-selector="editBillingAddressButton"
                                                >
                                                    {translate("Edit")}
                                                    <VisuallyHidden>{translate("Billing Address")}</VisuallyHidden>
                                                </Link>
                                            </>
                                        )}
                                    </GridItem>
                                )}
                                <GridItem {...styles.billToGridItem} data-test-selector="BillingAddress">
                                    <AddressInfoDisplay
                                        {...billTo}
                                        state={billTo.state ? billTo.state.abbreviation : undefined}
                                        country={billTo.country ? billTo.country.abbreviation : undefined}
                                        extendedStyles={styles.addressInfoDisplay}
                                    />
                                </GridItem>
                                <BillToSelectorModal
                                    isOpen={isBillToSelectorModalOpen}
                                    onModalClose={billToSelectorModalCloseHandler}
                                    extendedStyles={styles.billToSelectorModal}
                                />
                            </>
                        )}
                    </>
                )}
            </GridContainer>
            <FindLocationModal
                modalIsOpen={isFindLocationOpen}
                onWarehouseSelected={handleWarehouseSelected}
                onModalClose={handleFindLocationModalClose}
                forCart
                extendedStyles={styles.findLocationModal}
            />
        </LoadingOverlay>
    );
};

const widgetModule: WidgetModule = {
    component: connect(mapStateToProps, mapDispatchToProps)(withTheme(OnePageCheckoutShippingSection)),
    definition: {
        displayName: "Shipping Section",
        group: "One Page Checkout",
        allowedContexts: ["OnePageCheckoutPage"],
    },
};

export default widgetModule;
