import StyledWrapper from "@insite/client-framework/Common/StyledWrapper";
import { getIsWebCrawler } from "@insite/client-framework/Common/WebCrawler";
import Zone from "@insite/client-framework/Components/Zone";
import ApplicationState from "@insite/client-framework/Store/ApplicationState";
import { getSettingsCollection } from "@insite/client-framework/Store/Context/ContextSelectors";
import addProductFilters from "@insite/client-framework/Store/Pages/ProductList/Handlers/AddProductFilters";
import setTableColumns from "@insite/client-framework/Store/Pages/ProductList/Handlers/SetTableColumns";
import setView from "@insite/client-framework/Store/Pages/ProductList/Handlers/SetView";
import setVisibleColumnNames from "@insite/client-framework/Store/Pages/ProductList/Handlers/SetVisibleColumnNames";
import {
    getProductListDataView,
    isLoadMore,
} from "@insite/client-framework/Store/Pages/ProductList/ProductListSelectors";
import { getProductListDataViewProperty } from "@insite/client-framework/Store/Pages/ProductList/ProductListSelectors";
import { ProductListViewType } from "@insite/client-framework/Store/Pages/ProductList/ProductListState";
import translate from "@insite/client-framework/Translate";
import WidgetModule from "@insite/client-framework/Types/WidgetModule";
import WidgetProps from "@insite/client-framework/Types/WidgetProps";
import CardContainer, { CardContainerStyles } from "@insite/content-library/Components/CardContainer";
import CardContainerMultiColumn, {
    CardContainerMultiColumnStyles,
} from "@insite/content-library/Components/CardContainerMultiColumn";
import CardList, { CardListStyles } from "@insite/content-library/Components/CardList";
import { ProductListPageDataContext } from "@insite/content-library/Pages/ProductListPage";
import ProductListProductCard from "@insite/content-library/Widgets/ProductList/ProductListProductCard";
import ProductListProductContext from "@insite/content-library/Widgets/ProductList/ProductListProductContext";
import ProductListProductGridCard from "@insite/content-library/Widgets/ProductList/ProductListProductGridCard";
import ProductListTable from "@insite/content-library/Widgets/ProductList/ProductListTable";
import Button, { ButtonPresentationProps } from "@insite/mobius/Button";
import Hidden from "@insite/mobius/Hidden";
import LoadingOverlay from "@insite/mobius/LoadingOverlay";
import LoadingSpinner, { LoadingSpinnerProps } from "@insite/mobius/LoadingSpinner";
import { TypographyPresentationProps } from "@insite/mobius/Typography";
import { breakpointMediaQueries } from "@insite/mobius/utilities";
import getColor from "@insite/mobius/utilities/getColor";
import InjectableCss from "@insite/mobius/utilities/InjectableCss";
import sortBy from "lodash/sortBy";
import React, { ReactNode, useCallback, useEffect, useRef } from "react";
import { connect, ResolveThunks } from "react-redux";
import { css } from "styled-components";

const enum fields {
    showImage = "showImage",
    showCompare = "showCompare",
    showBrand = "showBrand",
    showTitle = "showTitle",
    showPartNumbers = "showPartNumbers",
    showAvailability = "showAvailability",
    showAttributes = "showAttributes",
    showPrice = "showPrice",
    showAddToList = "showAddToList",
    productListDefaultView = "productListDefaultView",
    showImageBadges = "showImageBadges",
    maximumNumberOfImageBadges = "maximumNumberOfImageBadges",
    showTextBadges = "showTextBadges",
    maximumNumberOfTextBadges = "maximumNumberOfTextBadges",
}

interface OwnProps extends WidgetProps {
    fields: {
        [fields.showImage]: boolean;
        [fields.showCompare]: boolean;
        [fields.showTitle]: boolean;
        [fields.showBrand]: boolean;
        [fields.showPartNumbers]: boolean;
        [fields.showAvailability]: boolean;
        [fields.showAttributes]: boolean;
        [fields.showPrice]: boolean;
        [fields.showAddToList]: boolean;
        [fields.productListDefaultView]: ProductListViewType;
        [fields.showImageBadges]: boolean;
        [fields.maximumNumberOfImageBadges]: number;
        [fields.showTextBadges]: boolean;
        [fields.maximumNumberOfTextBadges]: number;
    };
}

const mapStateToProps = (state: ApplicationState, ownProps: OwnProps) => {
    const {
        pages: {
            productList: {
                isLoading,
                isSearchPage,
                view,
                productFilters: { pageCategoryId },
                parameter,
            },
        },
    } = state;
    const {
        websiteSettings: { includeRealTimeInventoryAndPricingForSeo },
        productSettings: { productLoadStyle },
    } = getSettingsCollection(state);
    return {
        isLoading,
        productsDataView: getProductListDataView(state),
        view: view || ownProps.fields.productListDefaultView,
        isSearchPage,
        attributeTypeFacets: getProductListDataViewProperty(state, "attributeTypeFacets"),
        brandFacets: getProductListDataViewProperty(state, "brandFacets"),
        productLineFacets: getProductListDataViewProperty(state, "productLineFacets"),
        includeRealTimeInventoryAndPricingForSeo,
        pageCategoryId,
        productLoadStyle,
        page: parameter?.page ?? 1,
        pageSize: parameter?.pageSize ?? 0,
        totalItemCount: getProductListDataViewProperty(state, "pagination")?.totalItemCount ?? 0,
        isLoadMore: isLoadMore(state),
    };
};

const mapDispatchToProps = {
    setView,
    setTableColumns,
    setVisibleColumnNames,
    addProductFilters,
};

type Props = ReturnType<typeof mapStateToProps> & OwnProps & ResolveThunks<typeof mapDispatchToProps>;

export interface ProductListCardListStyles {
    wrapper?: InjectableCss;
    cardList?: CardListStyles;
    noProductsText?: TypographyPresentationProps;
    centeringWrapper?: InjectableCss;
    spinner?: LoadingSpinnerProps;
    cardContainerStyles?: CardContainerStyles;
    cardContainerMultiColumnStyles?: CardContainerMultiColumnStyles;
    loadMoreWrapper?: InjectableCss;
    loadMoreButton?: ButtonPresentationProps;
}

export const listStyles: ProductListCardListStyles = {
    wrapper: {
        css: css`
            margin-top: 20px;
            border-top: 1px solid ${getColor("common.border")};
        `,
    },
    cardList: {
        gridContainer: {
            gap: 0,
        },
    },
    centeringWrapper: {
        css: css`
            width: 100%;
            height: 300px;
            display: flex;
            align-items: center;
        `,
    },
    spinner: {
        css: css`
            margin: auto;
        `,
    },
    noProductsText: {
        variant: "h4",
        css: css`
            display: block;
            margin: auto;
        `,
    },
    cardContainerStyles: {
        cardDivider: {
            css: css`
                width: 100%;
            `,
        },
    },
    loadMoreWrapper: {
        css: css`
            width: 100%;
            height: 100px;
            display: flex;
            align-items: center;
            justify-content: center;
        `,
    },
    loadMoreButton: {
        variant: "secondary",
        css: css`
            margin: 0 10px;
            ${({ theme }) =>
                breakpointMediaQueries(theme, [
                    css`
                        width: 100%;
                    `,
                    css`
                        width: 100%;
                    `,
                    null,
                    null,
                    null,
                ])}
        `,
    },
};

const styles = listStyles;

const ProductListCardList = ({
    id,
    isLoading,
    isSearchPage,
    productsDataView,
    view,
    fields,
    attributeTypeFacets,
    brandFacets,
    productLineFacets,
    includeRealTimeInventoryAndPricingForSeo,
    pageCategoryId,
    productLoadStyle,
    page,
    pageSize,
    totalItemCount,
    isLoadMore,
    setView,
    setTableColumns,
    setVisibleColumnNames,
    addProductFilters,
}: Props) => {
    useEffect(() => {
        setView({ view });
    }, []);

    useEffect(() => {
        if (!attributeTypeFacets?.length && !brandFacets?.length && !productLineFacets?.length) {
            setTableColumns({ tableColumns: [] });
            setVisibleColumnNames({ visibleColumnNames: [] });
            return;
        }

        const columns = attributeTypeFacets
            ? sortBy(
                  attributeTypeFacets,
                  o => o.sortOrder,
                  o => o.name,
              )
            : [];
        if (brandFacets?.length) {
            columns.push({
                attributeTypeId: "",
                name: "Brand",
                nameDisplay: "",
                sortOrder: 0,
                attributeValueFacets: [],
            });
        }

        if (productLineFacets?.length) {
            columns.push({
                attributeTypeId: "",
                name: "Product Line",
                nameDisplay: "",
                sortOrder: 0,
                attributeValueFacets: [],
            });
        }

        setTableColumns({ tableColumns: columns });
        setVisibleColumnNames({ visibleColumnNames: columns?.slice(0, 3).map(o => o.name) || [] });
    }, [pageCategoryId, attributeTypeFacets?.length, brandFacets?.length, productLineFacets?.length]);

    const observer: React.RefObject<any> = useRef(null);
    const itemsLeft = totalItemCount - page * pageSize;

    const infiniteScrollTriggerRef = useCallback(
        (node: any) => {
            if (productLoadStyle !== "InfiniteScroll" || getIsWebCrawler() || isLoading) {
                return;
            }

            if (observer.current) {
                observer.current.disconnect();
            }

            observer.current = new IntersectionObserver(entries => {
                if (entries[0].isIntersecting && itemsLeft > 0) {
                    addProductFilters({ page: page + 1 });
                }
            });

            if (node) {
                observer.current?.observe(node);
            }
        },
        [productLoadStyle, isLoading, itemsLeft],
    );

    const loadMoreClickHandler = () => {
        addProductFilters({ page: page + 1 });
    };

    if (isLoading && !productsDataView.value && !isLoadMore) {
        return (
            <StyledWrapper {...styles.centeringWrapper}>
                <LoadingSpinner {...styles.spinner} data-test-selector="productListCardListSpinner" />
            </StyledWrapper>
        );
    }

    if (!productsDataView.value) {
        return null;
    }

    const products = productsDataView.value;
    if (products.length === 0) {
        const styledWrapper =
            totalItemCount === 0 && !productsDataView.searchTermRedirectUrl ? (
                <StyledWrapper {...styles.wrapper}>
                    <StyledWrapper {...styles.centeringWrapper} data-test-selector="productListNoneFound">
                        {isSearchPage && <Zone contentId={id} zoneName="Content00" />}
                        {!isSearchPage && <Zone contentId={id} zoneName="Content01" />}
                    </StyledWrapper>
                </StyledWrapper>
            ) : (
                <StyledWrapper {...styles.centeringWrapper}>
                    <LoadingSpinner {...styles.spinner} data-test-selector="productListCardListSpinner" />
                </StyledWrapper>
            );

        return styledWrapper;
    }

    const renderProducts = (children: ReactNode) => {
        return products.map((product, idx) => (
            <ProductListProductContext product={product} idx={idx} key={product.id}>
                {children}
            </ProductListProductContext>
        ));
    };

    const overriddenProps = { ...fields };
    if (getIsWebCrawler() && !includeRealTimeInventoryAndPricingForSeo) {
        overriddenProps.showAvailability = false;
    }

    return (
        <StyledWrapper {...styles.wrapper}>
            <ProductListPageDataContext.Consumer>
                {({ ref }) => {
                    return ref ? <span ref={ref} tabIndex={-1} /> : undefined;
                }}
            </ProductListPageDataContext.Consumer>
            <LoadingOverlay loading={isLoading && !isLoadMore}>
                <Hidden below="md">
                    <CardList extendedStyles={styles.cardList} data-test-selector={`productListCards_${view}`}>
                        {view === "Table" ? (
                            <ProductListTable products={products} {...overriddenProps} />
                        ) : (
                            renderProducts(
                                view === "List" ? (
                                    <CardContainer extendedStyles={styles.cardContainerStyles}>
                                        <ProductListProductCard {...overriddenProps} />
                                    </CardContainer>
                                ) : (
                                    <CardContainerMultiColumn extendedStyles={styles.cardContainerMultiColumnStyles}>
                                        <ProductListProductGridCard {...overriddenProps} />
                                    </CardContainerMultiColumn>
                                ),
                            )
                        )}
                    </CardList>
                </Hidden>
                <Hidden above="sm">
                    <CardList extendedStyles={styles.cardList} data-test-selector="cardListProductsNarrow">
                        {renderProducts(
                            <CardContainer extendedStyles={styles.cardContainerStyles}>
                                <ProductListProductCard {...overriddenProps} />
                            </CardContainer>,
                        )}
                    </CardList>
                </Hidden>
                {!isLoading && isLoadMore && productLoadStyle === "InfiniteScroll" && itemsLeft > 0 && (
                    <div ref={infiniteScrollTriggerRef} data-test-selector="infiniteScrollTrigger"></div>
                )}
                {!isLoading && isLoadMore && productLoadStyle === "LoadMore" && itemsLeft > 0 && (
                    <StyledWrapper {...styles.loadMoreWrapper}>
                        <Button
                            {...styles.loadMoreButton}
                            onClick={loadMoreClickHandler}
                            data-test-selector="loadMoreButton"
                        >
                            {translate("Load More")}
                        </Button>
                    </StyledWrapper>
                )}
                {isLoading && isLoadMore && (
                    <StyledWrapper {...styles.centeringWrapper}>
                        <LoadingSpinner {...styles.spinner} />
                    </StyledWrapper>
                )}
            </LoadingOverlay>
        </StyledWrapper>
    );
};

const widgetModule: WidgetModule = {
    component: connect(mapStateToProps, mapDispatchToProps)(ProductListCardList),
    definition: {
        group: "Product List",
        displayName: "Card List",
        allowedContexts: ["ProductListPage"],
        fieldDefinitions: [
            {
                name: fields.showImage,
                displayName: "Show Image",
                editorTemplate: "CheckboxField",
                defaultValue: true,
                fieldType: "General",
                sortOrder: 1,
            },
            {
                name: fields.showCompare,
                displayName: "Show Compare",
                editorTemplate: "CheckboxField",
                defaultValue: true,
                fieldType: "General",
                sortOrder: 2,
            },
            {
                name: fields.showBrand,
                displayName: "Show Brand",
                editorTemplate: "CheckboxField",
                defaultValue: true,
                fieldType: "General",
                sortOrder: 3,
            },
            {
                name: fields.showTitle,
                displayName: "Show Title",
                editorTemplate: "CheckboxField",
                defaultValue: true,
                fieldType: "General",
                sortOrder: 4,
            },
            {
                name: fields.showPartNumbers,
                displayName: "Show Part Numbers",
                editorTemplate: "CheckboxField",
                defaultValue: true,
                fieldType: "General",
                sortOrder: 5,
            },
            {
                name: fields.showAvailability,
                displayName: "Show Availability",
                editorTemplate: "CheckboxField",
                defaultValue: true,
                fieldType: "General",
                sortOrder: 6,
            },
            {
                name: fields.showAttributes,
                displayName: "Show Attributes",
                editorTemplate: "CheckboxField",
                defaultValue: true,
                fieldType: "General",
                sortOrder: 7,
            },
            {
                name: fields.showPrice,
                displayName: "Show Price",
                editorTemplate: "CheckboxField",
                defaultValue: true,
                fieldType: "General",
                sortOrder: 8,
            },
            {
                name: fields.showAddToList,
                displayName: "Show Add To List",
                editorTemplate: "CheckboxField",
                defaultValue: true,
                fieldType: "General",
                sortOrder: 9,
            },
            {
                name: fields.productListDefaultView,
                editorTemplate: "RadioButtonsField",
                defaultValue: "List",
                fieldType: "General",
                sortOrder: 10,
                options: [
                    {
                        displayName: "List",
                        value: "List",
                    },
                    {
                        displayName: "Grid",
                        value: "Grid",
                    },
                    {
                        displayName: "Table",
                        value: "Table",
                    },
                ],
            },
            {
                name: fields.showImageBadges,
                displayName: "Show Image Badges",
                editorTemplate: "CheckboxField",
                defaultValue: false,
                fieldType: "General",
                sortOrder: 11,
            },
            {
                name: fields.maximumNumberOfImageBadges,
                editorTemplate: "DropDownField",
                isVisible: widget => widget.fields.showImageBadges,
                defaultValue: "4",
                options: [
                    { displayName: "1", value: "1" },
                    { displayName: "2", value: "2" },
                    { displayName: "3", value: "3" },
                    { displayName: "4", value: "4" },
                    { displayName: "5", value: "5" },
                    { displayName: "6", value: "6" },
                    { displayName: "7", value: "7" },
                    { displayName: "8", value: "8" },
                ],
                fieldType: "General",
                sortOrder: 12,
            },
            {
                name: fields.showTextBadges,
                displayName: "Show Text Badges",
                editorTemplate: "CheckboxField",
                defaultValue: false,
                fieldType: "General",
                sortOrder: 13,
            },
            {
                name: fields.maximumNumberOfTextBadges,
                editorTemplate: "DropDownField",
                isVisible: widget => widget.fields.showTextBadges,
                defaultValue: "4",
                options: [
                    { displayName: "1", value: "1" },
                    { displayName: "2", value: "2" },
                    { displayName: "3", value: "3" },
                    { displayName: "4", value: "4" },
                    { displayName: "5", value: "5" },
                    { displayName: "6", value: "6" },
                    { displayName: "7", value: "7" },
                    { displayName: "8", value: "8" },
                ],
                fieldType: "General",
                sortOrder: 14,
            },
        ],
    },
};

export default widgetModule;
