import React, { useState, useEffect } from "react";
import { motion } from "framer-motion";

export type IndexPath = {
  section: number;
  item: number;
};

export type CardStackSection = {
  id: number;
  text: string;
};

export const isEqual = (pathA: IndexPath, pathB: IndexPath): boolean => {
  return pathA.section === pathB.section && pathA.item === pathB.item;
};

export const isLessThan = (pathA: IndexPath, pathB: IndexPath): boolean => {
  if (pathA.section < pathB.section) {
    return true;
  }

  if (pathA.section === pathB.section && pathA.item < pathB.item) {
    return true;
  }

  return false;
};

export const isGreaterThan = (pathA: IndexPath, pathB: IndexPath): boolean => {
  if (pathA.section > pathB.section) {
    return true;
  }

  if (pathA.section === pathB.section && pathA.item > pathB.item) {
    return true;
  }

  return false;
};

interface CardStackSliderProps {
  sections: CardStackSection[][];
}

const containerHeight = 300;
const pageSize = 198;
const cellSize = 198;
const cellSpacing = 212;
const stackSpacing = 194;
const stackScale = 0.82;
const depthScale = 0.9;
const depthOffset = 17;
const maxStackSize = 6;

const getCellIndices = (sections: CardStackSection[][]) => {
  const cellIndices: (IndexPath & CardStackSection)[] = [];

  sections.forEach((section, sectionIndex) => {
    section.forEach((item, itemIndex) => {
      const indexPath = {
        section: sectionIndex,
        item: itemIndex,
        ...item,
      };

      cellIndices.push(indexPath);
    });
  });

  return cellIndices;
};

const sectionsData = [
  [
    { id: 1, text: "Stack 1 Item 1" },
    { id: 2, text: "Stack 1 Item 2" },
    { id: 3, text: "Stack 1 Item 3" },
    { id: 4, text: "Stack 1 Item 4" },
  ],
  [
    { id: 5, text: "Stack 2 Item 1" },
    { id: 6, text: "Stack 2 Item 2" },
    { id: 7, text: "Stack 2 Item 3" },
    { id: 8, text: "Stack 2 Item 4" },
  ],
];

const CardStackSlider: React.FC<CardStackSliderProps> = ({
  sections = sectionsData,
}) => {
  const [offset, setOffset] = useState(0);
  const [touchStart, setTouchStart] = useState(0);
  const [touchEnd, setTouchEnd] = useState(0);

  const cellIndices = getCellIndices(sections);
  const cellCount = cellIndices.length;

  const minOffset = 0;
  const maxOffset = pageSize * Math.max(0, cellCount - 1);

  const sanitizeOffset = (value: number) =>
    Math.min(maxOffset, Math.max(minOffset, value));
  const safeSetOffset = (value: number) => {
    setOffset(sanitizeOffset(value));
  };

  const bottomPage = Math.floor(offset / pageSize);
  const bottomIndex = cellIndices[bottomPage] ?? { item: 0, section: 0 };
  const topPage = bottomPage + 1;
  const topIndex = cellIndices[topPage] ?? bottomIndex;

  const pageProgress = (offset % pageSize) / pageSize;

  const currentPage = Math.floor(offset / pageSize + 0.5);
  const currentIndex = cellIndices[currentPage] ?? { item: 0, section: 0 };

  // Keyboard events
  const handleKeyUp = (e: KeyboardEvent) => {
    if (e.key === "ArrowRight") {
      safeSetOffset(offset + pageSize);
    }

    if (e.key === "ArrowLeft") {
      safeSetOffset(offset - pageSize);
    }
  };

  useEffect(() => {
    document.addEventListener("keydown", handleKeyUp);

    return () => document.removeEventListener("keydown", handleKeyUp);
  }, [offset]);

  // Touch events
  const handleTouchStart = (e: React.TouchEvent) => {
    setTouchStart(e.targetTouches[0].clientX);
  };

  const handleTouchMove = (e: React.TouchEvent) => {
    setTouchEnd(e.targetTouches[0].clientX);
  };

  const handleTouchEnd = () => {
    const touchDiff = touchEnd - touchStart;

    if (touchDiff < 0) {
      safeSetOffset(offset + pageSize);
    } else {
      safeSetOffset(offset - pageSize);
    }
  };

  return (
    <>
      <div
        className="relative w-full overflow-hidden"
        style={{ height: `${containerHeight}px` }}
      >
        {cellIndices.map(({ id, text, ...indexPath }, index) => {
          const getItemOffset = () => {
            if (isEqual(indexPath, bottomIndex)) {
              // left active item
              const progress = pageProgress;
              return -progress * cellSpacing;
            } else if (isEqual(indexPath, topIndex)) {
              // right active item
              const progress = 1 - pageProgress;
              return progress * cellSpacing;
            } else if (isLessThan(indexPath, bottomIndex)) {
              // left stack
              return -cellSpacing;
            } else {
              // right stack
              return cellSpacing;
            }
          };

          const getSectionOffset = () => {
            let distance = 0;

            if (indexPath.section < bottomIndex.section) {
              distance = indexPath.section - bottomIndex.section;

              const previousIndex = cellIndices[bottomPage - 1];

              if (
                previousIndex &&
                previousIndex.section < bottomIndex.section
              ) {
                distance += 1 - pageProgress;
              }
            } else if (indexPath.section > topIndex.section) {
              distance = indexPath.section - topIndex.section;

              const nextIndex = cellIndices[topPage + 1];

              if (nextIndex && nextIndex.section > topIndex.section) {
                distance -= pageProgress;
              }
            }

            return distance * stackSpacing;
          };

          const translateXValue = () => {
            const itemOffset = getItemOffset();
            const sectionOffset = getSectionOffset();
            const xTranslation = itemOffset + sectionOffset;

            return xTranslation; // Numeric value in pixels
          };

          const getBaseScale = () => {
            let progress = 1;

            if (isEqual(indexPath, bottomIndex)) {
              progress = pageProgress;
            } else if (isEqual(indexPath, topIndex)) {
              progress = 1 - pageProgress;
            }

            return 1 - progress * (1 - stackScale);
          };

          const numberOfItems = (section: number) => {
            return sections[section].length;
          };

          const getDepthScale = () => {
            let depth = 0;
            let progress: number | undefined;

            if (isLessThan(indexPath, bottomIndex)) {
              if (bottomIndex.section === indexPath.section) {
                // current section
                progress = pageProgress;
                depth = bottomIndex.item - indexPath.item - 1;
              } else {
                // previous section
                depth = numberOfItems(indexPath.section) - 1 - indexPath.item;
              }
            } else if (isGreaterThan(indexPath, topIndex)) {
              if (indexPath.section === topIndex.section) {
                // current section
                progress = 1 - pageProgress;
                depth = indexPath.item - topIndex.item - 1;
              } else {
                // future section
                depth = indexPath.item;
              }
            }

            let scale = Math.pow(depthScale, depth);
            if (progress !== undefined) {
              scale *= 1 - progress * (1 - depthScale);
            }

            return scale;
          };

          const scaleValue = () => {
            const baseScale = getBaseScale();
            const depthScaleValue = getDepthScale();
            const scale = baseScale * depthScaleValue;

            return scale; // Numeric value
          };

          const translateYValue = () => {
            let depth = 0;
            let progress: number | undefined;

            if (isLessThan(indexPath, bottomIndex)) {
              if (bottomIndex.section === indexPath.section) {
                // current section
                progress = pageProgress;
                depth = bottomIndex.item - indexPath.item - 1;
              } else {
                // previous section
                depth = numberOfItems(indexPath.section) - 1 - indexPath.item;
              }
            } else if (isGreaterThan(indexPath, topIndex)) {
              if (indexPath.section === topIndex.section) {
                // current section
                progress = 1 - pageProgress;
                depth = indexPath.item - topIndex.item - 1;
              } else {
                // future section
                depth = indexPath.item;
              }
            }

            let translation = depth * depthOffset;
            if (progress !== undefined) {
              translation += progress * depthOffset;
            }

            return translation; // Numeric value in pixels
          };

          const alpha = () => {
            let maxDepth = Math.max(1, maxStackSize - 1);
            let depth = 0;
            let progress = 0;

            if (isLessThan(indexPath, bottomIndex)) {
              if (bottomIndex.section === indexPath.section) {
                // current section
                progress = pageProgress;
                depth = bottomIndex.item - indexPath.item - 1;
              } else {
                // previous section
                depth = numberOfItems(indexPath.section) - 1 - indexPath.item;
              }
            } else if (isGreaterThan(indexPath, topIndex)) {
              if (indexPath.section === topIndex.section) {
                // current section
                progress = 1 - pageProgress;
                depth = indexPath.item - topIndex.item - 1;
              } else {
                // future section
                depth = indexPath.item;
              }
            }

            if (depth === maxDepth) {
              return 1 - progress;
            } else if (depth > maxDepth) {
              return 0;
            } else {
              return 1;
            }
          };

          const zIndexValue = () => {
            if (!isEqual(currentIndex, cellIndices[currentPage])) {
              return 0;
            }

            // Negative zIndex'd elements aren't clickable so this ensures we don't go into negative values
            let baseIndex = numberOfItems(currentIndex.section);

            if (
              isLessThan(indexPath, currentIndex) ||
              isEqual(indexPath, currentIndex)
            ) {
              // If the cell is in a previous stack, higher number cells should be higher up.
              return baseIndex + indexPath.item;
            } else {
              // If the cell is in a future stack, the lower number cells should be higher up.
              if (indexPath.section === currentIndex.section) {
                // If the cell is in the current section, make sure the current item overlaps correctly.
                return baseIndex + currentIndex.item - indexPath.item;
              } else {
                return (
                  baseIndex + numberOfItems(indexPath.section) - indexPath.item
                );
              }
            }
          };

          const handleClick = () => {
            safeSetOffset(index * pageSize);
          };

          return (
            <motion.div
              key={id}
              className="absolute flex flex-col items-center justify-center rounded-2xl border border-gray-300 bg-white"
              style={{
                zIndex: zIndexValue(),
                height: `${cellSize}px`,
                width: `${cellSize}px`,
              }}
              onClick={handleClick}
              onTouchStart={handleTouchStart}
              onTouchMove={handleTouchMove}
              onTouchEnd={handleTouchEnd}
              animate={{
                x: translateXValue(),
                y: translateYValue(),
                scale: scaleValue(),
                opacity: alpha(),
              }}
              transition={{
                duration: 0.3,
                ease: [0.2, 0.7, 0.7, 1.15],
              }}
            >
              <p>{text}</p>
            </motion.div>
          );
        })}
      </div>
      <p className="text-center">offset: {offset}</p>
      <p className="text-center">progress: {pageProgress.toFixed(2)}</p>
      <p className="text-center">bottom page: {bottomPage}</p>
      <p className="text-center">page: {currentPage}</p>
      <p className="text-center">top page: {topPage}</p>
      <p className="text-center">currentIndex: {currentIndex.id}</p>
      <div className="flex justify-center">
        <input
          className="w-4/5"
          type="range"
          min={minOffset}
          max={maxOffset}
          value={offset}
          onChange={(e) => safeSetOffset(Number(e.target.value))}
        />
      </div>
    </>
  );
};

export default CardStackSlider;



Make it a version where multiple cards are visible in view instead of only one.