/* eslint-disable @next/next/no-img-element */
"use client";

import { useRef } from "react";
import gsap from "gsap";
import { useGSAP } from "@gsap/react";
import { ScrollTrigger } from "gsap/ScrollTrigger";
import Parallax from "@/components/effects/Parallax";
import { slides } from "./constants";

import "./excellence.css";

const Excellence = () => {
  const sliderRef = useRef<HTMLDivElement>(null);
  useGSAP(
    () => {
      const slideImages = document.querySelector(".slide-images");
      const textElement = document.getElementById("title-text");
      const excellenceNumber = document.getElementById("excellence-number");
      const excellenceDescription = document.getElementById(
        "excellence-description"
      );

      const totalSlides = slides.length;
      const stripsCount = 23;
      let currentTitleIndex = 0;
      let queuedTitleIndex: number | null = null;
      let isAnimating = false;

      const thumbnails = gsap.utils.toArray(".slider-thumbnail");
      thumbnails.forEach((thumbnail) => {
        gsap.set(thumbnail as HTMLElement, {
          clipPath: "inset(0% 0% 0% 0%)",
        });
      });

      // Preload all images to prevent white boxes
      const preloadImages = () => {
        const imagePromises = slides.map((slide) => {
          return new Promise((resolve, reject) => {
            const img = new Image();
            img.onload = resolve;
            img.onerror = reject;
            img.src = slide.image;
          });
        });
        return Promise.all(imagePromises);
      };

      const firstSlideImage = document.querySelector(
        "#img-1 img"
      ) as HTMLElement;
      firstSlideImage.style.transform = "scale(1.25)";

      // Wait for images to preload before creating strips
      preloadImages()
        .then(() => {
          for (let i = 1; i < totalSlides; i++) {
            const imgContainer = document.createElement("div");
            imgContainer.className = "img-container";
            imgContainer.id = `img-container-${i + 1}`;
            imgContainer.style.opacity = "0";

            for (let j = 0; j < stripsCount; j++) {
              const strip = document.createElement("div");
              strip.className = "strip";
              const img = document.createElement("img");
              img.src = slides[i].image;
              img.alt = slides[i].title;
              img.style.transform = "scale(1.25)";
              img.style.opacity = "0"; // Start hidden

              // Fade in image when loaded
              img.onload = () => {
                img.style.opacity = "1";
                img.style.transition = "opacity 0.3s ease";
              };

              const stripPositionFromBottom = stripsCount - j - 1;
              const stripLowerBound =
                (stripPositionFromBottom + 1) * (100 / stripsCount);

              const stripUpperBound =
                stripPositionFromBottom * (100 / stripsCount);

              strip.style.clipPath = `polygon(0 ${stripLowerBound}%, 100% ${stripLowerBound}%, 100% ${stripUpperBound}%, 0 ${stripUpperBound}%)`;

              strip.appendChild(img);
              imgContainer.appendChild(strip);
            }
            if (slideImages !== null) {
              slideImages.appendChild(imgContainer);
            }
          }
        })
        .catch((error) => {
          console.error("Error preloading images:", error);
          // Continue with strip creation even if preloading fails
          for (let i = 1; i < totalSlides; i++) {
            const imgContainer = document.createElement("div");
            imgContainer.className = "img-container";
            imgContainer.id = `img-container-${i + 1}`;
            imgContainer.style.opacity = "0";

            for (let j = 0; j < stripsCount; j++) {
              const strip = document.createElement("div");
              strip.className = "strip";
              const img = document.createElement("img");
              img.src = slides[i].image;
              img.alt = slides[i].title;
              img.style.transform = "scale(1.25)";

              const stripPositionFromBottom = stripsCount - j - 1;
              const stripLowerBound =
                (stripPositionFromBottom + 1) * (100 / stripsCount);

              const stripUpperBound =
                stripPositionFromBottom * (100 / stripsCount);

              strip.style.clipPath = `polygon(0 ${stripLowerBound}%, 100% ${stripLowerBound}%, 100% ${stripUpperBound}%, 0 ${stripUpperBound}%)`;

              strip.appendChild(img);
              imgContainer.appendChild(strip);
            }
            if (slideImages !== null) {
              slideImages.appendChild(imgContainer);
            }
          }
        });

      const transitionCount = totalSlides - 1;
      const scrollDistancePerTransition = 1000;
      const initialScrollDelay = 300;
      const finalScrollDelay = 300;

      const totalScrollDistance =
        transitionCount * scrollDistancePerTransition +
        initialScrollDelay +
        finalScrollDelay;

      const transitionRange: {
        transition: number;
        startVh: number;
        endVh: number;
        startPercent: number;
        endPercent: number;
      }[] = [];
      let currentScrollPosition = initialScrollDelay;

      for (let i = 0; i < transitionCount; i++) {
        const transitionStart = currentScrollPosition;
        const transitionEnd = transitionStart + scrollDistancePerTransition;

        transitionRange.push({
          transition: i,
          startVh: transitionStart,
          endVh: transitionEnd,
          startPercent: transitionStart / totalScrollDistance,
          endPercent: transitionEnd / totalScrollDistance,
        });

        currentScrollPosition = transitionEnd;
      }

      function calculateImageProgress(scrollProgress: number) {
        let imageProgress = 0;

        if (scrollProgress < transitionRange[0].startPercent) {
          return 0;
        }

        if (
          scrollProgress >
          transitionRange[transitionRange.length - 1].endPercent
        ) {
          return transitionRange.length;
        }

        for (let i = 0; i < transitionRange.length; i++) {
          const range = transitionRange[i];

          if (
            scrollProgress >= range.startPercent &&
            scrollProgress <= range.endPercent
          ) {
            const rangeSize = range.endPercent - range.startPercent;
            const normalizedProgress =
              (scrollProgress - range.startPercent) / rangeSize;
            imageProgress = i + normalizedProgress;
            break;
          } else if (scrollProgress > range.endPercent) {
            imageProgress = i + 1;
          }
        }

        return imageProgress;
      }

      function getScaleForImage(
        imageIndex: number,
        currentImageIndex: number,
        progress: number
      ) {
        if (imageIndex > currentImageIndex) return 1.25;
        if (imageIndex < currentImageIndex - 1) return 1;

        const totalProgress =
          imageIndex === currentImageIndex ? progress : 1 + progress;
        return 1.25 - (totalProgress * 0.25) / 2;
      }

      function animateTitleChange(index: number, direction: string) {
        if (index === currentTitleIndex) return;
        if (index < 0 || index >= slides.length) return;

        if (isAnimating) {
          queuedTitleIndex = index;
          return;
        }

        isAnimating = true;
        const newTitle = slides[index].title;
        const newNumber = slides[index].number;
        const newDescription = slides[index].description;

        const outY = direction === "down" ? "-120%" : "120%";
        const inY = direction === "down" ? "120%" : "-120%";

        gsap.killTweensOf(textElement);
        gsap.killTweensOf(excellenceNumber);
        gsap.killTweensOf(excellenceDescription);
        gsap.to([textElement, excellenceNumber, excellenceDescription], {
          y: outY,
          duration: 0.5,
          ease: "power3.out",
          onComplete: () => {
            (textElement as HTMLElement).textContent = newTitle;
            (excellenceNumber as HTMLElement).textContent = newNumber;
            (excellenceDescription as HTMLElement).textContent = newDescription;
            gsap.set([textElement, excellenceNumber, excellenceDescription], {
              y: inY,
            });

            gsap.to([textElement, excellenceNumber, excellenceDescription], {
              y: "0%",
              duration: 0.5,
              ease: "power3.out",
              onComplete: () => {
                isAnimating = false;
                currentTitleIndex = index;

                if (
                  queuedTitleIndex !== (null as unknown as number) &&
                  queuedTitleIndex !== currentTitleIndex
                ) {
                  const nextTitle = queuedTitleIndex;
                  queuedTitleIndex = null;
                  animateTitleChange(nextTitle as number, direction);
                }
              },
            });
          },
        });
      }

      let lastImageProgress = 0;
      let displayedTitleIndex = 0;

      // Cache DOM elements for better performance
      const cachedElements = {
        containers: [] as HTMLElement[],
        strips: [] as NodeListOf<Element>[],
        images: [] as NodeListOf<HTMLImageElement>[],
      };

      // Pre-cache all DOM elements
      const cacheElements = () => {
        for (let i = 1; i < totalSlides; i++) {
          const imgIndex = i + 1;
          const imgContainer = document.getElementById(
            `img-container-${imgIndex}`
          );
          if (imgContainer) {
            cachedElements.containers[i] = imgContainer as HTMLElement;
            cachedElements.strips[i] = imgContainer.querySelectorAll(".strip");
            cachedElements.images[i] = imgContainer.querySelectorAll("img");
          }
        }
      };

      let animationFrameId: number | null = null;
      const throttledUpdate = (
        imageProgress: number,
        currentImageIndex: number,
        imageSpecificProgress: number
      ): void => {
        if (animationFrameId) return;

        animationFrameId = window.requestAnimationFrame(() => {
          // Update first slide image
          const firstSlideImgScale = getScaleForImage(
            0,
            currentImageIndex,
            imageSpecificProgress
          );
          firstSlideImage.style.transform = `scale(${firstSlideImgScale})`;

          // Update other slides
          for (let i = 1; i < totalSlides; i++) {
            const imgIndex = i + 1;
            const transitionIndex = imgIndex - 2;

            const imgContainer = cachedElements.containers[i];
            if (!imgContainer || !(imgContainer instanceof HTMLElement))
              continue;

            (imgContainer as HTMLElement).style.opacity = "1";
            const strips = cachedElements.strips[i] as NodeListOf<Element>;
            const images = cachedElements.images[
              i
            ] as NodeListOf<HTMLImageElement>;

            if (transitionIndex < currentImageIndex) {
              strips.forEach((strip: Element, stripIndex: number) => {
                const stripPositionFromBottom = stripsCount - stripIndex - 1;
                const stripUpperBound =
                  stripPositionFromBottom * (100 / stripsCount);
                const stripLowerBound =
                  (stripPositionFromBottom + 1) * (100 / stripsCount);
                // Add small overlap to prevent black lines
                const overlap = 0.05;
                (strip as HTMLElement).style.clipPath = `polygon(0 ${
                  stripLowerBound + overlap
                }%, 100% ${stripLowerBound + overlap}%, 100% ${
                  stripUpperBound - overlap
                }%, 0% ${stripUpperBound - overlap}%)`;
              });
            } else if (transitionIndex === currentImageIndex) {
              strips.forEach((strip: Element, stripIndex: number) => {
                const stripPositionFromBottom = stripsCount - stripIndex - 1;
                const stripUpperBound =
                  stripPositionFromBottom * (100 / stripsCount);
                const stripLowerBound =
                  (stripPositionFromBottom + 1) * (100 / stripsCount);
                const stripDelay = (stripIndex / stripsCount) * 0.5;
                const adjustedProgress = Math.max(
                  0,
                  Math.min(1, (imageSpecificProgress - stripDelay) * 2)
                );
                // Add overlap to prevent black lines
                const overlap = 0.05;
                const currentstripUpperBound =
                  stripLowerBound +
                  overlap -
                  (stripLowerBound + overlap - (stripUpperBound - overlap)) *
                    adjustedProgress;
                (strip as HTMLElement).style.clipPath = `polygon(0% ${
                  stripLowerBound + overlap
                }%, 100% ${
                  stripLowerBound + overlap
                }%, 100% ${currentstripUpperBound}%, 0% ${currentstripUpperBound}%)`;
              });
            } else {
              strips.forEach((strip: Element, stripIndex: number) => {
                const stripPositionFromBottom = stripsCount - stripIndex - 1;
                const stripLowerBound =
                  (stripPositionFromBottom + 1) * (100 / stripsCount);
                // Add overlap to prevent black lines
                const overlap = 0.05;
                (strip as HTMLElement).style.clipPath = `polygon(0% ${
                  stripLowerBound + overlap
                }%, 100% ${stripLowerBound + overlap}%, 100% ${
                  stripLowerBound + overlap
                }%, 0% ${stripLowerBound + overlap}%)`;
              });
            }

            const imgScale = getScaleForImage(
              transitionIndex,
              currentImageIndex,
              imageSpecificProgress
            );
            images.forEach((image: HTMLImageElement) => {
              image.style.transform = `scale(${imgScale})`;
            });
          }

          // Update clipPath for stacked thumbnails: hide past, clip current, show future
          thumbnails.forEach((thumb, i) => {
            const el = thumb as HTMLElement;
            if (i < currentImageIndex) {
              // Past slides: fully hidden
              el.style.clipPath = "inset(0% 0% 100% 0%)";
            } else if (i === currentImageIndex) {
              // Current slide: progressively hide to reveal the next
              el.style.clipPath = `inset(0% 0% ${
                imageSpecificProgress * 100
              }% 0%)`;
            } else {
              // Future slides: fully visible (but underneath due to z-index)
              el.style.clipPath = "inset(0% 0% 0% 0%)";
            }
          });

          animationFrameId = null;
        });
      };

      ScrollTrigger.create({
        trigger: ".sticky-slider",
        start: "top top",
        end: `+=${totalScrollDistance}vh`,
        pin: true,
        pinSpacing: true,
        scrub: 1,
        invalidateOnRefresh: true,
        onUpdate: (self) => {
          const imageProgress = calculateImageProgress(self.progress);

          if (typeof imageProgress === "number") {
            const scrollDirection =
              imageProgress > lastImageProgress ? "down" : "up";
            const currentImageIndex = Math.floor(imageProgress);
            const imageSpecificProgress = imageProgress - currentImageIndex;

            const HOLD_START = 0.2; // 20% rest before transition begins
            const HOLD_END = 0.2; // 20% rest after transition ends
            let phasedProgress = imageSpecificProgress;
            if (imageSpecificProgress <= HOLD_START) {
              phasedProgress = 0;
            } else if (imageSpecificProgress >= 1 - HOLD_END) {
              phasedProgress = 1;
            } else {
              phasedProgress =
                (imageSpecificProgress - HOLD_START) /
                (1 - HOLD_START - HOLD_END);
            }

            // Use throttled update for better performance
            throttledUpdate(imageProgress, currentImageIndex, phasedProgress);

            // Change texts only AFTER a slide transition completes
            if (scrollDirection === "down" && phasedProgress === 1) {
              const targetIndex = Math.min(
                currentImageIndex + 1,
                slides.length - 1
              );
              if (targetIndex !== displayedTitleIndex && !isAnimating) {
                animateTitleChange(targetIndex, scrollDirection);
                displayedTitleIndex = targetIndex;
              }
            } else if (scrollDirection === "up" && phasedProgress === 0) {
              const targetIndex = Math.max(currentImageIndex, 0);
              if (targetIndex !== displayedTitleIndex && !isAnimating) {
                animateTitleChange(targetIndex, scrollDirection);
                displayedTitleIndex = targetIndex;
              }
            }

            lastImageProgress = imageProgress;
          }
        },
        onRefresh: () => {
          cacheElements();
        },
      });

      // Initial cache
      setTimeout(cacheElements, 100);
    },
    { scope: sliderRef }
  );
  return (
    <>
      <section
        ref={sliderRef}
        data-speed="0"
        className="xl:min-h-dvh relative z-10"
      >
        <div className="sticky-slider">
          <div className="slide-images">
            <div className="img" id="img-1">
              <Parallax amount={120} start="top bottom" end="bottom center">
                {/* eslint-disable-next-line @next/next/no-img-element */}
                <img
                  className="slider-image w-full h-full object-cover"
                  src={slides[0].image}
                  alt={slides[0].title}
                />
              </Parallax>
            </div>
          </div>
          <h3 className="md:pl-32 xl:pl-32 md:pb-10 xl:pb-10 font-times-sans text-white text-left pl-4 text-[clamp(80px,10.7vw,100px)] absolute bottom-0 left-0 uppercase">
            All Eyes
            <br />
            on Designs
          </h3>
          {/* inner card */}
          <div className="slide-info z-30 flex justify-end items-center">
            <div className="container mx-auto h-full flex justify-end items-center relative px-4 lg:px-8">
              <div className="bg-white py-10 min-h-[600px] flex flex-col justify-between ml-auto origin-right lg:scale-[0.9] xl:scale-[0.8] 2xl:scale-100">
                <div className="text-center font-creato">
                  <p className="mb-7 flex items-center overflow-hidden justify-center font-normal text-xl">
                    <span
                      id="excellence-number"
                      className="excellence-number overflow-hidden"
                    >
                      01
                    </span>{" "}
                    <span className="relative -top-[1px] mx-1">-</span>{" "}
                    <span className="text-[#A7A7A7]">
                      {slides.length < 10 ? `0${slides.length}` : slides.length}
                    </span>
                  </p>
                  <div className="slide-title">
                    <p
                      id="title-text"
                      className="font-normal text-[24px] leading-[100%] tracking-[0.1em] uppercase excellence-card-title"
                    >
                      Superior Quality
                    </p>
                  </div>
                </div>
                <div className="py-9 px-10 flex justify-center">
                  <div className="relative overflow-hidden w-[500px] h-[300px]">
                    {slides.map((data, index) => (
                      <img
                        key={`${data.id}`}
                        className="slider-thumbnail absolute inset-0 w-full h-full object-cover"
                        style={{
                          zIndex: slides.length - index,
                        }}
                        src={`${data.image}`}
                        alt={`${data.title}`}
                      />
                    ))}
                  </div>
                </div>
                <div className="text-center overflow-hidden min-w-96 mt-4 h-32 flex items-center justify-center">
                  <p
                    id="excellence-description"
                    className="max-w-96 mx-auto font-creato font-normal text-[14px] leading-[28px] tracking-[0] text-center excellence-description"
                  >
                    Every detail is thoughtfully designed and built to the
                    highest standards, ensuring long-term durability and refined
                    elegance. From premium materials to flawless finishes, the
                    result is a living experience defined by excellence and
                    trust.
                  </p>
                </div>
              </div>
            </div>
          </div>
        </div>
      </section>
    </>
  );
};

export default Excellence;
