'use client';

import { useCallback, useEffect, useRef, useState } from 'react';

import type { Swiper as SwiperType } from 'swiper';

import { HOMEPAGE_CAROUSEL_STOP_ON_INTERACTION } from '@/config/homepageCarousel';

const wrapIndex = (value: number, total: number) =>
  total > 0 ? ((value % total) + total) % total : 0;

export interface UseAppSwiperOptions {
  slideCount: number;
  loop?: boolean;
  autoplay?: boolean;
  intervalMs?: number;
  stopOnInteraction?: boolean;
  initialIndex?: number;
  onIndexChange?: (index: number) => void;
}

export interface AppSwiperBind {
  onSwiper: (swiper: SwiperType) => void;
  onSlideChange: (swiper: SwiperType) => void;
  onReachBeginning: (swiper: SwiperType) => void;
  onReachEnd: (swiper: SwiperType) => void;
  onFromEdge: (swiper: SwiperType) => void;
}

export interface AppSwiperController {
  activeIndex: number;
  isReady: boolean;
  isBeginning: boolean;
  isEnd: boolean;
  next: () => void;
  prev: () => void;
  goTo: (index: number) => void;
  pauseAutoplay: () => void;
  resumeAutoplay: () => void;
  bind: AppSwiperBind;
}

export const useAppSwiper = ({
  slideCount,
  loop,
  autoplay = true,
  stopOnInteraction = HOMEPAGE_CAROUSEL_STOP_ON_INTERACTION,
  initialIndex = 0,
  onIndexChange,
}: UseAppSwiperOptions): AppSwiperController => {
  const swiperRef = useRef<SwiperType | null>(null);
  const onIndexChangeRef = useRef(onIndexChange);
  const [isReady, setIsReady] = useState(false);
  const [activeIndex, setActiveIndex] = useState(() => wrapIndex(initialIndex, slideCount));
  const [isBeginning, setIsBeginning] = useState(true);
  const [isEnd, setIsEnd] = useState(slideCount <= 1);

  const resolvedLoop = (loop ?? slideCount > 1) && slideCount > 1;

  useEffect(() => {
    onIndexChangeRef.current = onIndexChange;
  }, [onIndexChange]);

  const syncEdgeState = useCallback(
    (swiper: SwiperType | null) => {
      if (!swiper || slideCount <= 1) {
        setIsBeginning(true);
        setIsEnd(true);
        return;
      }
      if (resolvedLoop) {
        setIsBeginning(false);
        setIsEnd(false);
        return;
      }
      setIsBeginning(swiper.isBeginning);
      setIsEnd(swiper.isEnd);
    },
    [resolvedLoop, slideCount]
  );

  useEffect(() => {
    if (slideCount <= 0) {
      setActiveIndex(0);
      setIsBeginning(true);
      setIsEnd(true);
      return;
    }

    setActiveIndex((prev) => wrapIndex(prev, slideCount));
    syncEdgeState(swiperRef.current);
  }, [slideCount, syncEdgeState]);

  useEffect(() => {
    const nextIndex = wrapIndex(initialIndex, slideCount);
    setActiveIndex(nextIndex);

    const swiper = swiperRef.current;
    if (!swiper) return;

    if (resolvedLoop) {
      swiper.slideToLoop(nextIndex, 0);
    } else {
      swiper.slideTo(nextIndex, 0);
    }
    syncEdgeState(swiper);
  }, [initialIndex, resolvedLoop, slideCount, syncEdgeState]);

  const pauseAutoplay = useCallback(() => {
    if (!autoplay) return;
    swiperRef.current?.autoplay?.stop();
  }, [autoplay]);

  const resumeAutoplay = useCallback(() => {
    if (!autoplay || slideCount <= 1) return;
    swiperRef.current?.autoplay?.start();
  }, [autoplay, slideCount]);

  const stopAutoplayOnInteraction = useCallback(() => {
    if (stopOnInteraction) {
      pauseAutoplay();
    }
  }, [pauseAutoplay, stopOnInteraction]);

  const goTo = useCallback(
    (index: number) => {
      const target = wrapIndex(index, slideCount);
      setActiveIndex(target);
      stopAutoplayOnInteraction();

      const swiper = swiperRef.current;
      if (!swiper) return;

      if (resolvedLoop) {
        swiper.slideToLoop(target);
        return;
      }
      swiper.slideTo(target);
    },
    [resolvedLoop, slideCount, stopAutoplayOnInteraction]
  );

  const next = useCallback(() => {
    if (slideCount <= 1) return;
    stopAutoplayOnInteraction();
    const swiper = swiperRef.current;
    if (!swiper) {
      setActiveIndex((prev) => wrapIndex(prev + 1, slideCount));
      return;
    }
    swiper.slideNext();
  }, [slideCount, stopAutoplayOnInteraction]);

  const prev = useCallback(() => {
    if (slideCount <= 1) return;
    stopAutoplayOnInteraction();
    const swiper = swiperRef.current;
    if (!swiper) {
      setActiveIndex((prev) => wrapIndex(prev - 1, slideCount));
      return;
    }
    swiper.slidePrev();
  }, [slideCount, stopAutoplayOnInteraction]);

  const onSwiper = useCallback(
    (swiper: SwiperType) => {
      swiperRef.current = swiper;
      setIsReady(true);

      const nextIndex = wrapIndex(activeIndex, slideCount);
      if (slideCount > 0) {
        if (resolvedLoop) {
          swiper.slideToLoop(nextIndex, 0);
        } else {
          swiper.slideTo(nextIndex, 0);
        }
      }
      syncEdgeState(swiper);
    },
    [activeIndex, resolvedLoop, slideCount, syncEdgeState]
  );

  const onSlideChange = useCallback(
    (swiper: SwiperType) => {
      const index = wrapIndex(swiper.realIndex, slideCount);
      setActiveIndex(index);
      onIndexChangeRef.current?.(index);
      syncEdgeState(swiper);
    },
    [slideCount, syncEdgeState]
  );

  const onReachBeginning = useCallback(
    (swiper: SwiperType) => {
      syncEdgeState(swiper);
    },
    [syncEdgeState]
  );

  const onReachEnd = useCallback(
    (swiper: SwiperType) => {
      syncEdgeState(swiper);
    },
    [syncEdgeState]
  );

  const onFromEdge = useCallback(
    (swiper: SwiperType) => {
      syncEdgeState(swiper);
    },
    [syncEdgeState]
  );

  return {
    activeIndex,
    isReady,
    isBeginning,
    isEnd,
    next,
    prev,
    goTo,
    pauseAutoplay,
    resumeAutoplay,
    bind: {
      onSwiper,
      onSlideChange,
      onReachBeginning,
      onReachEnd,
      onFromEdge,
    },
  };
};
