import React, { useEffect, useMemo, useRef, useState } from "react";
import { isMobile } from "react-device-detect";
import clsx from "clsx";
import { m, useMotionValueEvent, useScroll } from "framer-motion";

import { buildStaticAssetPath } from "@/helpers/file";
import {
  calculateArcPosition,
  clamp,
  radiusFromCircumference,
} from "@/helpers/math";
import { useGlobalAudio, useResizeObserver, useWindowSize } from "@/hooks";
import { useStore } from "@/store";
import type { FlowerPetal, PetalElement } from "@/types";

import { Petal } from "./petal";

import styles from "./styles.module.scss";

export const START_ANGLE = 360;
export const END_ANGLE = 90;
export const ARC_ANGLE = Math.abs(END_ANGLE - START_ANGLE);
export const RADIUS_SIZE = 2;

interface FlowerProps {
  items: FlowerPetal[];
}

export const Flower = ({ items }: FlowerProps) => {
  const midpointIndex = Math.floor(items.length / 2);
  const lastIndex = items.length - 1;

  const showDebugClass = useStore.use.showDebugClass();
  const isMuted = useStore.use.isMuted();
  const setIsMuted = useStore.use.setIsMuted();
  const setAudioAllowed = useStore.use.setAudioAllowed();
  const { playAudioSequence } = useGlobalAudio();
  const flowerContainerRef = useRef<HTMLDivElement>(null);
  const petalsRef = useRef<Array<HTMLDivElement | null>>(
    new Array(items.length),
  );
  const [activePetal, setActivePetal] = useState<PetalElement | null>(null);
  const [scrollProgress, setScrollProgress] = useState(0);
  const [isAnimating, setIsAnimating] = useState(true);
  const { scrollYProgress } = useScroll({
    target: flowerContainerRef,
  });
  const { width } = useWindowSize();
  const { height } = useWindowSize({ triggerOnce: isMobile });
  const { height: firstPetalHeight = 0 } = useResizeObserver<HTMLDivElement>({
    ref: petalsRef.current[0],
  });
  const { height: lastPetalHeight = 0 } = useResizeObserver<HTMLDivElement>({
    ref: petalsRef.current[lastIndex],
  });

  // Intro flower animation
  useEffect(() => {
    const animationDuration = 2500;
    const startValue = -0.5;
    const endValue = 0.043;
    let startTime: number;
    let requestId: number;

    const easeOutExpo = (t: number) => 1 - Math.pow(2, -10 * t);

    const animate = (timestamp: number) => {
      if (!startTime) {
        startTime = timestamp;
      }

      const progress = timestamp - startTime;
      const completion = Math.min(progress / animationDuration, 1);

      const easedValue = easeOutExpo(completion);
      const animated = startValue + (endValue - startValue) * easedValue;

      if (isAnimating) {
        setScrollProgress(animated);
      }

      if (progress < animationDuration && isAnimating) {
        requestId = requestAnimationFrame(animate);
      } else {
        setIsAnimating(false);
      }
    };

    const startAnimation = () => {
      requestId = requestAnimationFrame(animate);
    };

    const timeoutId = setTimeout(startAnimation, 1400);

    return () => {
      clearTimeout(timeoutId);
      if (requestId) {
        cancelAnimationFrame(requestId);
      }
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  useMotionValueEvent(scrollYProgress, "change", (latest) => {
    if (isAnimating) {
      setIsAnimating(false);
    }
    setScrollProgress(latest);
  });

  const totalPetalsHeight = firstPetalHeight * lastIndex + lastPetalHeight;
  const radius = radiusFromCircumference(totalPetalsHeight) * RADIUS_SIZE;

  const xOffset = -radius;
  const yOffset = height / 2 - firstPetalHeight / 2;

  const containerHeight = height * 3;

  const scrollClampMax = lastIndex / 2 / items.length;
  const clippedScrollProgress = clamp(scrollProgress, -0.5, scrollClampMax);

  const petals = useMemo(() => {
    return items.map((item, index) => {
      const clippedIndex = index - midpointIndex;

      const scrollAngleAdjustment = clippedScrollProgress * ARC_ANGLE * -1;

      const baseAngle = (clippedIndex / items.length) * ARC_ANGLE;
      const angle = baseAngle + scrollAngleAdjustment;

      const { x, y } = calculateArcPosition(angle, radius, xOffset, yOffset);

      return { ...item, index, x, y, angle };
    });
  }, [items, midpointIndex, clippedScrollProgress, radius, xOffset, yOffset]);

  useEffect(() => {
    if (activePetal && activePetal.index !== lastIndex) {
      setAudioAllowed(true);
      playAudioSequence([
        {
          src: "https://cdn1.suno.ai/public-audio/click.mp3",
          options: { volume: 0.3, loop: false, html5: true },
        },
        {
          src: activePetal.song?.src,
          options: { volume: 0.3, loop: true, html5: true },
        },
      ]);
    } else {
      setAudioAllowed(false);
    }

    return () => {
      setAudioAllowed(false);
    };
  }, [activePetal, items, lastIndex, playAudioSequence, setAudioAllowed]);

  return (
    <m.div
      ref={flowerContainerRef}
      className={clsx(styles.flowerContainer, {
        [styles.debug]: showDebugClass,
      })}
      style={{ width, height: containerHeight }}
    >
      <m.div className={clsx(styles.flower)} style={{ width, height }}>
        <m.div className={styles.inner} layout>
          {petals.map((petal, index) => (
            <Petal
              ref={(el: HTMLDivElement) => {
                petalsRef.current[index] = el;
              }}
              key={petal.id}
              xOffset={width * 0.1}
              yOffset={yOffset}
              width={width}
              height={height}
              scrollYProgress={scrollYProgress}
              isActive={petal.id === activePetal?.id}
              isLast={petal.index === lastIndex}
              isNotAllowed={index - midpointIndex < 0}
              onClick={() => {
                if (index - midpointIndex >= 0) setActivePetal(petal);
                if (isMuted) setIsMuted(false);
              }}
              onViewportEnter={() => setActivePetal(petal)}
              onViewportLeave={() => {
                if (petal.index === items.length - 2) {
                  setActivePetal(null);
                }
              }}
              {...petal}
            />
          ))}
        </m.div>
      </m.div>
    </m.div>
  );
};

export default Flower;
