"use client";

/* eslint-disable @next/next/no-img-element */

import React, { useEffect, useRef, useState } from "react";
import { ArrowRight } from "lucide-react";
import styles from "./link-in-bio-preview.module.css";
import { LinkInBioPageCustomization } from "@/lib/linkInBioCustomization";

export type PreviewProductCardItem = {
  id: string;
  brand: string;
  title: string;
  description: string;
  price?: string;
  productLinkType?: "monetized" | "external";
  siteLabel?: string;
  imageUrl?: string;
  imageFallbackUrls?: string[];
  imageAlt: string;
  imageFit?: "cover" | "contain";
  ctaHref?: string;
  ctaLabel?: string;
  showDeals?: boolean;
  showMonetized?: boolean;
  isPaused?: boolean;
  isCustomLink?: boolean;
  isPriority?: boolean;
  priorityBadge?: string;
};

export type PreviewMediaCardItem = {
  id: string;
  thumbnailUrl: string;
  thumbnailFallbackUrls?: string[];
  thumbnailAlt: string;
  mediaUrl?: string;
  posterUrl?: string;
  href?: string;
  showPlayButton?: boolean;
  product: PreviewProductCardItem;
};

export type PreviewCategorySliderConfig = {
  id: string;
  title: string;
  titleColor?: string;
  navColor?: string;
  itemWidth?: string;
  items: PreviewProductCardItem[];
};

export type LinkInBioPreviewSectionConfig =
  | {
      id: string;
      kind: "priority-group";
      title: string;
      bgColor: string;
      items: PreviewProductCardItem[];
    }
  | {
      id: string;
      kind: "category-group";
      title: string;
      bgColor: string;
      titleColor?: string;
      categories: PreviewCategorySliderConfig[];
    }
  | {
      id: string;
      kind: "media";
      title: string;
      bgColor: string;
      titleColor?: string;
      navColor?: string;
      itemWidth?: string;
      mediaHeight?: string;
      maxDesktopCards?: number;
      enforceVisibleCount?: boolean;
      cardsPerViewRules?: Array<{ minWidth: number; cards: number }>;
      reserveSlotsWhenFewerItems?: boolean;
      items: PreviewMediaCardItem[];
    };

function normalizeExternalUrl(url?: string) {
  const trimmedUrl = url?.trim();

  if (!trimmedUrl) {
    return "#";
  }

  if (
    /^[a-zA-Z][a-zA-Z\d+.-]*:/.test(trimmedUrl) ||
    trimmedUrl.startsWith("//")
  ) {
    return trimmedUrl;
  }

  return `https://${trimmedUrl}`;
}

function decodeHtmlEntities(value?: string) {
  if (!value) return "";

  const namedEntities: Record<string, string> = {
    amp: "&",
    lt: "<",
    gt: ">",
    quot: '"',
    apos: "'",
    nbsp: " ",
    copy: "\u00A9",
    reg: "\u00AE",
    trade: "\u2122",
  };

  return value.replace(/&(#x?[0-9a-f]+|[a-z]+);/gi, (match, entity) => {
    if (!entity) return match;

    if (entity[0] === "#") {
      const isHex = entity[1]?.toLowerCase() === "x";
      const rawCode = isHex ? entity.slice(2) : entity.slice(1);
      const codePoint = parseInt(rawCode, isHex ? 16 : 10);

      if (!Number.isFinite(codePoint) || codePoint <= 0) return match;

      try {
        return String.fromCodePoint(codePoint);
      } catch {
        return match;
      }
    }

    const namedKey = entity.toLowerCase();
    return namedEntities[namedKey] ?? match;
  });
}

function VideoControlIcon({ isPlaying }: { isPlaying: boolean }) {
  if (isPlaying) {
    return (
      <svg
        width="28"
        height="28"
        viewBox="0 0 28 28"
        fill="none"
        xmlns="http://www.w3.org/2000/svg"
      >
        <path
          d="M14 2.33337C12.4679 2.33337 10.9508 2.63514 9.53537 3.22145C8.1199 3.80775 6.83378 4.66711 5.75043 5.75046C3.56251 7.93839 2.33334 10.9058 2.33334 14C2.33334 17.0942 3.56251 20.0617 5.75043 22.2496C6.83378 23.333 8.1199 24.1923 9.53537 24.7786C10.9508 25.3649 12.4679 25.6667 14 25.6667C17.0942 25.6667 20.0617 24.4375 22.2496 22.2496C24.4375 20.0617 25.6667 17.0942 25.6667 14C25.6667 12.468 25.3649 10.9509 24.7786 9.5354C24.1923 8.11994 23.3329 6.83381 22.2496 5.75046C21.1662 4.66711 19.8801 3.80775 18.4647 3.22145C17.0492 2.63514 15.5321 2.33337 14 2.33337Z"
          fill="black"
          opacity="0.45"
        />
        <path
          d="M11 9.5V18.5"
          stroke="white"
          strokeWidth="2.5"
          strokeLinecap="round"
        />
        <path
          d="M17 9.5V18.5"
          stroke="white"
          strokeWidth="2.5"
          strokeLinecap="round"
        />
      </svg>
    );
  }

  return (
    <svg
      width="28"
      height="28"
      viewBox="0 0 28 28"
      fill="none"
      xmlns="http://www.w3.org/2000/svg"
    >
      <circle cx="14" cy="14" r="10.5" stroke="white" strokeWidth="1.5" />
      <path
        d="M11 9.25L19 14L11 18.75V9.25Z"
        fill="white"
        stroke="white"
        strokeLinejoin="round"
      />
    </svg>
  );
}

function getButtonStyle(
  style: LinkInBioPageCustomization["buttonAndLinkStyle"],
  backgroundColor: string,
  borderColor: string,
  textColor: string,
): React.CSSProperties {
  const dimensions = {
    small: 36,
    medium: 42,
    large: 50,
  };
  const radii = {
    square: 0,
    rounded: 10,
    pill: 999,
  };
  const normalizedColor = backgroundColor.replace("#", "");
  const softBackground = /^[0-9a-f]{6}$/i.test(normalizedColor)
    ? `rgba(${parseInt(normalizedColor.slice(0, 2), 16)}, ${parseInt(normalizedColor.slice(2, 4), 16)}, ${parseInt(normalizedColor.slice(4, 6), 16)}, 0.14)`
    : "rgba(47, 97, 155, 0.14)";
  return {
    minHeight: dimensions[style.size],
    borderRadius: radii[style.shape],
    color: style.fill === "solid" ? textColor : backgroundColor,
    backgroundColor:
      style.fill === "solid"
        ? backgroundColor
        : style.fill === "soft"
          ? softBackground
          : "#ffffff",
    border:
      style.borderStyle === "none"
        ? "none"
        : `${style.borderStyle} 1px ${borderColor}`,
  };
}

function CustomTextLink({
  item,
  buttonStyle,
  buttonBgColor,
  buttonBorderColor,
  buttonTextColor,
  interactive = true,
}: {
  item: PreviewProductCardItem;
  buttonStyle: LinkInBioPageCustomization["buttonAndLinkStyle"];
  buttonBgColor: string;
  buttonBorderColor: string;
  buttonTextColor: string;
  interactive?: boolean;
}) {
  const className =
    "d-flex align-items-center justify-content-between gap-2 w-100 px-3 py-2 fw-semibold text-decoration-none";
  const style = {
    ...getButtonStyle(buttonStyle, buttonBgColor, buttonBorderColor, buttonTextColor),
    ...(interactive ? {} : { cursor: "default" }),
  };
  const content = (
    <>
      <span className="text-truncate">{item.title}</span>
      <span aria-hidden="true">→</span>
    </>
  );

  if (!interactive) {
    return (
      <div className={className} style={style} aria-disabled="true">
        {content}
      </div>
    );
  }

  return (
    <a
      href={item.ctaHref || "#"}
      target="_blank"
      rel="noopener noreferrer"
      className={className}
      style={style}
    >
      {content}
    </a>
  );
}

function ExternalBadge({
  label,
  variant,
}: {
  label: string;
  variant: "deals" | "monetized";
}) {
  const baseStyle: React.CSSProperties = {
    fontSize: "10px",
    fontWeight: 500,
    padding: "3px 8px",
    position: "absolute",
    width: "max-content",
    height: "auto",
    borderRadius: "100px",
    zIndex: 3,
    display: "flex",
    justifyContent: "center",
    alignItems: "center",
    textDecoration: "none",
    color: "var(--color-black, #000)",
    cursor: "default",
    lineHeight: 1.1,
    boxShadow: "0 4px 12px rgba(15, 23, 42, 0.18)",
  };

  if (variant === "deals") {
    return (
      <span
        className="rounded-5"
        style={{
          ...baseStyle,
          top: "7px",
          left: "8px",
          backgroundColor: "#dfebff",
        }}
      >
        {label}
      </span>
    );
  }

  return (
    <span
      className="rounded-5"
      style={{
        ...baseStyle,
        top: "7px",
        right: "8px",
        backgroundColor: "#d1f7e4",
        border: "1px solid #198754",
      }}
    >
      {label}
    </span>
  );
}

// function PausedBanner() {
//   return (
//     <p
//       className="font-10 text-start d-flex align-items-start justify-content-start gap-2 fw-500 border dismissible-banner status-paused-border rounded-2 w-100 px-2 py-1 mb-2"
//       style={{
//         fontSize: "10px",
//         textAlign: "left",
//         border: "1px solid #DC3545",
//         padding: "4px 8px",
//         marginTop: 0,
//         marginBottom: "8px",
//         lineHeight: 1.2,
//       }}
//     >
//       <svg
//         xmlns="http://www.w3.org/2000/svg"
//         width="24"
//         height="24"
//         viewBox="0 0 24 24"
//         fill="none"
//         stroke="currentColor"
//         strokeWidth="2"
//         strokeLinecap="round"
//         strokeLinejoin="round"
//         className="feather feather-alert-triangle iconSize-14 svg-stroke-red mt-1"
//       >
//         <path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path>
//         <line x1="12" y1="9" x2="12" y2="13"></line>
//         <line x1="12" y1="17" x2="12.01" y2="17"></line>
//       </svg>
//       Link paused.
//       <br />
//       Edit to update.
//     </p>
//   );
// }

function ProductCardBody({
  item,
  ctaBgColor,
  ctaBorderColor,
  ctaTextColor,
  buttonStyle,
  roundedTop = true,
  applyPhpCardLayout = false,
}: {
  item: PreviewProductCardItem;
  ctaBgColor: string;
  ctaBorderColor?: string;
  ctaTextColor?: string;
  buttonStyle: LinkInBioPageCustomization["buttonAndLinkStyle"];
  roundedTop?: boolean;
  applyPhpCardLayout?: boolean;
}) {
  const layoutClass = applyPhpCardLayout
    ? `${styles.phpProductCard}${
        item.isPaused ? ` ${styles.phpProductCardPaused}` : ""
      }`
    : "";
  const wrapperClassName = roundedTop
    ? `${styles.productCard} ${styles.xmlAdsCard} bg-color-white border-0 rounded-3 shadow-sm`
    : `${styles.productCard} ${styles.productCardBottom} ${styles.xmlAdsCard} bg-color-white border-0 shadow-sm`;
  const brandText = decodeHtmlEntities(item.brand);
  const titleText = decodeHtmlEntities(item.title);
  const descriptionText = decodeHtmlEntities(item.description);
  const siteLabelText = decodeHtmlEntities(item.siteLabel);
  const imageSources = [
    item.imageUrl,
    ...(item.imageFallbackUrls || []),
  ].filter(
    (source, index, allSources) =>
      Boolean(source) && allSources.indexOf(source) === index,
  ) as string[];
  const [currentImageIndex, setCurrentImageIndex] = useState(0);
  const currentImageSrc = imageSources[currentImageIndex] || item.imageUrl;
  useEffect(() => {
    setCurrentImageIndex(0);
  }, [item.id, item.imageUrl, item.imageFallbackUrls]);
  // const hasCtaHref = Boolean(
  //   item.ctaHref && item.ctaHref.trim() !== "" && item.ctaHref !== "#",
  // );
  const imageContent = (
    <>
      <img
        src={currentImageSrc}
        alt={item.imageAlt}
        className={styles.productImage}
        style={{
          objectFit: item.imageFit || "cover",
          objectPosition: "center",
          cursor: "default",
        }}
        onError={() => {
          setCurrentImageIndex((currentIndex) =>
            Math.min(currentIndex + 1, imageSources.length - 1),
          );
        }}
      />
      {item.showDeals ? <ExternalBadge label="Deals" variant="deals" /> : null}
    </>
  );

  return (
    <div className={`${wrapperClassName} p-2 ${layoutClass}`}>
      <div className={`${styles.productImageWrap} rounded-3 overflow-hidden`}>
        {imageContent}
      </div>

      <div
        className={`product-info flex-grow-1 text-start px-2 pt-2 pb-1 ${styles.productInfoWrap}`}
      >
        <div className={styles.monetizedBadgeSlot}>
          {item.showMonetized ? (
            <span
              className={`${styles.monetizedBadge} external-link rounded-5 color-black visible monetized-text mt-1`}
            >
              Monetized
            </span>
          ) : null}
        </div>
        <div className={styles.brandSlot}>
          <h4
            className={`text-sm fw-600 color-black mb-0 ${styles.singleLineClamp} ${styles.hoverTooltip}`}
            title={brandText}
          >
            {brandText || <span aria-hidden="true">&nbsp;</span>}
          </h4>
        </div>
        <div className={styles.titleSlot}>
          <h4
            className={`text-md fw-600 color-black mb-0 ${styles.titleClamp} ${styles.hoverTooltip} text-limit-3`}
            title={titleText}
          >
            {titleText || <span aria-hidden="true">&nbsp;</span>}
          </h4>
        </div>
        <div className={styles.descriptionSlot}>
          <h4 className="text-sm fw-400 mb-0">
            <span
              className={`${styles.descriptionClamp} ${styles.hoverTooltip} text-sm text-limit-2`}
              title={descriptionText}
            >
              {descriptionText || <span aria-hidden="true">&nbsp;</span>}
            </span>
          </h4>
        </div>
        <div className={styles.priceSlot}>
          {item.productLinkType !== "external" && item.price ? (
            <div
              className={`d-block price text-md fw-600 mt-0 ${styles.singleLineClamp} ${styles.hoverTooltip}`}
              title={item.price}
            >
              {item.price}
            </div>
          ) : (
            <div
              className="d-block price text-md fw-600 mt-0"
              aria-hidden="true"
            >
              &nbsp;
            </div>
          )}
        </div>
        <div className={styles.siteLabelSlot}>
          {siteLabelText ? (
            <a
              href={normalizeExternalUrl(siteLabelText)}
              target="_blank"
              rel="noopener noreferrer"
              className={`d-block text-xs h-auto color-link w-100 ${styles.singleLineClamp} ${styles.hoverTooltip}`}
              title={siteLabelText}
            >
              {siteLabelText}
            </a>
          ) : (
            <span aria-hidden="true">&nbsp;</span>
          )}
        </div>
      </div>

      <div className="w-100 px-2 pb-2">
        {/* {item.isPaused ? <PausedBanner /> : null} */}
        <button
          type="button"
          className="button-style text-sm w-100 h-32 px-3 py-1 m-0 d-flex align-items-center justify-content-center text-decoration-none"
          disabled
          aria-disabled="true"
          style={{
              ...getButtonStyle(
                buttonStyle,
                ctaBgColor,
                ctaBorderColor || "transparent",
                ctaTextColor || "#ffffff",
              ),
            opacity: 0.55,
            cursor: "not-allowed",
          }}
        >
          {item.ctaLabel || "View"}
        </button>
      </div>
    </div>
  );
}

function CategoryProductCard({
  item,
  ctaBgColor,
  ctaBorderColor,
  ctaTextColor,
  buttonStyle,
}: {
  item: PreviewProductCardItem;
  ctaBgColor: string;
  ctaBorderColor?: string;
  ctaTextColor?: string;
  buttonStyle: LinkInBioPageCustomization["buttonAndLinkStyle"];
}) {
  if (item.isCustomLink) {
    return (
      <div className="item pb-2">
        <CustomTextLink
          item={item}
          buttonStyle={buttonStyle}
          buttonBgColor={ctaBgColor}
          buttonBorderColor={ctaBorderColor || "transparent"}
          buttonTextColor={ctaTextColor || "#ffffff"}
        />
      </div>
    );
  }
  return (
    <div className="item pb-2">
      <ProductCardBody
        item={item}
        ctaBgColor={ctaBgColor}
        ctaBorderColor={ctaBorderColor}
        ctaTextColor={ctaTextColor}
        buttonStyle={buttonStyle}
        applyPhpCardLayout
      />
    </div>
  );
}

function SocialPromoCard({
  item,
  ctaBgColor,
  ctaBorderColor,
  ctaTextColor,
  buttonStyle,
  mediaHeight,
  applyPhpCardLayout,
}: {
  item: PreviewMediaCardItem;
  ctaBgColor: string;
  ctaBorderColor?: string;
  ctaTextColor?: string;
  buttonStyle: LinkInBioPageCustomization["buttonAndLinkStyle"];
  mediaHeight?: string;
  applyPhpCardLayout?: boolean;
}) {
  const isPlayableVideo = Boolean(item.showPlayButton && item.mediaUrl);
  const videoRef = useRef<HTMLVideoElement | null>(null);
  const [isPlaying, setIsPlaying] = useState(false);
  const thumbnailSources = [
    item.thumbnailUrl,
    ...(item.thumbnailFallbackUrls || []),
  ].filter(
    (source, index, allSources) =>
      Boolean(source) && allSources.indexOf(source) === index,
  ) as string[];
  const [currentThumbnailIndex, setCurrentThumbnailIndex] = useState(0);
  const currentThumbnailSrc =
    thumbnailSources[currentThumbnailIndex] || item.thumbnailUrl;
  useEffect(() => {
    setCurrentThumbnailIndex(0);
  }, [item.id, item.thumbnailUrl, item.thumbnailFallbackUrls]);
  const mediaClassName = `${styles.mediaThumb} rounded-top-3 rounded-bottom-0 shadow-sm border-0 mb-0 d-block position-relative overflow-hidden`;
  const mediaStyle = mediaHeight ? { height: mediaHeight } : undefined;
  const togglePlayback = () => {
    const videoElement = videoRef.current;
    if (!videoElement) return;

    if (videoElement.paused) {
      void videoElement.play().catch(() => {
        setIsPlaying(false);
      });
      return;
    }

    videoElement.pause();
  };
  const mediaContent = isPlayableVideo ? (
    <video
      ref={videoRef}
      className="rounded-top-3 rounded-bottom-0"
      src={item.mediaUrl}
      poster={item.posterUrl || item.thumbnailUrl}
      playsInline
      preload="metadata"
      onPlay={() => setIsPlaying(true)}
      onPause={() => setIsPlaying(false)}
      onEnded={() => setIsPlaying(false)}
    />
  ) : (
    <img
      className="rounded-top-3 rounded-bottom-0"
      src={currentThumbnailSrc}
      alt={item.thumbnailAlt}
      onError={() => {
        setCurrentThumbnailIndex((currentIndex) =>
          Math.min(currentIndex + 1, thumbnailSources.length - 1),
        );
      }}
    />
  );

  return (
    <div className="item pb-2">
      <div className={`d-block ${styles.xmlAdsCard}`}>
        {isPlayableVideo ? (
          <div className={mediaClassName} style={mediaStyle}>
            {mediaContent}
            <button
              type="button"
              className={`${styles.playBtn} ${styles.videoControlButton} z-1`}
              aria-label={isPlaying ? "Pause video" : "Play video"}
              onClick={togglePlayback}
            >
              <VideoControlIcon isPlaying={isPlaying} />
            </button>
          </div>
        ) : (
          <a
            href={item.href || "#"}
            target="_blank"
            rel="noopener noreferrer"
            className={mediaClassName}
            style={mediaStyle}
          >
            {mediaContent}
            {item.showPlayButton ? (
              <div
                className={`${styles.playBtn} cursor-pointer z-1`}
                aria-hidden="true"
              >
                <svg
                  width="28"
                  height="28"
                  viewBox="0 0 28 28"
                  fill="none"
                  xmlns="http://www.w3.org/2000/svg"
                >
                  <path
                    d="M14 2.33337C12.4679 2.33337 10.9508 2.63514 9.53537 3.22145C8.1199 3.80775 6.83378 4.66711 5.75043 5.75046C3.56251 7.93839 2.33334 10.9058 2.33334 14C2.33334 17.0942 3.56251 20.0617 5.75043 22.2496C6.83378 23.333 8.1199 24.1923 9.53537 24.7786C10.9508 25.3649 12.4679 25.6667 14 25.6667C17.0942 25.6667 20.0617 24.4375 22.2496 22.2496C24.4375 20.0617 25.6667 17.0942 25.6667 14C25.6667 12.468 25.3649 10.9509 24.7786 9.5354C24.1923 8.11994 23.3329 6.83381 22.2496 5.75046C21.1662 4.66711 19.8801 3.80775 18.4647 3.22145C17.0492 2.63514 15.5321 2.33337 14 2.33337Z"
                    fill="black"
                    opacity="0.45"
                  />
                  <path
                    d="M11.6667 19.25V8.75004L18.6667 14"
                    stroke="white"
                    strokeWidth="2"
                    strokeLinejoin="round"
                    strokeLinecap="round"
                  />
                  <path
                    d="M14 2.33337C12.4679 2.33337 10.9508 2.63514 9.53537 3.22145C8.1199 3.80775 6.83378 4.66711 5.75043 5.75046C3.56251 7.93839 2.33334 10.9058 2.33334 14C2.33334 17.0942 3.56251 20.0617 5.75043 22.2496C6.83378 23.333 8.1199 24.1923 9.53537 24.7786C10.9508 25.3649 12.4679 25.6667 14 25.6667C17.0942 25.6667 20.0617 24.4375 22.2496 22.2496C24.4375 20.0617 25.6667 17.0942 25.6667 14C25.6667 12.468 25.3649 10.9509 24.7786 9.5354C24.1923 8.11994 23.3329 6.83381 22.2496 5.75046C21.1662 4.66711 19.8801 3.80775 18.4647 3.22145C17.0492 2.63514 15.5321 2.33337 14 2.33337Z"
                    stroke="white"
                    strokeWidth="1.5"
                  />
                </svg>
              </div>
            ) : null}
          </a>
        )}

        <ProductCardBody
          item={item.product}
          ctaBgColor={ctaBgColor}
          ctaBorderColor={ctaBorderColor}
          ctaTextColor={ctaTextColor}
          buttonStyle={buttonStyle}
          roundedTop={false}
          applyPhpCardLayout={applyPhpCardLayout}
        />
      </div>
    </div>
  );
}

function CarouselSectionShell<T>({
  title,
  bgColor,
  titleColor,
  navColor = "#2f619b",
  items,
  itemWidth,
  maxDesktopCards = 3,
  enforceVisibleCount = false,
  cardsPerViewRules,
  reserveSlotsWhenFewerItems = false,
  renderItem,
  containerClassName = "px-3 py-3",
}: {
  title: string;
  bgColor: string;
  titleColor?: string;
  navColor?: string;
  items: T[];
  itemWidth: string;
  maxDesktopCards?: number;
  enforceVisibleCount?: boolean;
  cardsPerViewRules?: Array<{ minWidth: number; cards: number }>;
  reserveSlotsWhenFewerItems?: boolean;
  renderItem: (item: T) => React.ReactNode;
  containerClassName?: string;
}) {
  const trackRef = useRef<HTMLDivElement>(null);
  const hasMany = items.length > 1;
  const [canScrollPrev, setCanScrollPrev] = useState(false);
  const [canScrollNext, setCanScrollNext] = useState(hasMany);
  const [cardsPerView, setCardsPerView] = useState(3);

  useEffect(() => {
    const updateCardsPerView = () => {
      if (typeof window === "undefined") return;

      const viewportWidth = window.innerWidth;
      const defaultRules = [
        { minWidth: 0, cards: 1 },
        { minWidth: 600, cards: Math.min(2, maxDesktopCards) },
        { minWidth: 1024, cards: maxDesktopCards },
      ];
      const resolvedRules = (
        cardsPerViewRules?.length ? cardsPerViewRules : defaultRules
      )
        .slice()
        .sort((a, b) => a.minWidth - b.minWidth);

      let nextCards = resolvedRules[0]?.cards || 1;
      for (const rule of resolvedRules) {
        if (viewportWidth >= rule.minWidth) {
          nextCards = rule.cards;
        }
      }

      setCardsPerView(Math.max(1, nextCards));
    };

    updateCardsPerView();
    window.addEventListener("resize", updateCardsPerView);

    return () => {
      window.removeEventListener("resize", updateCardsPerView);
    };
  }, [maxDesktopCards, cardsPerViewRules]);

  const visibleCards = reserveSlotsWhenFewerItems
    ? Math.max(1, cardsPerView)
    : Math.max(1, Math.min(cardsPerView, items.length || 1));
  const itemGap = 16;
  const computedItemWidth = `calc((100% - ${(visibleCards - 1) * itemGap}px) / ${visibleCards})`;

  useEffect(() => {
    const track = trackRef.current;
    if (!track) return;

    const updateScrollState = () => {
      const maxScrollLeft = track.scrollWidth - track.clientWidth;
      const epsilon = 2;

      setCanScrollPrev(track.scrollLeft > epsilon);
      setCanScrollNext(maxScrollLeft - track.scrollLeft > epsilon);
    };

    updateScrollState();
    track.addEventListener("scroll", updateScrollState, { passive: true });
    window.addEventListener("resize", updateScrollState);

    return () => {
      track.removeEventListener("scroll", updateScrollState);
      window.removeEventListener("resize", updateScrollState);
    };
  }, [items.length]);

  const scroll = (direction: "prev" | "next") => {
    const track = trackRef.current;
    if (!track) return;
    track.scrollBy({
      left: direction === "next" ? track.clientWidth : -track.clientWidth,
      behavior: "smooth",
    });
  };

  return (
    <div className={containerClassName} style={{ backgroundColor: bgColor }}>
      <div className="d-flex align-items-center justify-content-between mb-3">
        <p
          className="text-lg fw-600 text-capitalize mb-0"
          style={{ color: titleColor || "#000000" }}
        >
          {title}
        </p>

        {hasMany ? (
          <div className="d-flex gap-1 promotion-bio__nav">
            <button
              type="button"
              aria-label={`Previous ${title}`}
              className="border-0 d-flex align-items-center justify-content-center rounded-circle"
              disabled={!canScrollPrev}
              style={{
                width: "28px",
                height: "28px",
                background: navColor,
                color: "#fff",
                fontSize: "18px",
                lineHeight: 1,
                opacity: canScrollPrev ? 1 : 0.45,
                cursor: canScrollPrev ? "pointer" : "not-allowed",
              }}
              onClick={() => scroll("prev")}
            >
              ‹
            </button>
            <button
              type="button"
              aria-label={`Next ${title}`}
              className="border-0 d-flex align-items-center justify-content-center rounded-circle"
              disabled={!canScrollNext}
              style={{
                width: "28px",
                height: "28px",
                background: navColor,
                color: "#fff",
                fontSize: "18px",
                lineHeight: 1,
                opacity: canScrollNext ? 1 : 0.45,
                cursor: canScrollNext ? "pointer" : "not-allowed",
              }}
              onClick={() => scroll("next")}
            >
              ›
            </button>
          </div>
        ) : null}
      </div>

      <div
        ref={trackRef}
        className={`d-flex gap-3 overflow-auto pb-1 promotion-bio__track ${styles.mediaTrack}`}
      >
        {items.map((item, index) => (
          <div
            key={index}
            className="flex-shrink-0"
            style={{
              width: computedItemWidth,
              minWidth: computedItemWidth,
              maxWidth: enforceVisibleCount ? undefined : itemWidth,
              scrollSnapAlign: "start",
            }}
          >
            {renderItem(item)}
          </div>
        ))}
      </div>
    </div>
  );
}

function CategoryGroupSection({
  section,
  ctaBgColor,
  ctaBorderColor,
  ctaTextColor,
  buttonStyle,
}: {
  section: Extract<LinkInBioPreviewSectionConfig, { kind: "category-group" }>;
  ctaBgColor: string;
  ctaBorderColor?: string;
  ctaTextColor?: string;
  buttonStyle: LinkInBioPageCustomization["buttonAndLinkStyle"];
}) {
  return (
    <div className="px-3 py-3" style={{ backgroundColor: section.bgColor }}>
      <div
        className={`d-flex justify-content-center align-items-center preview-btns rounded-4 color-black text-11 text-uppercase fw-400 bg-color-white h-32 py-1 px-3 mt-0 mb-3 ms-auto me-0 ${styles.sponsoredBadge}`}
      >
        Sponsored
        <a
          href=""
          target="_blank"
          rel="noopener noreferrer"
          aria-label="Sponsored by AdMedia"
          className={styles.sponsoredLogoLink}
        >
          <img
            src="https://admedia.com/assets/img/admedia_logo.png"
            alt="AdMedia"
            className={styles.sponsoredLogo}
          />
        </a>
      </div>
      <div>
        {section.categories.map((category, index) => (
          <CarouselSectionShell
            key={category.id}
            title={category.title}
            bgColor="transparent"
            titleColor={category.titleColor || section.titleColor}
            navColor={category.navColor}
            items={category.items}
            itemWidth={category.itemWidth || "clamp(130px, 32vw, 165px)"}
            maxDesktopCards={3}
            cardsPerViewRules={[
              { minWidth: 0, cards: 1 },
              { minWidth: 600, cards: 2 },
              { minWidth: 1024, cards: 3 },
            ]}
            reserveSlotsWhenFewerItems
            containerClassName={
              index === section.categories.length - 1 ? "pb-0" : "pb-3"
            }
            renderItem={(item) => (
              <CategoryProductCard
                item={item}
                ctaBgColor={ctaBgColor}
                ctaBorderColor={ctaBorderColor}
                ctaTextColor={ctaTextColor}
                buttonStyle={buttonStyle}
              />
            )}
          />
        ))}
      </div>
    </div>
  );
}

function PriorityLinkThumb({
  imageUrl,
  fallbackUrls,
  size,
  radius,
}: {
  imageUrl?: string;
  fallbackUrls?: string[];
  size: number;
  radius: string | number;
}) {
  const sources = [imageUrl, ...(fallbackUrls || [])].filter(
    (source, index, all): source is string =>
      Boolean(source) && all.indexOf(source) === index,
  );
  const sourceKey = sources.join("|");
  const [index, setIndex] = useState(0);

  useEffect(() => {
    setIndex(0);
  }, [sourceKey]);

  if (sources.length === 0) return null;

  return (
    <img
      src={sources[Math.min(index, sources.length - 1)]}
      alt=""
      width={size}
      height={size}
      style={{
        width: size,
        height: size,
        objectFit: "cover",
        borderRadius: radius,
        flexShrink: 0,
      }}
      onError={() => {
        setIndex((current) => Math.min(current + 1, sources.length - 1));
      }}
    />
  );
}

function PriorityGroupSection({
  section,
  buttonStyle,
  buttonBgColor,
  buttonBorderColor,
  buttonTextColor,
}: {
  section: Extract<LinkInBioPreviewSectionConfig, { kind: "priority-group" }>;
  buttonStyle: LinkInBioPageCustomization["buttonAndLinkStyle"];
  buttonBgColor: string;
  buttonBorderColor: string;
  buttonTextColor: string;
}) {
  const sizeOptions = {
    small: { image: 40, minHeight: 56, arrow: 32, fontSize: 13 },
    medium: { image: 48, minHeight: 64, arrow: 36, fontSize: 15 },
    large: { image: 56, minHeight: 72, arrow: 40, fontSize: 17 },
  };
  const shapeRadius = {
    square: 0,
    rounded: 10,
    pill: 999,
  };
  const itemSize = sizeOptions[buttonStyle.size];
  const thumbRadius = buttonStyle.shape === "pill" ? "50%" : shapeRadius[buttonStyle.shape];
  const cardBorderStyle = buttonStyle.borderStyle === "none" ? "solid" : buttonStyle.borderStyle;

  return (
    <section className="px-3 py-3" style={{ backgroundColor: section.bgColor || "#fff8ec" }}>
      <h3 className="text-md fw-600 mb-3">{section.title}</h3>
      <div className="d-flex flex-column gap-3">
        {section.items.map((item) =>
          item.isCustomLink ? (
            <CustomTextLink
              key={item.id}
              item={item}
              buttonStyle={buttonStyle}
              buttonBgColor={buttonBgColor}
              buttonBorderColor={buttonBorderColor}
              buttonTextColor={buttonTextColor}
              interactive={false}
            />
          ) : (
            <div
              key={item.id}
              aria-disabled="true"
              className="d-flex align-items-center gap-3 w-100 px-3 py-2 fw-semibold text-decoration-none"
              style={{
                position: "relative",
                display: "flex",
                alignItems: "center",
                minHeight: itemSize.minHeight,
                minWidth: 0,
                color: "#1f2937",
                backgroundColor: "#ffffff",
                border: `2px ${cardBorderStyle} ${buttonBgColor}`,
                borderRadius: shapeRadius[buttonStyle.shape],
                boxShadow: "0 2px 8px rgba(15, 23, 42, 0.14)",
                cursor: "default",
              }}
            >
              <PriorityLinkThumb
                imageUrl={item.imageUrl}
                fallbackUrls={item.imageFallbackUrls}
                size={itemSize.image}
                radius={thumbRadius}
              />
              <span className="d-flex flex-column flex-grow-1" style={{ minWidth: 0 }}>
                <span className="text-truncate" style={{ minWidth: 0, fontSize: itemSize.fontSize }}>
                  {item.title}
                </span>
                {item.description ? (
                  <span className="text-truncate fw-normal" style={{ minWidth: 0, color: "#777777", fontSize: Math.max(11, itemSize.fontSize - 2) }}>
                    {item.description}
                  </span>
                ) : null}
              </span>
              <span
                aria-hidden="true"
                className="d-flex align-items-center justify-content-center rounded-circle flex-shrink-0"
                style={{
                  ...getButtonStyle(buttonStyle, buttonBgColor, buttonBorderColor, buttonTextColor),
                  width: itemSize.arrow,
                  minWidth: itemSize.arrow,
                  height: itemSize.arrow,
                  minHeight: itemSize.arrow,
                  padding: 0,
                  borderRadius: "50%",
                }}
              >
                <ArrowRight size={Math.round(itemSize.arrow * 0.5)} />
              </span>
              <span
                className="position-absolute top-0 start-0 translate-middle-y rounded-pill px-2 py-1 text-white"
                style={{ backgroundColor: "#2f619b", fontSize: 10 }}
              >
                {item.priorityBadge || "FEATURED"}
              </span>
            </div>
          ),
        )}
      </div>
    </section>
  );
}

export default function LinkInBioPreviewSections({
  sections,
  ctaBgColor,
  ctaBorderColor,
  ctaTextColor,
  buttonStyle,
}: {
  sections: LinkInBioPreviewSectionConfig[];
  ctaBgColor: string;
  ctaBorderColor?: string;
  ctaTextColor?: string;
  buttonStyle: LinkInBioPageCustomization["buttonAndLinkStyle"];
}) {
  return (
    <>
      {sections.map((section) => {
        if (section.kind === "priority-group") {
          return (
            <PriorityGroupSection
              key={section.id}
              section={section}
              buttonStyle={buttonStyle}
              buttonBgColor={ctaBgColor}
              buttonBorderColor={ctaBorderColor || "transparent"}
              buttonTextColor={ctaTextColor || "#ffffff"}
            />
          );
        }
        if (section.kind === "category-group") {
          return (
            <CategoryGroupSection
              key={section.id}
              section={section}
              ctaBgColor={ctaBgColor}
              ctaBorderColor={ctaBorderColor}
              ctaTextColor={ctaTextColor}
              buttonStyle={buttonStyle}
            />
          );
        }

        return (
          <CarouselSectionShell
            key={section.id}
            title={section.title}
            bgColor={section.bgColor}
            titleColor={section.titleColor}
            navColor={section.navColor}
            items={section.items}
            itemWidth={section.itemWidth || "clamp(192px, 46vw, 280px)"}
            maxDesktopCards={section.maxDesktopCards}
            enforceVisibleCount={section.enforceVisibleCount}
            cardsPerViewRules={section.cardsPerViewRules}
            reserveSlotsWhenFewerItems={section.reserveSlotsWhenFewerItems}
            renderItem={(item) => (
              <SocialPromoCard
                item={item}
                ctaBgColor={ctaBgColor}
                ctaBorderColor={ctaBorderColor}
                ctaTextColor={ctaTextColor}
                buttonStyle={buttonStyle}
                mediaHeight={section.mediaHeight}
                applyPhpCardLayout={section.id !== "youtube"}
              />
            )}
          />
        );
      })}
    </>
  );
}
