"use client";

import React, { Fragment, useCallback, useEffect, useMemo, useRef, useState } from "react";
import Image from "next/image";
import { AnimatePresence, motion } from "framer-motion";
import { Playfair_Display } from "next/font/google";

import { playfair } from "@/lib/fonts";


const CMS_BASE_URL =
  process.env.NEXT_PUBLIC_CMS_BASE_URL?.replace(/\/$/, "") ?? "";

import {
  type RichTextRoot,
  type RichTextChild,
  renderLexical,
  extractPlainText
} from "../utils";

type MediaSizes = {
  small?: { url?: string | null };
  medium?: { url?: string | null };
  large?: { url?: string | null };
  og?: { url?: string | null };
};

type MediaDocument = {
  url?: string | null;
  alt?: string | null;
  sizes?: MediaSizes | null;
};

type TestimonialRating = {
  id?: string;
  "platform-name"?: string | null;
  score?: string | null;
  label?: string | null;
  platformIconId?: number | null;
};

type TestimonialItem = {
  id?: string;
  quote?: RichTextRoot | null;
  author?: string | null;
  rating?: number | null;
  role?: string | null;
  company?: string | null;
  avatarId?: number | null;
};

type TestimonialBlock = {
  type?: string;
  props?: {
    heading?: RichTextRoot | null;
    ratings?: TestimonialRating[];
    items?: TestimonialItem[];
    meta?: {
      anchorId?: string | null;
    } | null;
  } | null;
};

type ScillyPageData = {
  layout?: TestimonialBlock[];
  included?: {
    media?: Record<string, MediaDocument>;
  } | null;
};

type Testimonial = {
  id: string;
  name: React.ReactNode;
  rating: number;
  quoteTitle: React.ReactNode;
  quote: React.ReactNode;
};

type ReviewRating = {
  id: string;
  platform: string;
  score: string;
  label: string;
  icon: string | null;
  alt: string;
  width: number;
  height: number;
};

function isTestimonialBlock(
  block: TestimonialBlock | undefined,
): block is TestimonialBlock {
  return block?.type === "testimonial";
}

function toAbsoluteUrl(url?: string | null) {
  if (!url) return null;
  if (url.startsWith("http://") || url.startsWith("https://")) return url;
  if (!CMS_BASE_URL || !url.startsWith("/")) return url;
  return `${CMS_BASE_URL}${url}`;
}

function resolvePreferredImageUrl(media: MediaDocument | null) {
  return toAbsoluteUrl(
    media?.sizes?.small?.url ??
    media?.sizes?.medium?.url ??
    media?.sizes?.large?.url ??
    media?.url,
  );
}

function cleanQuote(value: string) {
  return value
    .replace(/^["']+|["']+$/g, "")
    .replace(/\s+/g, " ")
    .trim();
}

function extractQuoteParts(quote?: RichTextRoot | null) {
  const children = quote?.root?.children ?? [];
  const heading = children.find((child) => child.type === "heading");
  const paragraphs = children.filter((child) => child.type === "paragraph");

  return {
    title: heading ? renderLexical(heading) : null,
    body: paragraphs.length > 0 ? (
      <>
        {paragraphs.map((p, i) => (
          <Fragment key={i}>
            {renderLexical(p)}
            {i < paragraphs.length - 1 && <br />}
          </Fragment>
        ))}
      </>
    ) : null,
  };
}

function wrapIndex(index: number, length: number) {
  return (index + length) % length;
}

function renderStars(rating: number, className: string) {
  const starCount = Math.max(0, Math.min(5, Math.round(rating)));

  return Array.from({ length: starCount }).map((_, index) => (
    <span key={index} className={className}>
      &#9733;
    </span>
  ));
}

export default function TestimonialCarousel({
  data,
}: {
  data: ScillyPageData;
}) {
  const testimonialBlock = data.layout?.find(isTestimonialBlock);
  const [currentIndex, setCurrentIndex] = useState(0);
  const [direction, setDirection] = useState(1);
  const [hoveredImage, setHoveredImage] = useState<{ id: string; x: number; y: number } | null>(null);
  const hoverTimerRef = React.useRef<NodeJS.Timeout | null>(null);
  const touchStartX = useRef<number | null>(null);
  const touchStartY = useRef<number | null>(null);

  const handleTouchStart = useCallback((e: React.TouchEvent) => {
    touchStartX.current = e.touches[0].clientX;
    touchStartY.current = e.touches[0].clientY;
  }, []);

  const handleTouchEnd = useCallback((e: React.TouchEvent) => {
    if (touchStartX.current === null || touchStartY.current === null) return;
    const dx = e.changedTouches[0].clientX - touchStartX.current;
    const dy = e.changedTouches[0].clientY - touchStartY.current;
    touchStartX.current = null;
    touchStartY.current = null;

    if (Math.abs(dx) < 30 || Math.abs(dx) < Math.abs(dy)) return;
    if (dx < 0) {
      setDirection(1);
      setCurrentIndex((prev) => wrapIndex(prev + 1, total));
    } else {
      setDirection(-1);
      setCurrentIndex((prev) => wrapIndex(prev - 1, total));
    }
  }, []);

  const heading = extractPlainText(testimonialBlock?.props?.heading).trim();

  const reviewRatings = useMemo<ReviewRating[]>(() => {
    const ratings = testimonialBlock?.props?.ratings ?? [];
    const mediaMap = data.included?.media ?? {};

    const mapped = ratings
      .map((rating, index) => {
        const media =
          rating.platformIconId != null
            ? (mediaMap[String(rating.platformIconId)] ?? null)
            : null;
        const icon = resolvePreferredImageUrl(media);
        const platform = rating["platform-name"]?.trim() ?? "";

        if (!icon && !platform && !rating.score && !rating.label) {
          return null;
        }

        return {
          id: rating.id ?? `${platform || "rating"}-${index}`,
          platform,
          score: rating.score?.trim() ?? "",
          label: rating.label?.trim() ?? "",
          icon,
          alt: media?.alt?.trim() || platform || "Review platform",
          width: platform.toLowerCase().includes("google") ? 40 : 80,
          height: platform.toLowerCase().includes("google") ? 40 : 38,
        };
      })
      .filter((rating): rating is ReviewRating => Boolean(rating));

    return mapped;
  }, [data.included?.media, testimonialBlock?.props?.ratings]);

  const testimonials = useMemo<Testimonial[]>(() => {
    const items = testimonialBlock?.props?.items ?? [];

    const mapped = items
      .map((item, index) => {
        const quoteParts = extractQuoteParts(item.quote);
        const name = item.author?.trim() ?? "";

        if (!quoteParts.body && !name) {
          return null;
        }

        return {
          id: item.id ?? String(index),
          name: name,
          rating: item.rating ?? 5,
          quoteTitle: quoteParts.title,
          quote: quoteParts.body || quoteParts.title,
        } as Testimonial;
      })
      .filter((item): item is Testimonial => item !== null);

    return mapped;
  }, [testimonialBlock?.props?.items]);

  const total = testimonials.length;
  const safeCurrentIndex = total > 0 ? wrapIndex(currentIndex, total) : 0;

  const handleNext = () => {
    if (total <= 1) return;
    setDirection(1);
    setCurrentIndex((prev) => wrapIndex(prev + 1, total));
  };

  const handlePrev = () => {
    if (total <= 1) return;
    setDirection(-1);
    setCurrentIndex((prev) => wrapIndex(prev - 1, total));
  };

  useEffect(() => {
    if (total <= 1) return;

    const interval = setInterval(() => {
      setDirection(1);
      setCurrentIndex((prev) => wrapIndex(prev + 1, total));
    }, 5000);

    return () => clearInterval(interval);
  }, [total, currentIndex]);

  if (!testimonialBlock || !heading || total === 0) {
    return null;
  }

  const getOffset = useCallback(
    (index: number) => {
      if (total <= 1) return 0;
      let offset = index - safeCurrentIndex;
      const half = total / 2;
      if (offset < -half) offset += total;
      if (offset > half) offset -= total;
      if (offset === half) offset = -half;
      return offset;
    },
    [safeCurrentIndex, total]
  );

  return (
    <section
      id={testimonialBlock?.props?.meta?.anchorId ?? undefined}
      className="relative w-full overflow-hidden px-4 pb-[30px] pt-[28px] md:px-8 md:pb-[60px]"
    >
      <div className="mx-auto max-w-[1400px]">
        <div className="text-center">
          <h2
            className={`text-[24px] font-normal leading-none tracking-[0.03em] text-[#85714D] md:text-[56px] ${playfair.className}`}
          >
            {heading}
          </h2>
        </div>

        <div className="mb-[26px] mt-[24px] flex items-center justify-center gap-[26px] md:mb-[50px] md:mt-[50px] md:gap-[80px]">
          {reviewRatings.map((rating) => (
            <div
              key={rating.id}
              className="relative flex items-center gap-[8px] md:gap-[16px]"
              onMouseMove={rating.icon ? (e) => {
                const r = e.currentTarget.getBoundingClientRect();
                const x = e.clientX - r.left;
                const y = e.clientY - r.top;
                if (hoverTimerRef.current) clearTimeout(hoverTimerRef.current);
                setHoveredImage((prev) => (prev !== null ? null : prev));
                hoverTimerRef.current = setTimeout(() => setHoveredImage({ id: rating.id, x, y }), 1000);
              } : undefined}
              onMouseLeave={rating.icon ? () => {
                if (hoverTimerRef.current) clearTimeout(hoverTimerRef.current);
                setHoveredImage(null);
              } : undefined}
            >
              {rating.icon ? (
                <Image
                  src={rating.icon}
                  alt={rating.alt}
                  width={rating.width}
                  height={rating.height}
                  sizes="(max-width: 768px) 100px, 150px"
                  loading="lazy"
                  className={`h-auto cursor-pointer object-contain ${rating.platform.toLowerCase().includes("google")
                      ? "max-w-[25px] md:max-w-[40px]"
                      : "max-w-[50px] md:max-w-[80px]"
                    }`}
                />
              ) : null}
              <AnimatePresence>
                {hoveredImage?.id === rating.id && (
                  <motion.div
                    initial={{ opacity: 0, scale: 0.95 }}
                    animate={{ opacity: 1, scale: 1 }}
                    exit={{ opacity: 0, scale: 0.95 }}
                    transition={{ duration: 0.12 }}
                    style={{
                      position: "absolute",
                      left: hoveredImage.x,
                      top: hoveredImage.y,
                      transform: "translate(-50%, calc(-100% - 8px))",
                      pointerEvents: "none",
                      zIndex: 100,
                      whiteSpace: "nowrap",
                    }}
                    className="rounded border border-black bg-white px-3 py-1.5 text-sm font-semibold text-black shadow-sm"
                  >
                    {rating.alt}
                  </motion.div>
                )}
              </AnimatePresence>
              <div>
                {rating.score ? (
                  <div className="text-[12px] font-bold leading-none text-gray-900 md:text-[24px]">
                    {rating.score}&#9733; Rated
                  </div>
                ) : null}
                {rating.label ? (
                  <div className="mt-[2px] text-[7px] uppercase tracking-[0.18em] text-gray-600 md:text-[12px] md:tracking-widest">
                    {rating.label}
                  </div>
                ) : null}
              </div>
            </div>
          ))}
        </div>

        <div className="relative mx-auto flex w-full items-center justify-center md:min-h-[358px]">
          <button
            onClick={handleNext}
            aria-label="Previous testimonial"
            className="absolute left-[20px] top-1/2 z-40 hidden h-[40px] w-[40px] -translate-y-1/2 items-center justify-center rounded-full border-[2px] border-[#85714D] bg-transparent transition duration-300 hover:bg-[#85714D]/5 md:flex lg:left-[50px] lg:h-[56px] lg:w-[56px]"
          >
            <svg
              width="10"
              height="18"
              viewBox="0 0 11 21"
              fill="none"
              xmlns="http://www.w3.org/2000/svg"
              className="mr-[2px] lg:h-[25px] lg:w-[15px]"
            >
              <path
                d="M9 19L1.5 10.5L9 2"
                stroke="#85714D"
                strokeWidth="3"
                strokeLinecap="round"
                strokeLinejoin="round"
              />

            </svg>
          </button>

          <button
            onClick={handlePrev}
            aria-label="Next testimonial"
            className="absolute right-[20px] top-1/2 z-40 hidden h-[40px] w-[40px] -translate-y-1/2 items-center justify-center rounded-full border-[2px] border-[#85714D] bg-transparent transition duration-300 hover:bg-[#85714D]/5 md:flex lg:right-[50px] lg:h-[56px] lg:w-[56px]"
          >
            <svg
              width="10"
              height="18"
              viewBox="0 0 11 21"
              fill="none"
              xmlns="http://www.w3.org/2000/svg"
              className="ml-[2px] lg:h-[25px] lg:w-[15px]"
            >
              <path
                d="M2 19L9.5 10.5L2 2"
                stroke="#85714D"
                strokeWidth="3"
                strokeLinecap="round"
                strokeLinejoin="round"
              />
            </svg>
          </button>

          <div className="relative hidden h-[358px] w-full overflow-hidden md:block scale-[0.75] lg:scale-100 origin-center transition-transform duration-300">
            {testimonials.map((item, index) => {
              const offset = getOffset(index);
              const isCenter = offset === 0;
              const isLeft = offset === -1;
              const isRight = offset === 1;
              const isHidden = Math.abs(offset) > 1;

              return (
                <motion.div
                  key={item.id}
                  initial={false}
                  animate={
                    isCenter ? "center" :
                      isLeft ? "left" :
                        isRight ? "right" :
                          offset < 0 ? "hiddenLeft" : "hiddenRight"
                  }
                  variants={{
                    center: { opacity: 1, x: 0, scale: 1, zIndex: 20 },
                    left: { opacity: 1, x: -280, scale: 0.7, zIndex: 10 },
                    right: { opacity: 1, x: 280, scale: 0.7, zIndex: 10 },
                    hiddenLeft: { opacity: 0, x: -380, scale: 0.5, zIndex: 0 },
                    hiddenRight: { opacity: 0, x: 380, scale: 0.5, zIndex: 0 },
                  }}
                  transition={{ duration: 0.6, ease: [0.22, 1, 0.36, 1] }}
                  onClick={() => {
                    if (isLeft) handlePrev();
                    if (isRight) handleNext();
                  }}
                  className={`absolute left-1/2 top-1/2 flex h-[358px] w-[450px] origin-center -translate-x-1/2 -translate-y-1/2 flex-col items-center overflow-hidden rounded-[31px] px-8 py-6 text-center transition-colors duration-500 ${isCenter ? "bg-[#C8A87F]" : "bg-[#F1F1F1] cursor-pointer hover:bg-[#EAEAEA]"
                    } ${isHidden ? "pointer-events-none" : ""}`}
                >
                  <h3
                    className={`mb-3 text-[24px] font-semibold transition-colors duration-500 ${playfair.className} ${isCenter ? "text-black" : "text-[#000000]"
                      }`}
                  >
                    {item.name}
                  </h3>
                  <div className="mb-4 flex items-center justify-center gap-[5px]">
                    {renderStars(item.rating, `text-[18px] transition-colors duration-500 ${isCenter ? "text-white" : "text-[#85714D]"}`)}
                  </div>
                  {item.quoteTitle ? (
                    <p
                      className={`mb-4 shrink-0 px-2 text-[17px] font-semibold leading-[1.4] tracking-[0.06em] transition-colors duration-500 ${playfair.className} ${isCenter ? "text-white" : "text-[#2E2A26]"
                        }`}
                    >
                      &ldquo;{item.quoteTitle}&rdquo;
                    </p>
                  ) : null}
                  <div className={`max-h-[200px] overflow-y-auto scrollbar-none px-2 text-[11px] font-semibold leading-[1.55] tracking-[0.08em] transition-colors duration-500 ${isCenter ? "text-white" : "text-[#2E2A26]"
                    }`}>
                    &ldquo;{item.quote}&rdquo;
                  </div>
                </motion.div>
              );
            })}
          </div>

          <div className="relative block h-[570px] w-full md:hidden">
            {testimonials.map((item, index) => {
              const offset = getOffset(index);
              const isCenter = offset === 0;
              const isLeft = offset === -1;
              const isRight = offset === 1;
              const isHidden = Math.abs(offset) > 1;

              return (
                <motion.div
                  key={`mob-${item.id}`}
                  initial={false}
                  animate={
                    isCenter ? "center" :
                      isLeft ? "left" :
                        isRight ? "right" :
                          offset < 0 ? "hiddenLeft" : "hiddenRight"
                  }
                  variants={{
                    center: { opacity: 1, x: 0, y: 0, scale: 1, zIndex: 20 },
                    left: { opacity: 0.9, x: -140, y: 40, scale: 0.8, zIndex: 10 },
                    right: { opacity: 0.9, x: 140, y: 40, scale: 0.8, zIndex: 10 },
                    hiddenLeft: { opacity: 0, x: -240, y: 40, scale: 0.5, zIndex: 0 },
                    hiddenRight: { opacity: 0, x: 240, y: 40, scale: 0.5, zIndex: 0 },
                  }}
                  transition={{ duration: 0.6, ease: [0.22, 1, 0.36, 1] }}
                  onTouchStart={isCenter ? handleTouchStart : undefined}
                  onTouchEnd={isCenter ? handleTouchEnd : undefined}
                  onClick={() => {
                    if (isLeft) handlePrev();
                    if (isRight) handleNext();
                  }}
                  className={`absolute left-1/2 top-0 flex h-[530px] w-[320px] origin-top -translate-x-1/2 flex-col items-center rounded-[22px] px-[20px] pb-[20px] pt-[30px] text-center transition-colors duration-500 ${isCenter ? "bg-[#C8A87F]" : "bg-[#E5E5E3] cursor-pointer"
                    } ${isHidden ? "pointer-events-none" : ""}`}
                >
                  <h3
                    className={`mb-[10px] text-[18px] font-semibold leading-none transition-colors duration-500 ${playfair.className} ${isCenter ? "text-black" : "text-[#000000]"
                      }`}
                  >
                    {item.name}
                  </h3>
                  <div className="mb-[14px] flex items-center justify-center gap-[3px]">
                    {renderStars(item.rating, `text-[12px] transition-colors duration-500 ${isCenter ? "text-white" : "text-[#85714D]"}`)}
                  </div>
                  {item.quoteTitle ? (
                    <div
                      className={`mb-4 shrink-0 px-2 text-[12px] font-semibold leading-[1.4] tracking-[0.04em] capitalize transition-colors duration-500 ${playfair.className} ${isCenter ? "text-white" : "text-[#2E2A26]"
                        }`}
                    >
                      &ldquo;{item.quoteTitle}&rdquo;
                    </div>
                  ) : null}
                  <div
                    className={`max-h-[390px] overflow-y-auto scrollbar-none px-2 text-[12px] font-medium leading-[1.8] capitalize transition-colors duration-500 ${playfair.className} ${isCenter ? "text-white" : "text-[#2E2A26]"
                      }`}
                  >
                    &ldquo;{item.quote}&rdquo;
                  </div>
                </motion.div>
              );
            })}



            <div className="absolute bottom-0 left-1/2 z-30 flex -translate-x-1/2 items-center justify-center gap-[8px]">
              {testimonials.map((item, index) => (
                <button
                  key={item.id}
                  onClick={() => {
                    setDirection(index > safeCurrentIndex ? 1 : -1);
                    setCurrentIndex(index);
                  }}
                  aria-label={`Go to testimonial ${index + 1}`}
                  className={`h-[9px] w-[9px] rounded-full transition-all duration-300 ${index === safeCurrentIndex ? "bg-[#85714D]" : "bg-[#D2D2D2]"
                    }`}
                />
              ))}
            </div>
          </div>
        </div>
      </div>
    </section>
  );
}