/**
 * External dependencies
 */
import { isObject, isString } from "lodash";

/**
 * WordPress dependencies
 */
import { __ } from "@wordpress/i18n";

/**
 * Internal dependencies
 */
import { isValidSettingObject } from "sdk/utils";
import { toType } from "../../utils/dom";

/**
 * Breakpoint values in px
 */
export const breakpointValues = {
  sm: 576,
  md: 768,
  lg: 1024,
};

/**
 * Define the list of effect options
 */
export const effectOptions = [
  { value: "slide", label: __("Slide", "content-blocks-builder") },
  { value: "fade", label: __("Fade", "content-blocks-builder") },
  { value: "flip", label: __("Flip", "content-blocks-builder") },
];

/**
 * If the current effect support multiple slides
 *
 * @param {String} effect
 * @returns {Boolean}
 */
export const isSingleSlideEffect = (effect) =>
  ["fade", "flip", "cube", "cards", "creative"].includes(effect);

/**
 * Default pagination
 */
export const paginationDefault = {
  enable: true,
  bulletWidth: "12px",
  bulletHeight: "12px",
  spacing: "10px",
  color: "",
  inactiveColor: "",
  opacity: 0.3,
  dynamicBullets: true,
  horizontalGap: "5px",
};

/**
 * Default navigation
 */
export const navigationDefault = {
  enable: false,
  size: "44px",
  spacing: "10px",
  color: "",
};

/**
 * Default scrollbar
 */
export const scrollbarDefault = {
  enable: false,
  trackSize: "5px",
  draggable: true,
  hide: false,
  snapOnRelease: true,
  trackColor: "",
  sliderColor: "",
};

/**
 * Default carousel settings
 */
export const carouselSettingsDefault = {
  speed: 500,
  effect: "slide",
  effectSettings: {},
  loopType: "infinite", // rewind, none
  autoplay: { enable: true, delay: 3000 },
  slidesPerView: 1,
  spaceBetween: 0,
  enableResponsive: false,
  breakpoints: {
    lg: {
      value: {
        slidesPerView: 4, // 'auto'
        spaceBetween: 0,
      },
      inherit: null,
    },
    md: { inherit: "lg" },
    sm: { inherit: "lg" },
  },
  pagination: paginationDefault,
  navigation: navigationDefault,
};

/**
 * Build the carousel settings.
 *
 * @param {Object} carousel
 * @param {Boolean} isDeprecatedAutoplay
 */
export function buildCarouselSettings(
  carousel,
  { isDeprecatedAutoplay = false, isBlockEdit },
) {
  // Extract sub-values.
  const {
    speed,
    loopType,
    autoplay,
    playPause = {},
    direction = "horizontal",
    effect: effectRaw,
    effectSettings,
    slidesPerView: slidesPerViewRaw,
    slidesPerGroup,
    spaceBetween,
    centeredSlides,
    centeredSlidesSettings,
    enableResponsive,
    breakpoints,
    oneSlidePerViewInMobile = false,
    pagination = paginationDefault,
    navigation = navigationDefault,
    scrollbar = scrollbarDefault,
    equalHeight = false,
    thumbs,
  } = carousel ?? {};

  let dataset = {};

  // Speed
  if (speed) {
    dataset.speed = speed;
  }

  // Loop and rewind
  dataset.loop = false;
  dataset.rewind = false;
  if (loopType === "infinite") {
    dataset.loop = true;
  } else if (loopType === "rewind") {
    dataset.rewind = true;
  }

  // Autoplay
  dataset.autoplay = false;
  let enableAutoplay;
  if (isDeprecatedAutoplay) {
    enableAutoplay = autoplay && isObject(autoplay);
  } else {
    enableAutoplay = autoplay && isObject(autoplay) && autoplay?.enable;
  }
  if (enableAutoplay) {
    if (isDeprecatedAutoplay) {
      const { delay } = autoplay;
      if (delay) {
        dataset.autoplay = {
          delay,
        };
      }
    } else {
      const { enable, ...restAutoplay } = autoplay;
      dataset.autoplay = restAutoplay;
    }

    // Only in edit mode
    if (isBlockEdit) {
      if (!dataset.loop && !dataset.rewind) {
        dataset.autoplay = { ...dataset.autoplay, stopOnLastSlide: true };
      }
    }
  }

  // Direction
  if (direction) {
    dataset.direction = direction;
  }

  // Effect
  const effect =
    toType(effectRaw) === "object" ? effectRaw?.name ?? "slide" : effectRaw;
  if (effect) {
    const { [`${effect}Effect`]: settings } = effectSettings ?? {};
    let refinedEffect = effectRaw;
    // Deprecated issue for very old version
    if (
      toType(effectRaw) === "object" &&
      toType(effectRaw?.settings) === "array" &&
      effectRaw.settings.length === 0
    ) {
      refinedEffect = { ...effectRaw, settings: {} };
    }
    dataset.effect = refinedEffect;

    // Effect settings
    if (effect === "fade") {
      dataset.fadeEffect = { crossFade: true };
    } else if (effect === "coverflow") {
      if (isObject(settings)) {
        dataset.coverflowEffect = settings;
      } else {
        dataset.coverflowEffect = {
          slideShadows: false,
        };
      }
    } else if (effect === "creative") {
      if (isObject(settings)) {
        dataset.creativeEffect = settings;
      } else {
        dataset.creativeEffect = {
          prev: {
            translate: ["-20%", 0, -1],
          },
          next: {
            translate: ["100%", 0, 0],
          },
        };
      }

      dataset.direction = "horizontal";
    }
  }

  // slidesPerView
  if (isSingleSlideEffect(effect)) {
    dataset.slidesPerView = 1;
  } else {
    let slidesPerView = slidesPerViewRaw;
    if (toType(slidesPerViewRaw) === "object") {
      slidesPerView = slidesPerViewRaw?.auto
        ? "auto"
        : slidesPerViewRaw?.value ?? 1;
    }

    if (slidesPerViewRaw) {
      // Only in edit mode
      if (isBlockEdit) {
        dataset.slidesPerView = slidesPerView;
      } else {
        dataset.slidesPerView = slidesPerViewRaw;
      }

      if (slidesPerView > 1 && slidesPerGroup) {
        dataset.slidesPerGroup = slidesPerGroup;
      }
    }

    // Breakpoints
    if (enableResponsive && isObject(breakpoints)) {
      let breakpointAtts = {};
      Object.keys(breakpoints).forEach((breakpoint) => {
        const {
          [breakpoint]: { value, inherit },
        } = breakpoints;

        let valueByBreakpoint;
        if (value && isObject(value)) {
          valueByBreakpoint = value;
        }

        if (!valueByBreakpoint && isString(inherit)) {
          const { value: inheritValue } = breakpoints[inherit] ?? {};
          if (inheritValue && isObject(inheritValue)) {
            valueByBreakpoint = inheritValue;
          }
        }

        if (isObject(valueByBreakpoint)) {
          // Only in edit mode
          if (isBlockEdit) {
            valueByBreakpoint.slidesPerView = refineSlidesPerView(
              valueByBreakpoint?.slidesPerView ?? 1,
            );
          }
          breakpointAtts[breakpointValues[breakpoint]] = valueByBreakpoint;
        }
      });

      dataset.breakpoints = breakpointAtts;

      // Only in edit mode
      if (isBlockEdit) {
        const { slidesPerView, slidesPerGroup, spaceBetween } =
          Object.values(breakpointAtts)[0] ?? {};
        if (slidesPerView) {
          dataset.slidesPerView = slidesPerView;
          if (slidesPerView > 1 && slidesPerGroup) {
            dataset.slidesPerGroup = slidesPerGroup;
          }
          if (spaceBetween) {
            dataset.spaceBetween = spaceBetween;
          }
        }
      }
    } else {
      if (oneSlidePerViewInMobile) {
        let breakpointAtts = {
          [breakpointValues.sm]: {
            slidesPerView: 1,
            spaceBetween: 0,
          },
          [breakpointValues.md]: {
            // Only in edit mode
            slidesPerView: isBlockEdit ? slidesPerView : slidesPerViewRaw,
            spaceBetween,
          },
        };

        dataset.breakpoints = breakpointAtts;
      }
    }
  }

  // centeredSlides
  dataset.centeredSlides = centeredSlides;
  if (
    centeredSlides &&
    effect === "slide" &&
    centeredSlidesSettings &&
    centeredSlidesSettings?.enable
  ) {
    dataset.centeredSlidesSettings = centeredSlidesSettings;
  }

  // spaceBetween
  if (spaceBetween) {
    dataset.spaceBetween = spaceBetween;
  }

  // Pagination
  dataset.pagination = !!pagination?.enable;
  dataset.paginationSettings = pagination;

  // Navigation
  dataset.navigation = !!navigation?.enable;
  dataset.navigationSettings = navigation;

  // Play/pause since 2.8.2
  if (playPause?.enable) {
    dataset.playPause = playPause;
  }

  // Scrollbar
  dataset.scrollbar = !!scrollbar?.enable;
  dataset.scrollbarSettings = scrollbar;

  // Equal height?
  if (equalHeight) {
    // Only add the property when it is true to prevent from invalid content
    dataset.equalHeight = equalHeight;
  }

  if (thumbs && thumbs?.enable) {
    dataset.thumbsSettings = thumbs;
  }

  return dataset;
}

export const buildCarouselEditStyle = ({
  selector,
  wraperSelector = ".carousel__inner",
  itemSelector = ".is-carousel-item",
  cssVariables,
}) => {
  let styles = `
  ${selector} {${cssVariables}}
  /* Appender */
  ${selector}.cbb-carousel-edit > ${wraperSelector} > .block-list-appender .block-list-appender__toggle {
    display: flex;
  }
  ${selector}.cbb-carousel-edit > ${wraperSelector} > .block-list-appender {
    bottom: 17px;
    left: calc(var(--bb--carousel--inner-width, 100%) - 24px);
  }
  /* Edit mode */
  ${selector}.cbb-carousel-edit {
    overflow-x: auto;
    overflow-y: hidden;
  }
  ${selector}.cbb-carousel-edit > ${wraperSelector} {
    position: relative;
    display: flex;
    gap: var(--bb--carousel--spacebetween, 0);
    max-width: none;
    scroll-snap-type: x mandatory;
  }
  ${selector}.cbb-carousel-edit > ${wraperSelector} > ${itemSelector} {
    flex-shrink: 0;
    scroll-snap-align: start;
  }
  ${selector}.cbb-carousel-edit > ${wraperSelector} > ${itemSelector} {
    width: var(--bb--carousel--item-width, 100%) !important;
  }
  `;

  return styles;
};

/**
 * Build style for EDIT mode
 *
 * @param {Object} carousel
 * @param {Integer} slideCount
 * @param {String} devicePrefix
 * @returns {String}
 */
export const buildEditLayoutStyle = (carousel, slideCount, devicePrefix) => {
  const {
    effect,
    slidesPerView,
    spaceBetween,
    enableResponsive,
    breakpoints = {},
    oneSlidePerViewInMobile = false,
  } = carousel;

  const styles = [];
  let slidesPerViewValue = refineSlidesPerView(slidesPerView);
  let spaceBetweenValue = spaceBetween;

  if (isSingleSlideEffect(effect)) {
    slidesPerViewValue = 1;
  } else {
    if (enableResponsive && isValidSettingObject(breakpoints)) {
      let { value, inherit } = breakpoints[devicePrefix] ?? {};
      if (!isValidSettingObject(value) && inherit) {
        value = (breakpoints[inherit] ?? {})["value"] ?? {};
      }

      if (isValidSettingObject(value)) {
        if (value?.slidesPerView) {
          slidesPerViewValue = refineSlidesPerView(value.slidesPerView);
        }
        if (value?.spaceBetween) {
          spaceBetweenValue = value.spaceBetween;
        }
      }
    } else {
      if (oneSlidePerViewInMobile && devicePrefix === "sm") {
        slidesPerViewValue = 1;
      }
    }
  }

  if (slidesPerViewValue && slidesPerViewValue !== "auto") {
    styles.push(`--bb--carousel--slidesperview: ${slidesPerViewValue};`);
    slidesPerViewValue = parseFloat(slidesPerViewValue);
    slidesPerViewValue = isNaN(slidesPerViewValue) ? 1 : slidesPerViewValue;
    styles.push(
      `--bb--carousel--inner-width: calc(${
        (1 / slidesPerViewValue) * slideCount * 100
      }%);`,
    );

    styles.push(
      `--bb--carousel--item-width: calc(${100 / slidesPerViewValue}% - ${
        ((slidesPerViewValue - 1) * spaceBetweenValue) / slidesPerViewValue
      }px);`,
    );
  }

  if (spaceBetweenValue) {
    styles.push(`--bb--carousel--spacebetween: ${spaceBetweenValue}px;`);
  }

  return styles.join("");
};

/**
 * Get the valid value for slidesPerView
 *
 * @param {mixed} rawValue
 * @returns {String}
 */
const refineSlidesPerView = (rawValue) => {
  const value =
    toType(rawValue) === "object"
      ? rawValue?.auto
        ? "auto"
        : rawValue?.value
      : rawValue;

  if (value === "auto") return value;

  const numericValue = parseFloat(value);
  return isNaN(numericValue) ? 1 : numericValue;
};

export const minimumSlidesNeeded = (
  spv,
  spg = 1,
  centeredSlides = false,
  breakpoints = false,
) => {
  if (breakpoints) {
    spv = breakpoints?.slidesPerView;
    spg = breakpoints?.slidesPerGroup ?? 1;
  }

  if (!spv || spv === "auto") {
    return 0;
  }

  spv = Math.ceil(parseFloat(spv));
  if (centeredSlides && spv % 2 === 0) spv += 1;

  let loopedSlides = centeredSlides ? Math.max(spg, Math.ceil(spv / 2)) : spg;

  if (loopedSlides % spg !== 0) {
    loopedSlides += spg - (loopedSlides % spg);
  }

  return spv + loopedSlides;
};
