import { useEffect, useRef } from "react";

import { getChangeIndices } from "@/helpers/array";
import { getThemeColors, lerpThemeColors } from "@/helpers/color";
import { mediaQueries, removeCssVar, setCssVar } from "@/helpers/layout";
import { findKeyByIndex, getOrElse } from "@/helpers/map";
import { clamp, mapLinear } from "@/helpers/math";
import { useLenis, useMatchMedia } from "@/hooks";
import { useStore } from "@/store";
import { Theme, type ThemeColors } from "@/types";

interface ThemeOptions {
  scrollTransition: boolean;
}

export default function useTheme(
  { scrollTransition }: ThemeOptions = { scrollTransition: false },
) {
  const lenis = useStore.use.lenis();
  const { scrollProgress } = useLenis();
  const sectionHeights = useStore.use.sectionHeights();
  const sectionThemes = useStore.use.sectionThemes();
  const currentTheme = useStore.use.currentTheme();
  const themeColors = useRef<ThemeColors>();
  const matchesMedium = useMatchMedia(mediaQueries.minWidth.medium);

  function applyThemeColors(colors: ThemeColors) {
    Object.entries(colors).forEach(([key, value]) => {
      setCssVar(`color-${key}`, value);
    });
    themeColors.current = colors;
  }

  useEffect(() => {
    document.documentElement.setAttribute("data-theme", currentTheme);

    if (!scrollTransition) {
      const immediateThemeColors = getThemeColors(currentTheme);
      applyThemeColors(immediateThemeColors);
    }
  }, [currentTheme, scrollTransition]);

  useEffect(() => {
    if (scrollTransition) {
      const totalScroll = Array.from(sectionHeights.values()).reduce(
        (acc, height) => acc + height,
        0,
      );

      const transitionIndices = getChangeIndices(
        Array.from(sectionThemes.values()),
      );

      if (transitionIndices.length === 0) {
        const immediateThemeColors = getThemeColors(currentTheme);
        applyThemeColors(immediateThemeColors);
        return;
      }

      const transitionIndex = transitionIndices[0];

      const startSectionId = findKeyByIndex(sectionThemes, transitionIndex - 1);
      const endSectionId = findKeyByIndex(sectionThemes, transitionIndex);

      const startSectionHeight = getOrElse(sectionHeights, startSectionId, 0);
      const endSectionHeight = getOrElse(sectionHeights, endSectionId, 0);

      const startSectionScrollRatio = startSectionHeight / totalScroll;
      const endSectionScrollRatio = endSectionHeight / totalScroll;

      const startMultiplier = matchesMedium ? 0.85 : 0.75;
      const endMultiplier = matchesMedium ? 1.25 : 0.5;

      const scrollProgressStart = startSectionScrollRatio * startMultiplier;
      const scrollProgressEnd =
        startSectionScrollRatio + endSectionScrollRatio * endMultiplier;

      const mappedScrollProgress = mapLinear(
        scrollProgress,
        [scrollProgressStart, scrollProgressEnd],
        [0, 1],
      );
      const lerpedThemeColors = lerpThemeColors(
        getOrElse(sectionThemes, startSectionId, Theme.Light),
        getOrElse(sectionThemes, endSectionId, Theme.Dark),
        scrollProgress <= scrollProgressStart
          ? 0
          : scrollProgress >= scrollProgressEnd
            ? 1
            : clamp(mappedScrollProgress, 0, 1),
      );

      applyThemeColors(lerpedThemeColors);
    }
  }, [
    lenis,
    scrollProgress,
    matchesMedium,
    scrollTransition,
    sectionHeights,
    sectionThemes,
    currentTheme,
  ]);

  useEffect(() => {
    return () => {
      if (themeColors.current)
        Object.keys(themeColors.current).forEach((key) => {
          removeCssVar(`color-${key}`);
        });
    };
  }, []);
}
