/**
 * External dependencies
 */
import clsx from "clsx";
import isDeepEqual from "fast-deep-equal/es6";
import { nanoid } from "nanoid";

/**
 * WordPress dependencies
 */
import { __, sprintf } from "@wordpress/i18n";
import { addFilter, applyFilters } from "@wordpress/hooks";
import { createHigherOrderComponent } from "@wordpress/compose";
import {
  InspectorControls,
  BlockControls,
  store as blockEditorStore,
} from "@wordpress/block-editor";
import { hasBlockSupport } from "@wordpress/blocks";
import { useSelect } from "@wordpress/data";
import { useEffect } from "@wordpress/element";
import {
  ToggleControl,
  SelectControl,
  TextControl,
  __experimentalUnitControl as UnitControl,
  __experimentalVStack as VStack,
  __experimentalToolsPanel as ToolsPanel,
  __experimentalToolsPanelItem as ToolsPanelItem,
} from "@wordpress/components";

/**
 * Internal dependencies
 */
import { ColorGradientDropdown } from "sdk/components";
import {
  getBreakpointType,
  getAllBreakpoints,
  handleChangeSettingField,
  handleChangeResponsiveSettingGroupField,
  getResponsiveSettingGroupFieldValue,
  handleChangeSettingGroupField,
  getSettingGroupFieldValue,
  getColorObject,
  useMultipleOriginColors,
  addAttributes,
  addEditProps,
  usePropsStyle,
} from "sdk/utils";
import {
  hasSupportLayout,
  getSelectOptions,
  isPremium,
  getPreviewMode,
  useSetPreviewMode,
  toInt,
} from "../../utils";
import { getProLabel } from "../../utils/labels";
import { toType } from "../../utils/dom";
import {
  effectOptions,
  isSingleSlideEffect,
  paginationDefault,
  navigationDefault,
  carouselSettingsDefault,
  scrollbarDefault,
  buildCarouselSettings,
  buildCarouselEditStyle,
  buildEditLayoutStyle,
  minimumSlidesNeeded,
} from "./utils";
import PreviewMode from "../../components/preview-mode";
import InputWithAutoControl from "../../components/input-auto-control";
import "./carousel-editor";
import { GroupSettingWrapper } from "./styles";

// Shared label
const spacingLabel = __("Spacing from edge", "content-blocks-builder");
const buttonSizeLabel = __("Button size", "content-blocks-builder");
const equalHeightLabel = __("Equal height", "content-blocks-builder");
const slideDirectionLabel = __("Slide direction", "content-blocks-builder");

/**
 * Define feature name
 */
export const layoutType = "layoutType";
export const featureName = "carousel";

/**
 * Add custom attributes.
 *
 * @param {Object} settings Settings for the block.
 * @return {Object} settings Modified settings.
 */
// pretter-ignore
addFilter(
  "blocks.registerBlockType",
  `boldblocks/${featureName}/addAttributes`,
  (settings) => {
    let defaultSettings = { carousel: carouselSettingsDefault };
    // Don't add attributes to the post template bilock
    if (settings?.name === "core/post-template") {
      defaultSettings = {};
    }

    return addAttributes(
      defaultSettings,
      (settings, { featureName, layoutType }) =>
        hasSupportLayout(settings, featureName, layoutType),
      { featureName, layoutType },
    )(settings);
  },
);

/**
 * Override the default edit UI to include new inspector controls for custom settings.
 *
 * @param {Function} BlockEdit Block edit component.
 * @return {Function} BlockEdit Modified block edit component.
 */
const withInspectorControls = createHigherOrderComponent((BlockEdit) => {
  return (props) => {
    if (!hasSupportLayout(props.name, featureName, layoutType)) {
      return <BlockEdit {...props} />;
    }

    // Allow the ability to cancel the support.
    if (
      !applyFilters("boldblocks.hasSupportCarousel", true, {
        props,
        context: "BlockEdit",
      })
    ) {
      return <BlockEdit {...props} />;
    }

    const setPreviewMode = useSetPreviewMode();

    const { attributes, setAttributes, clientId, name, isSelected } = props;
    const { boldblocks: { carousel = {} } = {} } = attributes;

    const onChangeCarousel = (values) =>
      handleChangeSettingField({
        attributes,
        setAttributes,
        fieldName: "carousel",
      })({
        ...carousel,
        ...values,
      });

    // Get all breakpoints
    const allBreakpoints = getAllBreakpoints();

    // Get current breakpoint
    const breakpoint = getBreakpointType();

    const { allColors } = useMultipleOriginColors();

    // Get group field value
    const getCarouselGroupFieldValue = ({ fieldName, defaultValue }) =>
      getSettingGroupFieldValue({
        fieldName,
        defaultValue,
        attributes,
        groupName: "carousel",
      });

    // Handle group field change
    const handleCarouselGroupFieldChange =
      (fieldName, fc = "") =>
      (value) =>
        handleChangeSettingGroupField({
          fieldName,
          groupName: "carousel",
          setAttributes,
          attributes,
        })(fc ? fc(value) : value);

    // Autoplay
    const autoplay = getCarouselGroupFieldValue({
      fieldName: "autoplay",
      defaultValue: { enable: true, delay: 3000 },
    });

    // Effect
    let effect = getCarouselGroupFieldValue({
      fieldName: "effect",
      defaultValue: "slide",
    });

    if (toType(effect) === "object") {
      effect = effect?.name ?? "slide";
    }

    const isSingleEffect = isSingleSlideEffect(effect);

    const effectSettings = getCarouselGroupFieldValue({
      fieldName: "effectSettings",
      defaultValue: {},
    });

    // slidesPerView
    const slidesPerViewRaw = getCarouselGroupFieldValue({
      fieldName: "slidesPerView",
      defaultValue: 1,
    });

    const slidesPerView =
      toType(slidesPerViewRaw) === "object"
        ? slidesPerViewRaw?.auto
          ? "auto"
          : slidesPerViewRaw?.value
        : slidesPerViewRaw;

    const slidesPerGroup = getCarouselGroupFieldValue({
      fieldName: "slidesPerGroup",
      defaultValue: 1,
    });

    // Breakpoints
    const enableResponsive = getCarouselGroupFieldValue({
      fieldName: "enableResponsive",
      defaultValue: false,
    });

    const breakpoints = getResponsiveSettingGroupFieldValue({
      fieldName: "breakpoints",
      groupName: "carousel",
      attributes,
      breakpoint,
      defaultValue: {},
    });

    const onBreakpointFieldChange =
      (fieldName, fc = "") =>
      (value) =>
        handleChangeResponsiveSettingGroupField({
          fieldName: "breakpoints",
          groupName: "carousel",
          setAttributes,
          attributes,
          breakpoint,
          allBreakpoints,
        })({ ...breakpoints, [fieldName]: fc ? fc(value) : value });

    const onBreakpointGroupChange = (groupValue) =>
      handleChangeResponsiveSettingGroupField({
        fieldName: "breakpoints",
        groupName: "carousel",
        setAttributes,
        attributes,
        breakpoint,
        allBreakpoints,
      })({ ...breakpoints, ...groupValue });

    const centeredSlides = getCarouselGroupFieldValue({
      fieldName: "centeredSlides",
      defaultValue: false,
    });

    const centeredSlidesSettings = getCarouselGroupFieldValue({
      fieldName: "centeredSlidesSettings",
      defaultValue: {},
    });

    // Loop type
    const loopType = getCarouselGroupFieldValue({
      fieldName: "loopType",
      defaultValue: "none",
    });

    let loopTypeHelp = "";
    if (loopType === "none") {
      loopTypeHelp = __(
        "If the loop type is set to None, the carousel will stop at the last slide during autoplay.",
        "content-blocks-builder",
      );
    } else if (loopType === "infinite") {
      const slidesNeeded = minimumSlidesNeeded(
        slidesPerView,
        slidesPerGroup,
        centeredSlides,
        enableResponsive
          ? getResponsiveSettingGroupFieldValue({
              fieldName: "breakpoints",
              groupName: "carousel",
              attributes,
              breakpoint: "lg",
              defaultValue: {},
            })
          : false,
      );
      if (slidesNeeded > 1) {
        loopTypeHelp = sprintf(
          __(
            "For infinite loop mode to work, you need at least %d slides.",
            "content-blocks-builder",
          ),
          slidesNeeded,
        );
      }
    }

    // Pagination
    const pagination = getCarouselGroupFieldValue({
      fieldName: "pagination",
      defaultValue: paginationDefault,
    });

    // Navigation
    const navigation = getCarouselGroupFieldValue({
      fieldName: "navigation",
      defaultValue: navigationDefault,
    });

    // Play/pause
    const playPause = getCarouselGroupFieldValue({
      fieldName: "playPause",
      defaultValue: {},
    });

    // Scrollbar
    const scrollbar = getCarouselGroupFieldValue({
      fieldName: "scrollbar",
      defaultValue: scrollbarDefault,
    });

    // Thumbs
    const thumbs = getCarouselGroupFieldValue({
      fieldName: "thumbs",
      defaultValue: { enable: false },
    });

    // Direction
    const direction = getCarouselGroupFieldValue({
      fieldName: "direction",
      defaultValue: "horizontal",
    });

    // Is Query Loop
    const isQueryLoop = name === "core/query" || name === "core/post-template";

    // Preview mode
    let previewMode;
    let handlePreviewModeChange;
    if (isQueryLoop) {
      previewMode = getPreviewMode(clientId);
      handlePreviewModeChange = (previewMode) =>
        setPreviewMode({ clientId, previewMode });
    } else {
      previewMode = getCarouselGroupFieldValue({
        fieldName: "previewMode",
        defaultValue: "EDIT",
      });
      handlePreviewModeChange = handleCarouselGroupFieldChange("previewMode");
    }

    // Switch to Edit mode when switching to small devices
    useEffect(() => {
      if (breakpoint !== "lg" && previewMode !== "EDIT" && isQueryLoop) {
        handlePreviewModeChange("EDIT");
      }
    }, [breakpoint, previewMode, isQueryLoop]);

    const itemsPerViewLabel = __("Slides per view", "content-blocks-builder");
    const itemsPerGroupLabel = __("Slides per group", "content-blocks-builder");
    const spaceBetweenItemsLabel = __(
      "Space between slides",
      "content-blocks-builder",
    );

    const onGroupPropertyChange =
      (groupName, groupValue) => (attr) => (value) => {
        handleCarouselGroupFieldChange(groupName)({
          ...groupValue,
          [attr]: value,
        });
      };

    const onGroupColorPropertyChange =
      (groupName, groupValue) => (attr) => (value) => {
        handleCarouselGroupFieldChange(groupName)({
          ...groupValue,
          [attr]: getColorObject(value, allColors),
        });
      };

    const onAutoplayChange = onGroupPropertyChange("autoplay", autoplay);
    const onNavigationChange = onGroupPropertyChange("navigation", navigation);
    const onPaginationChange = onGroupPropertyChange("pagination", pagination);
    const onScrollbarChange = onGroupPropertyChange("scrollbar", scrollbar);
    const onThumbsChange = onGroupPropertyChange("thumbs", thumbs);
    const onPlayPauseChange = onGroupPropertyChange("playPause", playPause);

    const canGroupPagination = pagination?.enable && direction === "horizontal";

    return (
      <>
        <BlockEdit {...props} />
        {isSelected && (
          <>
            <BlockControls>
              {breakpoint === "lg" && (
                <PreviewMode
                  previewMode={previewMode}
                  setPreviewMode={handlePreviewModeChange}
                />
              )}
            </BlockControls>
            <InspectorControls>
              <ToolsPanel
                label={__("Carousel settings", "content-blocks-builder")}
                resetAll={() => onChangeCarousel(carouselSettingsDefault)}
                className="cbb-settings is-carousel-settings"
              >
                <ToolsPanelItem
                  label={__("Effect & transition", "content-blocks-builder")}
                  isShownByDefault
                  hasValue={() =>
                    effect !== carouselSettingsDefault?.effect ||
                    carousel?.speed !== carouselSettingsDefault?.speed
                  }
                  onDeselect={() =>
                    onChangeCarousel({
                      effect: carouselSettingsDefault?.effect,
                      speed: carouselSettingsDefault?.speed,
                    })
                  }
                >
                  <VStack spacing={3}>
                    <SelectControl
                      label={__("Effect", "content-blocks-builder")}
                      value={effect}
                      options={applyFilters(
                        "cbb.carousel.effectOptions",
                        effectOptions,
                      )}
                      onChange={handleCarouselGroupFieldChange("effect")}
                    />
                    {applyFilters("cbb.carousel.effectSettings", null, {
                      effect,
                      effectSettings,
                      onEffectSettingsChange:
                        handleCarouselGroupFieldChange("effectSettings"),
                      GroupSettingWrapper,
                    })}
                    <TextControl
                      type="number"
                      label={__(
                        "Transiton duration (ms)",
                        "content-blocks-builder",
                      )}
                      value={getCarouselGroupFieldValue({
                        fieldName: "speed",
                        defaultValue: 100,
                      })}
                      max={10000}
                      min={0}
                      step={50}
                      onChange={handleCarouselGroupFieldChange("speed", toInt)}
                      className="is-row-layout"
                    />
                  </VStack>
                </ToolsPanelItem>
                {effect !== "creative" && (
                  <ToolsPanelItem
                    label={slideDirectionLabel}
                    hasValue={() => direction && direction !== "horizontal"}
                    onDeselect={() =>
                      onChangeCarousel({
                        direction: "horizontal",
                      })
                    }
                  >
                    <SelectControl
                      label={slideDirectionLabel}
                      value={direction}
                      options={[
                        {
                          value: "horizontal",
                          label: __("Horizontal", "content-blocks-builder"),
                        },
                        {
                          value: "vertical",
                          label: __("Vertical", "content-blocks-builder"),
                        },
                      ]}
                      onChange={handleCarouselGroupFieldChange("direction")}
                      help={
                        direction === "vertical" &&
                        __(
                          "In the vertical direction, you must set the height of the carousel block.",
                          "content-blocks-builder",
                        )
                      }
                    />
                  </ToolsPanelItem>
                )}
                <ToolsPanelItem
                  label={__("Autoplay & loop mode", "content-blocks-builder")}
                  isShownByDefault
                  hasValue={() =>
                    loopType !== carouselSettingsDefault?.loopType ||
                    !autoplay?.enable ||
                    autoplay?.delay !==
                      carouselSettingsDefault?.autoplay?.delay ||
                    !!autoplay?.pauseOnMouseEnter ||
                    !!autoplay?.disableOnInteraction
                  }
                  onDeselect={() =>
                    onChangeCarousel({
                      loopType: carouselSettingsDefault?.loopType,
                      autoplay: carouselSettingsDefault?.autoplay,
                    })
                  }
                >
                  <VStack spacing={3}>
                    <ToggleControl
                      label={__("Autoplay", "content-blocks-builder")}
                      checked={autoplay?.enable}
                      onChange={onAutoplayChange("enable")}
                    />
                    {autoplay?.enable && (
                      <GroupSettingWrapper>
                        <TextControl
                          type="number"
                          label={__(
                            "Delay time (ms)",
                            "content-blocks-builder",
                          )}
                          value={autoplay?.delay}
                          max={60000}
                          min={0}
                          step={100}
                          onChange={(value) =>
                            onAutoplayChange("delay")(toInt(value))
                          }
                          className="is-row-layout"
                        />
                        <ToggleControl
                          label={__(
                            "Pause on mouse hover",
                            "content-blocks-builder",
                          )}
                          checked={autoplay?.pauseOnMouseEnter}
                          onChange={onAutoplayChange("pauseOnMouseEnter")}
                        />
                        <ToggleControl
                          label={__(
                            "Disable on interaction",
                            "content-blocks-builder",
                          )}
                          checked={autoplay?.disableOnInteraction}
                          onChange={onAutoplayChange("disableOnInteraction")}
                        />
                      </GroupSettingWrapper>
                    )}
                    <SelectControl
                      label={__("Loop mode", "content-blocks-builder")}
                      value={loopType}
                      options={[
                        {
                          value: "none",
                          label: __("None", "content-blocks-builder"),
                        },
                        {
                          value: "infinite",
                          label: __("Infinite", "content-blocks-builder"),
                        },
                        {
                          value: "rewind",
                          label: __("Rewind", "content-blocks-builder"),
                        },
                      ]}
                      onChange={handleCarouselGroupFieldChange("loopType")}
                      help={loopTypeHelp}
                    />
                  </VStack>
                </ToolsPanelItem>
                <ToolsPanelItem
                  label={__("Slides per view & gap", "content-blocks-builder")}
                  isShownByDefault
                  hasValue={() =>
                    parseFloat(slidesPerView) !== 1 ||
                    carousel?.spaceBetween !== 0 ||
                    !!carousel?.sliderPerGroup ||
                    enableResponsive ||
                    !!carousel?.oneSlidePerViewInMobile ||
                    !!centeredSlides ||
                    !!centeredSlidesSettings?.enable
                  }
                  onDeselect={() =>
                    onChangeCarousel({
                      slidesPerView: 1,
                      spaceBetween: 0,
                      sliderPerGroup: undefined,
                      enableResponsive: false,
                      breakpoints: carouselSettingsDefault?.breakpoints,
                      oneSlidePerViewInMobile: undefined,
                      centeredSlides: undefined,
                      centeredSlidesSettings: undefined,
                    })
                  }
                >
                  <VStack spacing={3}>
                    {!enableResponsive && (
                      <>
                        {!isSingleEffect && (
                          <>
                            <InputWithAutoControl
                              label={itemsPerViewLabel}
                              value={slidesPerViewRaw}
                              onChange={handleCarouselGroupFieldChange(
                                "slidesPerView",
                              )}
                            />
                            {slidesPerView >= 2 && (
                              <>
                                <SelectControl
                                  label={itemsPerGroupLabel}
                                  value={slidesPerGroup}
                                  options={getSelectOptions(1, slidesPerView)}
                                  onChange={handleCarouselGroupFieldChange(
                                    "slidesPerGroup",
                                    toInt,
                                  )}
                                  className="is-row-layout is-number"
                                />
                              </>
                            )}
                          </>
                        )}
                        <UnitControl
                          label={spaceBetweenItemsLabel}
                          value={`${getCarouselGroupFieldValue({
                            fieldName: "spaceBetween",
                            defaultValue: 0,
                          })}px`}
                          onChange={handleCarouselGroupFieldChange(
                            "spaceBetween",
                            toInt,
                          )}
                          units={[
                            {
                              a11yLabel: "Pixels (px)",
                              label: "px",
                              step: 1,
                              value: "px",
                            },
                          ]}
                        />
                        {slidesPerView > 1 && (
                          <ToggleControl
                            label={__(
                              "Show 1 item per view in mobile",
                              "content-blocks-builder",
                            )}
                            checked={getCarouselGroupFieldValue({
                              fieldName: "oneSlidePerViewInMobile",
                              defaultValue: false,
                            })}
                            onChange={handleCarouselGroupFieldChange(
                              "oneSlidePerViewInMobile",
                            )}
                          />
                        )}
                      </>
                    )}
                    {!isSingleEffect && (
                      <>
                        <ToggleControl
                          label={getProLabel(
                            __(
                              "Allow difference settings per screen size",
                              "content-blocks-builder",
                            ),
                          )}
                          checked={enableResponsive}
                          onChange={handleCarouselGroupFieldChange(
                            "enableResponsive",
                          )}
                          disabled={!isPremium}
                        />
                        {enableResponsive &&
                          applyFilters(
                            "cbb.carousel.responsiveBreakpoints",
                            null,
                            {
                              breakpoints,
                              getSelectOptions,
                              onBreakpointFieldChange,
                              onBreakpointGroupChange,
                              GroupSettingWrapper,
                              itemsPerViewLabel,
                              itemsPerGroupLabel,
                              spaceBetweenItemsLabel,
                            },
                          )}

                        <ToggleControl
                          label={__(
                            "Active slide is at the center",
                            "content-blocks-builder",
                          )}
                          checked={centeredSlides}
                          onChange={handleCarouselGroupFieldChange(
                            "centeredSlides",
                          )}
                        />
                        {centeredSlides &&
                          effect === "slide" &&
                          applyFilters("cbb.carousel.centeredSlides", null, {
                            centeredSlidesSettings,
                            onFieldChange: (field) => (value) => {
                              const changed = {
                                ...centeredSlidesSettings,
                                [field]: value,
                              };
                              if (
                                field === "enable" &&
                                value &&
                                isNaN(centeredSlidesSettings?.inactiveSize)
                              ) {
                                changed.inactiveSize = 0.9;
                              }
                              handleCarouselGroupFieldChange(
                                "centeredSlidesSettings",
                              )(changed);
                            },
                            defaultSize: 0.9,
                            GroupSettingWrapper,
                          })}
                      </>
                    )}
                  </VStack>
                </ToolsPanelItem>
                <ToolsPanelItem
                  label={equalHeightLabel}
                  hasValue={() => !!carousel?.equalHeight}
                  onDeselect={() =>
                    onChangeCarousel({
                      equalHeight: undefined,
                    })
                  }
                >
                  <ToggleControl
                    label={equalHeightLabel}
                    checked={getCarouselGroupFieldValue({
                      fieldName: "equalHeight",
                      defaultValue: false,
                    })}
                    onChange={handleCarouselGroupFieldChange("equalHeight")}
                  />
                </ToolsPanelItem>
                <ToolsPanelItem
                  label={__("Pagination", "content-blocks-builder")}
                  isShownByDefault
                  hasValue={() =>
                    !pagination?.enable ||
                    !isDeepEqual(pagination, {
                      ...paginationDefault,
                      enable: true,
                    })
                  }
                  onDeselect={() =>
                    onChangeCarousel({
                      pagination: paginationDefault,
                    })
                  }
                >
                  <VStack spacing={3}>
                    <ToggleControl
                      label={__("Show pagination", "content-blocks-builder")}
                      checked={pagination?.enable}
                      onChange={onPaginationChange("enable")}
                      help={
                        !isPremium
                          ? __(
                              "Pro version can customize the style of the pagination like size, gap, dimension, active, inactive color",
                              "content-blocks-builder",
                            )
                          : ""
                      }
                    />
                    {pagination?.enable &&
                      applyFilters("cbb.carousel.pagination", null, {
                        pagination,
                        onPropertyChange: onPaginationChange,
                        onColorPropertyChange: onGroupColorPropertyChange(
                          "pagination",
                          pagination,
                        ),
                        GroupSettingWrapper,
                        spacingLabel,
                      })}
                  </VStack>
                </ToolsPanelItem>
                <ToolsPanelItem
                  label={__("Navigation", "content-blocks-builder")}
                  hasValue={() =>
                    navigation?.enable ||
                    !isDeepEqual(navigation, {
                      ...navigationDefault,
                      enable: navigation?.enable,
                    })
                  }
                  onDeselect={() =>
                    onChangeCarousel({
                      navigation: navigationDefault,
                    })
                  }
                >
                  <VStack spacing={3}>
                    <ToggleControl
                      label={__("Show navigation", "content-blocks-builder")}
                      checked={navigation?.enable}
                      onChange={onNavigationChange("enable")}
                      help={
                        !isPremium
                          ? __(
                              "Pro version can choose the custom icon for the next/prev buttons.",
                              "content-blocks-builder",
                            )
                          : ""
                      }
                    />
                    {navigation?.enable && (
                      <GroupSettingWrapper>
                        <UnitControl
                          label={buttonSizeLabel}
                          value={navigation?.size}
                          onChange={onNavigationChange("size")}
                        />
                        {!navigation?.position && (
                          <UnitControl
                            label={spacingLabel}
                            value={navigation?.spacing}
                            onChange={onNavigationChange("spacing")}
                            placeholder="10"
                          />
                        )}
                        <ColorGradientDropdown
                          enableAlpha={true}
                          settings={[
                            {
                              label: __("Color", "content-blocks-builder"),
                              onColorChange: onGroupColorPropertyChange(
                                "navigation",
                                navigation,
                              )("color"),
                              colorValue: getColorObject(navigation?.color)
                                ?.value,
                            },
                          ]}
                        />
                        {applyFilters("cbb.carousel.navigation", null, {
                          navigation,
                          onPropertyChange: onNavigationChange,
                          canGroupPagination,
                        })}
                      </GroupSettingWrapper>
                    )}
                  </VStack>
                </ToolsPanelItem>
                <ToolsPanelItem
                  label={__("Play/pause", "content-blocks-builder")}
                  hasValue={() => playPause?.enable}
                  onDeselect={() =>
                    onChangeCarousel({
                      playPause: {},
                    })
                  }
                >
                  <VStack spacing={3}>
                    <ToggleControl
                      label={__("Show play/pause", "content-blocks-builder")}
                      checked={playPause?.enable}
                      onChange={onPlayPauseChange("enable")}
                    />
                    {playPause?.enable &&
                      applyFilters("cbb.carousel.playPause", null, {
                        playPause,
                        canGroupPagination,
                        onPropertyChange: onPlayPauseChange,
                        onColorPropertyChange: onGroupColorPropertyChange(
                          "playPause",
                          playPause,
                        ),
                        GroupSettingWrapper,
                        buttonSizeLabel,
                      })}
                  </VStack>
                </ToolsPanelItem>
                <ToolsPanelItem
                  label={__("Scrollbar", "content-blocks-builder")}
                  hasValue={() => !!scrollbar?.enable}
                  onDeselect={() =>
                    onChangeCarousel({
                      scrollbar: {},
                    })
                  }
                >
                  <VStack spacing={3}>
                    <ToggleControl
                      label={__("Show scrollbar", "content-blocks-builder")}
                      checked={scrollbar?.enable}
                      onChange={onScrollbarChange("enable")}
                      help={
                        !isPremium
                          ? __(
                              "Pro version can customize the style of the scrollbar like size, track, slider color",
                              "content-blocks-builder",
                            )
                          : ""
                      }
                    />
                    {scrollbar?.enable &&
                      applyFilters("cbb.carousel.scrollbar", null, {
                        scrollbar,
                        onPropertyChange: onScrollbarChange,
                        onColorPropertyChange: onGroupColorPropertyChange(
                          "scrollbar",
                          scrollbar,
                        ),
                        GroupSettingWrapper,
                        spacingLabel,
                      })}
                  </VStack>
                </ToolsPanelItem>
                <ToolsPanelItem
                  label={__("Sync carousels", "content-blocks-builder")}
                  hasValue={() => !!thumbs?.enable}
                  onDeselect={() =>
                    onChangeCarousel({
                      thumbs: {},
                    })
                  }
                >
                  <VStack spacing={3}>
                    <ToggleControl
                      label={getProLabel(
                        __(
                          "Use another carousel as navigation",
                          "content-blocks-builder",
                        ),
                      )}
                      checked={thumbs?.enable}
                      onChange={onThumbsChange("enable")}
                      disabled={!isPremium}
                      help={
                        !isPremium
                          ? __(
                              "Pro version can manage this feature.",
                              "content-blocks-builder",
                            )
                          : ""
                      }
                    />
                    {thumbs?.enable &&
                      applyFilters("cbb.carousel.thumbs", null, {
                        thumbs,
                        onSelectorChange: onThumbsChange("selector"),
                      })}
                  </VStack>
                </ToolsPanelItem>
                <ToolsPanelItem
                  label={__("Preloader", "content-blocks-builder")}
                  hasValue={() => !!carousel?.displayLoader}
                  onDeselect={() =>
                    onChangeCarousel({
                      displayLoader: undefined,
                    })
                  }
                >
                  <ToggleControl
                    label={__("Show preloader", "content-blocks-builder")}
                    checked={getCarouselGroupFieldValue({
                      fieldName: "displayLoader",
                      defaultValue: false,
                    })}
                    onChange={handleCarouselGroupFieldChange("displayLoader")}
                  />
                </ToolsPanelItem>
              </ToolsPanel>
            </InspectorControls>
          </>
        )}
      </>
    );
  };
}, "withInspectorControls");
addFilter(
  "editor.BlockEdit",
  `boldblocks/${featureName}/withInspectorControls`,
  withInspectorControls,
);

/**
 * This adds class, style for the feature.
 *
 * @param {Function} BlockListBlock Original component.
 *
 * @return {Function} Wrapped component.
 */
export const withBlockListClassStyles = createHigherOrderComponent(
  (BlockListBlock) => (props) => {
    if (
      !hasSupportLayout(props.name, featureName, layoutType, {
        context: "BlockList",
      })
    ) {
      return <BlockListBlock {...props} />;
    }

    // Allow the ability to cancel the support.
    if (
      !applyFilters("boldblocks.hasSupportCarousel", true, {
        props,
        context: "BlockList",
      })
    ) {
      return <BlockListBlock {...props} />;
    }

    if (props.name === "core/post-template") {
      return <BlockListBlock {...props} />;
    }

    const { getBlocks } = useSelect((select) => select(blockEditorStore), []);

    const {
      clientId,
      attributes: { boldblocks: { carousel = {} } = {} } = {},
    } = props;

    // Get all slide blocks
    const slides = getBlocks(clientId);

    // Get slides count
    const slideCount = slides.length;

    // Get current breakpoint
    const breakpoint = getBreakpointType();
    let previewMode = "EDIT";
    if (breakpoint === "lg") {
      if (props.name === "core/query") {
        previewMode = getPreviewMode(props.clientId);
        if (!previewMode) {
          previewMode = "EDIT";
        }
      } else {
        previewMode = getSettingGroupFieldValue({
          fieldName: "previewMode",
          defaultValue: "EDIT",
          attributes: props.attributes,
          groupName: "carousel",
        });
      }
    }

    let wrapperProps = props.wrapperProps;
    if (props.name !== "core/post-template") {
      wrapperProps = {
        ...props.wrapperProps,
        className: clsx(
          props.wrapperProps?.className,
          `cbb-carousel-${previewMode.toLowerCase()}`,
        ),
      };
    }

    const selector = `${props.name.replace("/", "-")}-${nanoid(5)}`;
    const cssVariables = buildEditLayoutStyle(carousel, slideCount, breakpoint);

    const style = buildCarouselEditStyle({
      selector: `.${selector}`,
      cssVariables,
    });

    return (
      <>
        <BlockListBlock
          {...props}
          className={clsx(props?.className, {
            [selector]: style,
          })}
          wrapperProps={wrapperProps}
        />
        {style && <style>{style}</style>}
      </>
    );
  },
);
addFilter(
  "editor.BlockListBlock",
  `boldblocks/${featureName}/withBlockListClassStyles`,
  withBlockListClassStyles,
);

/**
 * Override props assigned to save component to inject the CSS variables definition.
 *
 * @param {Object} props      Additional props applied to save element.
 * @param {Object} blockType  Block type.
 * @param {Object} attributes Block attributes.
 *
 * @return {Object} Filtered props applied to save element.
 */
export function addSaveProps(props, blockType, attributes) {
  const hookContext = attributes?.isBlockEdit ? "EditProps" : "SaveProps";
  if (
    !hasSupportLayout(blockType, featureName, layoutType, {
      attributes,
      context: hookContext,
    })
  ) {
    return props;
  }

  const originalProps = { ...props };

  if (blockType.name !== "core/post-template") {
    // Don't add js-carousel-layout to post template block
    props.className = clsx(props.className, "js-carousel-layout", {
      "is-block-editor": attributes?.isBlockEdit,
    });
  }

  const { boldblocks: { carousel = {} } = {}, isBlockEdit } = attributes;

  const isDeprecatedAutoplay = hasBlockSupport(
    blockType,
    "CBBDeprecatedCarouselAutoplay",
  );

  let carouselSettings;
  if (isBlockEdit) {
    carouselSettings = usePropsStyle({
      value: carousel,
      getStyle: (value) => () =>
        JSON.stringify(buildCarouselSettings(value, { isBlockEdit })),
    });
  } else {
    carouselSettings = JSON.stringify(
      buildCarouselSettings(carousel, { isDeprecatedAutoplay }),
    );
  }

  const isDeprecatedSettings = hasBlockSupport(
    blockType,
    "CBBDeprecatedCarouselSettings",
  );

  if (isBlockEdit || isDeprecatedSettings) {
    props["data-carousel-settings"] = carouselSettings;
  }

  // Place at this low to prevent from hooks issue.
  if (
    !applyFilters("boldblocks.hasSupportCarousel", true, {
      props: { attributes, name: blockType.name },
      context: hookContext,
    })
  ) {
    return originalProps;
  }

  return props;
}
addFilter(
  "blocks.getSaveContent.extraProps",
  `boldblocks/${featureName}/addSaveProps`,
  addSaveProps,
);

/**
 * Filters registered block settings to extend the block edit wrapper
 * to apply the desired styles and className properly.
 *
 * @param {Object} settings Original block settings.
 *
 * @return {Object}.Filtered block settings.
 */
addFilter(
  "blocks.registerBlockType",
  `boldblocks/${featureName}/addEditProps`,
  addEditProps(
    addSaveProps,
    (settings, { featureName, layoutType }) =>
      hasSupportLayout(settings, featureName, layoutType),
    { featureName, layoutType },
  ),
);
