import mergeToNew from "@insite/client-framework/Common/mergeToNew";
import StyledWrapper from "@insite/client-framework/Common/StyledWrapper";
import { HasProductContext, withProductContext } from "@insite/client-framework/Components/ProductContext";
import ApplicationState from "@insite/client-framework/Store/ApplicationState";
import { getSettingsCollection } from "@insite/client-framework/Store/Context/ContextSelectors";
import translate from "@insite/client-framework/Translate";
import { ImageModel } from "@insite/client-framework/Types/ApiModels";
import Button, { ButtonIcon, ButtonProps } from "@insite/mobius/Button";
import Clickable from "@insite/mobius/Clickable";
import GridContainer, { GridContainerProps } from "@insite/mobius/GridContainer";
import GridItem, { GridItemProps } from "@insite/mobius/GridItem";
import ChevronDown from "@insite/mobius/Icons/ChevronDown";
import ChevronLeft from "@insite/mobius/Icons/ChevronLeft";
import ChevronRight from "@insite/mobius/Icons/ChevronRight";
import ChevronUp from "@insite/mobius/Icons/ChevronUp";
import LazyImage, { LazyImageProps } from "@insite/mobius/LazyImage";
import { injectCss } from "@insite/mobius/utilities";
import getColor from "@insite/mobius/utilities/getColor";
import InjectableCss from "@insite/mobius/utilities/InjectableCss";
import useEmblaCarousel from "embla-carousel-react";
import React, { useEffect, useState } from "react";
import { connect } from "react-redux";
import styled, { css } from "styled-components";

interface OwnProps {
    scrollingBehavior?: string;
    numberOfVisibleThumbnails?: number;
    selectedIndex?: number;
    onSelectImage?: (index: number) => void;
    extendedStyles?: ProductImageCarouselStyles;
}

const mapStateToProps = (state: ApplicationState) => ({
    productSettings: getSettingsCollection(state).productSettings,
});

type Props = OwnProps & HasProductContext & ReturnType<typeof mapStateToProps>;

export interface ProductImageCarouselStyles {
    mainContainer?: GridContainerProps;
    prevArrowGridItem?: GridItemProps;
    prevArrowButton?: ButtonProps;
    carouselGridItem?: GridItemProps;
    carouselWrapper?: InjectableCss;
    carouselContainer?: InjectableCss;
    carouselSlidesContainer?: InjectableCss;
    carouselSlide?: InjectableCss;
    carouselSlideInner?: InjectableCss;
    image?: LazyImageProps;
    selectedImage?: LazyImageProps;
    nextArrowGridItem?: GridItemProps;
    nextArrowButton?: ButtonProps;
    verticalPrevArrowGridItem?: GridItemProps;
    verticalCarouselGridItem?: GridItemProps;
    verticalNextArrowGridItem?: GridItemProps;
}

const getImageCss = (borderColor: string) => css`
    width: 100%;
    height: 100%;
    max-width: 64px;
    max-height: 64px;
    border: 1px solid ${getColor(borderColor)};
    cursor: pointer;
    justify-content: flex-start;
    img {
        max-width: 64px;
        max-height: 64px;
        padding: 3px;
    }
`;

export const productImageCarouselStyles: ProductImageCarouselStyles = {
    mainContainer: { gap: 0 },
    prevArrowGridItem: {
        width: 1,
        align: "middle",
        css: css`
            justify-content: flex-end;
        `,
    },
    verticalPrevArrowGridItem: {
        width: 12,
        align: "middle",
        css: css`
            justify-content: center;
        `,
    },
    prevArrowButton: {
        variant: "secondary",
        color: "text.main",
        css: css`
            border: 0;
            padding: 0;
        `,
    },
    carouselGridItem: { width: 10 },
    verticalCarouselGridItem: { width: 12 },
    carouselWrapper: {
        css: css`
            width: 100%;
        `,
    },
    carouselContainer: {
        css: css`
            overflow: hidden;
        `,
    },
    carouselSlideInner: {
        css: css`
            width: 100%;
            text-align: center;
            position: relative;
            padding: 10px 3px 0;
            display: flex;
            justify-content: center;
        `,
    },
    image: {
        css: getImageCss("common.border"),
    },
    selectedImage: {
        css: getImageCss("text.main"),
    },
    nextArrowGridItem: {
        width: 1,
        align: "middle",
        css: css`
            justify-content: flex-start;
        `,
    },
    verticalNextArrowGridItem: {
        width: 12,
        align: "middle",
        css: css`
            justify-content: center;
        `,
    },
    nextArrowButton: {
        variant: "secondary",
        color: "text.main",
        css: css`
            border: 0;
            padding: 0;
        `,
    },
};

export const CarouselSlidesContainer = styled.div<
    InjectableCss & { isVertical: boolean; numberOfVisibleThumbnails: number }
>`
    display: flex;
    flex-direction: ${props => (props.isVertical ? "column" : "row")};
    align-items: ${props => (props.isVertical ? "center" : "initial")};
    ${props => (props.isVertical ? `height: ${props.numberOfVisibleThumbnails * 74}px;` : "")}
    ${injectCss};
`;

export const CarouselSlide = styled.div<InjectableCss & { numberOfVisibleThumbnails: number }>`
    flex: 0 0 ${props => 100 / props.numberOfVisibleThumbnails}%;
    padding: 0;
    position: relative;
    ${injectCss};
`;

const ProductImageCarousel = ({
    productSettings,
    productContext,
    scrollingBehavior,
    numberOfVisibleThumbnails = 6,
    selectedIndex,
    onSelectImage,
    extendedStyles,
}: Props) => {
    const [styles] = useState(() => mergeToNew(productImageCarouselStyles, extendedStyles));
    const { product } = productContext;

    const [selectedImageIndex, setSelectedImageIndex] = useState(selectedIndex);
    useEffect(() => {
        setSelectedImageIndex(selectedIndex);
    }, [selectedIndex]);

    const imageClickHandler = (index: number) => {
        setSelectedImageIndex(index);
        onSelectImage?.(index);
    };

    const isVertical = scrollingBehavior === "vertical";
    const [emblaRef, embla] = useEmblaCarousel({
        axis: isVertical ? "y" : "x",
    });
    const [canScrollPrev, setCanScrollPrev] = useState(false);
    const [canScrollNext, setCanScrollNext] = useState(false);

    const setCanScroll = () => {
        setCanScrollPrev(!!embla && embla.canScrollPrev());
        setCanScrollNext(!!embla && embla.canScrollNext());
    };

    const updateVisibility = () => {
        if (!embla || !product.images) {
            return;
        }

        const canScroll = embla.canScrollPrev() || embla.canScrollNext();
        if (!canScroll) {
            embla.slideNodes().forEach(slide => {
                slide.setAttribute("data-is-visible", "true");
            });
            return;
        }

        const viewport = embla.rootNode().getBoundingClientRect();
        const buffer = 5;

        embla.slideNodes().forEach((slide, index) => {
            const slideRect = slide.getBoundingClientRect();
            const isFullyVisible =
                slideRect.top >= viewport.top - buffer &&
                slideRect.bottom <= viewport.bottom + buffer &&
                slideRect.left >= viewport.left - buffer &&
                slideRect.right <= viewport.right + buffer;

            slide.setAttribute("data-is-visible", isFullyVisible ? "true" : "false");
        });
    };

    useEffect(() => {
        if (!embla) {
            return;
        }

        const handleUpdate = () => {
            setCanScroll();
            updateVisibility();
        };

        embla.on("init", handleUpdate);
        embla.on("select", handleUpdate);
        embla.on("reInit", handleUpdate);
        embla.on("settle", handleUpdate);

        handleUpdate();
    }, [embla]);

    useEffect(() => {
        if (!embla || !product.images) {
            return;
        }

        embla.reInit({
            align: product.images.length > numberOfVisibleThumbnails || isVertical ? "start" : "center",
            slidesToScroll: numberOfVisibleThumbnails,
            axis: isVertical ? "y" : "x",
        });

        updateVisibility();
    }, [embla, product, scrollingBehavior, numberOfVisibleThumbnails]);

    const getProductImageThumbPath = (productImage: ImageModel) => {
        if (productImage.imageType === "Static") {
            return productImage.smallImagePath || productImage.mediumImagePath;
        }
        if (productImage.imageType === "360" && productSettings.imageProvider === "SIRV") {
            return productImage.mediumImagePath + (productImage.mediumImagePath.includes("?") ? "&thumb" : "?thumb");
        }
        return "";
    };

    if (!product || !product.images || product.images.length === 0) {
        return null;
    }

    return (
        <GridContainer {...styles.mainContainer} key={product.id}>
            <GridItem {...(isVertical ? styles.verticalPrevArrowGridItem : styles.prevArrowGridItem)}>
                {product.images.length > numberOfVisibleThumbnails && (
                    <Button
                        {...styles.prevArrowButton}
                        onClick={() => embla && embla.scrollPrev()}
                        disabled={!canScrollPrev}
                        aria-label={translate("Previous")}
                        data-test-selector="prevBtn"
                    >
                        <ButtonIcon src={isVertical ? ChevronUp : ChevronLeft} />
                    </Button>
                )}
            </GridItem>
            <GridItem {...(isVertical ? styles.verticalCarouselGridItem : styles.carouselGridItem)}>
                <StyledWrapper {...styles.carouselWrapper}>
                    <StyledWrapper {...styles.carouselContainer} ref={emblaRef}>
                        <CarouselSlidesContainer
                            isVertical={isVertical}
                            numberOfVisibleThumbnails={numberOfVisibleThumbnails}
                            data-test-selector="slides"
                            data-test-selector-isVertical={isVertical}
                        >
                            {product.images.map((productImage, index) => (
                                <CarouselSlide
                                    key={productImage.id}
                                    numberOfVisibleThumbnails={numberOfVisibleThumbnails}
                                    data-is-selected={index === selectedImageIndex}
                                    data-is-visible="false"
                                >
                                    <StyledWrapper {...styles.carouselSlideInner} data-test-selector="carousel_slide">
                                        <Clickable onClick={() => imageClickHandler(index)}>
                                            <LazyImage
                                                {...(index === selectedImageIndex
                                                    ? styles.selectedImage
                                                    : styles.image)}
                                                src={getProductImageThumbPath(productImage)}
                                                altText={productImage.imageAltText}
                                            />
                                        </Clickable>
                                    </StyledWrapper>
                                </CarouselSlide>
                            ))}
                        </CarouselSlidesContainer>
                    </StyledWrapper>
                </StyledWrapper>
            </GridItem>
            <GridItem {...(isVertical ? styles.verticalNextArrowGridItem : styles.nextArrowGridItem)}>
                {product.images.length > numberOfVisibleThumbnails && (
                    <Button
                        {...styles.nextArrowButton}
                        onClick={() => embla && embla.scrollNext()}
                        disabled={!canScrollNext}
                        aria-label={translate("Next")}
                        data-test-selector="nextBtn"
                    >
                        <ButtonIcon src={isVertical ? ChevronDown : ChevronRight} />
                    </Button>
                )}
            </GridItem>
        </GridContainer>
    );
};

export default connect(mapStateToProps)(withProductContext(ProductImageCarousel));
