import { useEffect, useRef, useState } from 'react'
import { m, useInView } from 'framer-motion'
import cx from 'classnames'

import { DEFAULT_DRAG_ELASTIC, DEFAULT_SPRING_TRANSITION, SWIPE_CONFIDENCE_THRESHOLD } from '@/lib/constants'
import { useBreakpoint, useInterval, useTimeout } from '@/lib/hooks'
import { getSwipePower } from '@/lib/utilities'

import BlurImage from '@/components/shared/BlurImage'
import Container from '@/components/shared/Container'
import Callout from '@/components/blocks/Callout'

type TriggerType = 'manual' | 'auto'

type Props = {
  title?: string
  content?: string
  button?: {
    url: string
    title: string
  }
  slides: {
    imageSlide?: any
    imageCaption: string
  }[]
  reverse?: boolean
}

const drawerVariants = {
  hidden: (reverse: boolean) => {
    return {
      x: reverse ? '100%' : '-100%',
    }
  },
  hover: {
    x: 0,
  },
}

const SLIDER_AUTOPLAY_PAUSE = 15000
const SLIDER_AUTOPLAY_NEXT_SLIDE_DELAY = 8000

const Controls = ({
  slides,
  page,
  updatePage,
  trackOffset,
  className,
}: {
  slides: Props['slides']
  page: number
  updatePage(newPage: number, triggerType: TriggerType): void
  trackOffset: number
  className?: string
}) => {
  return (
    <div className={className}>
      <ul className="flex gap-1">
        {slides.map((_, index) => (
          <li key={index}>
            <button
              className={cx('w-6 h-1 transition-colors', page === index ? 'bg-gray-dark' : 'bg-gray')}
              onClick={() => {
                updatePage(index, 'manual')
              }}
            >
              <span className="sr-only">View slide {index + 1}</span>
            </button>
          </li>
        ))}
      </ul>

      <div className="overflow-hidden pt-2">
        <m.ul animate={{ x: `${trackOffset}%` }} transition={DEFAULT_SPRING_TRANSITION} className="flex">
          {slides.map((slide, index) => (
            <p key={index} className="flex-shrink-0 w-full text-body-sm">
              {slide.imageCaption}
            </p>
          ))}
        </m.ul>
      </div>
    </div>
  )
}

export default function FeatureSlider({ title, content, button, slides, reverse = false }: Props) {
  const { isDesktop } = useBreakpoint()
  const [page, setPage] = useState(0)
  const sectionRef = useRef<HTMLElement>(null)
  const isInView = useInView(sectionRef, {
    margin: '-50% 0px -50% 0px',
    once: false,
  })
  const [autoplaySlider, setAutoplaySlider] = useState(false)

  const trackOffset = -100 * page
  const maxPage = slides.length - 1
  const ContentWrapper = isDesktop ? 'div' : Container

  const handleUpdatePage = (newPage: number, triggerType: TriggerType) => {
    if (triggerType === 'manual') setAutoplaySlider(false)
    if (newPage < 0 || newPage > maxPage) return
    setPage(newPage)
  }

  const nextPage = (triggerType: TriggerType) => {
    const newPage = page + 1
    handleUpdatePage(newPage > maxPage ? 0 : newPage, triggerType)
  }

  useEffect(() => {
    setAutoplaySlider(isInView)
  }, [isInView])

  useInterval(
    () => {
      nextPage('auto')
    },
    autoplaySlider && isInView ? SLIDER_AUTOPLAY_NEXT_SLIDE_DELAY : null
  )

  useTimeout(
    () => {
      nextPage('auto')
      setAutoplaySlider(true)
    },
    autoplaySlider ? null : SLIDER_AUTOPLAY_PAUSE
  )

  return (
    <m.section
      ref={sectionRef}
      initial={isInView || !isDesktop ? 'hover' : 'hidden'}
      animate={isInView || !isDesktop ? 'hover' : 'hidden'}
      className="group relative overflow-hidden"
    >
      {/* Drawer */}
      <m.div
        custom={reverse}
        variants={drawerVariants}
        transition={DEFAULT_SPRING_TRANSITION}
        className={cx(
          'md:absolute md:top-0 md:h-full md:max-w-144 lg:max-w-192 md:px-20 lg:px-28 md:py-16 lg:py-24 pb-8 bg-white z-10',
          reverse ? 'md:right-0' : 'left-md:0'
        )}
      >
        <ContentWrapper>
          <Callout heading={title} content={`<p>${content}</p>`} button={button} />
        </ContentWrapper>

        {isDesktop && (
          <Controls
            slides={slides}
            page={page}
            updatePage={handleUpdatePage}
            trackOffset={trackOffset}
            className="pt-12 pl-6 border-l border-transparent"
          />
        )}
      </m.div>

      {/* Image slider */}
      <div className=" overflow-hidden max-md:ml-[var(--container-padding)]">
        <m.div animate={{ x: `${trackOffset}%` }} transition={DEFAULT_SPRING_TRANSITION} className="flex w-full">
          <m.ul
            className="flex w-full"
            drag="x"
            dragDirectionLock
            dragSnapToOrigin
            dragConstraints={{ left: 0, right: 0 }}
            dragElastic={DEFAULT_DRAG_ELASTIC}
            onPanEnd={(e, { offset, velocity }) => {
              const swipe = getSwipePower(offset.x, velocity.x)

              if (swipe < -SWIPE_CONFIDENCE_THRESHOLD) {
                handleUpdatePage(page + 1, 'manual')
              } else if (swipe > SWIPE_CONFIDENCE_THRESHOLD) {
                handleUpdatePage(page - 1, 'manual')
              }
            }}
          >
            {slides.map((slide, index) => (
              <li key={index} className="relative flex-shrink-0 w-full h-full aspect-4/3">
                <BlurImage
                  src={slide.imageSlide.imageUrl}
                  alt={slide.imageSlide.alt}
                  placeholderSrc={slide.imageSlide.placeholder}
                  fill
                  className="pointer-events-none object-cover"
                  sizes="100vw"
                  quality={95}
                />
              </li>
            ))}
          </m.ul>
        </m.div>
      </div>

      {!isDesktop && (
        <ContentWrapper>
          <Controls
            slides={slides}
            page={page}
            updatePage={handleUpdatePage}
            trackOffset={trackOffset}
            className="pt-4"
          />
        </ContentWrapper>
      )}
    </m.section>
  )
}
