'use client'

import { cx, VariantProps } from 'class-variance-authority'
import {
  useCallback,
  useEffect,
  useRef,
  useState,
  VideoHTMLAttributes,
} from 'react'

import { Lang, languages } from '@scandic-hotels/common/constants/language'
import { FocalPoint } from '@scandic-hotels/common/utils/imageVault'
import { useIntl } from 'react-intl'
import { VideoPlayerButton } from './Button'
import { variants } from './variants'
import styles from './videoPlayer.module.css'

interface Caption {
  src: string
  srcLang: Lang
  isDefault: boolean
}

export interface VideoPlayerProps extends VariantProps<typeof variants> {
  sources: {
    src: string
    type: string
  }[]
  className?: string
  captions?: Caption[]
  focalPoint?: FocalPoint
  autoPlay?: boolean
  hasOverlay?: boolean
}

export function VideoPlayer({
  sources,
  captions,
  focalPoint = { x: 50, y: 50 },
  className,
  variant = 'inline',
  autoPlay,
  hasOverlay,
}: VideoPlayerProps) {
  const intl = useIntl()
  const videoRef = useRef<HTMLVideoElement>(null)
  const [isInteractedWith, setIsInteractedWith] = useState(false)
  const [isPlaying, setIsPlaying] = useState(
    (variant === 'hero' && (autoPlay ?? true)) || !!autoPlay
  )
  const [isMuted, setIsMuted] = useState(true)
  const [userPaused, setUserPaused] = useState(false)
  const defaultProps = getVideoPropsByVariant(
    variant,
    isInteractedWith,
    autoPlay
  )
  const classNames = variants({
    className,
    variant,
  })
  const showPlayButton =
    variant === 'hero' || (variant === 'inline' && !isInteractedWith)
  const showMuteButton = variant === 'inline' && isInteractedWith

  const handleIntersection = useCallback(
    (entries: IntersectionObserverEntry[]) => {
      entries.forEach((entry) => {
        if (entry.intersectionRatio >= 0.1 && !userPaused) {
          videoRef.current?.play()
        } else if (entry.intersectionRatio < 0.1) {
          videoRef.current?.pause()
        }
      })
    },
    [userPaused]
  )

  function togglePlay() {
    const videoElement = videoRef.current
    if (videoElement) {
      if (variant === 'hero') {
        if (videoElement.paused) {
          setUserPaused(false)
          videoElement.play()
        } else {
          setUserPaused(true)
          videoElement.pause()
        }
      } else {
        setIsInteractedWith(true)
        videoElement.play()
      }
    }
  }

  function handleMuteToggle() {
    const videoElement = videoRef.current
    if (videoElement) {
      const currentlyMuted = videoElement.muted
      videoElement.muted = !currentlyMuted
      setIsMuted(!currentlyMuted)
    }
  }

  function handleVolumeChangeEvent(event: React.UIEvent<HTMLVideoElement>) {
    if (event.currentTarget.muted && !isMuted) {
      setIsMuted(true)
    } else if (!event.currentTarget.muted && isMuted) {
      setIsMuted(false)
    }
  }

  useEffect(() => {
    const videoElement = videoRef.current

    if (!videoElement || variant !== 'hero') {
      return
    }

    const observer = new IntersectionObserver(handleIntersection, {
      // Play video when at least 10% of it is visible
      threshold: [0, 0.1, 1],
    })

    observer.observe(videoElement)
    return () => {
      observer.disconnect()
    }
  }, [variant, handleIntersection])

  if (!sources.length) {
    return null
  }

  // Sort sources to prioritize WebM format for better compression
  const sortedSources = [...sources].sort((a, b) => {
    const aIsWebM = a.type.includes('webm')
    const bIsWebM = b.type.includes('webm')

    return aIsWebM === bIsWebM ? 0 : aIsWebM ? -1 : 1
  })

  return (
    <div className={cx(classNames, { [styles.hasOverlay]: hasOverlay })}>
      <video
        ref={videoRef}
        className={styles.video}
        style={
          focalPoint
            ? { objectPosition: `${focalPoint.x}% ${focalPoint.y}%` }
            : undefined
        }
        onPlay={() => setIsPlaying(true)}
        onPause={() => setIsPlaying(false)}
        onVolumeChange={handleVolumeChangeEvent}
        {...defaultProps}
      >
        {sortedSources.map(({ src, type }) => (
          <source key={src} src={src} type={type} />
        ))}
        {captions?.length
          ? captions.map(({ src, srcLang, isDefault }) => (
              <track
                key={src}
                src={src}
                kind="captions"
                srcLang={srcLang}
                label={languages[srcLang] || srcLang}
                default={isDefault}
              />
            ))
          : null}
      </video>
      {showPlayButton ? (
        <VideoPlayerButton
          className={styles.playButton}
          onPress={togglePlay}
          iconName={isPlaying ? 'pause' : 'play_arrow'}
          ariaLabel={
            isPlaying
              ? intl.formatMessage({
                  id: 'videoPlayer.pause',
                  defaultMessage: 'Pause video',
                })
              : intl.formatMessage({
                  id: 'videoPlayer.play',
                  defaultMessage: 'Play video',
                })
          }
        />
      ) : null}
      {showMuteButton ? (
        <VideoPlayerButton
          className={styles.muteButton}
          onPress={handleMuteToggle}
          iconName={isMuted ? 'volume_off' : 'volume_up'}
          ariaLabel={
            isMuted
              ? intl.formatMessage({
                  id: 'videoPlayer.mute',
                  defaultMessage: 'Mute video',
                })
              : intl.formatMessage({
                  id: 'videoPlayer.unmute',
                  defaultMessage: 'Unmute video',
                })
          }
        />
      ) : null}
    </div>
  )
}

function getVideoPropsByVariant(
  variant: VideoPlayerProps['variant'],
  isInteractedWith: boolean,
  autoPlay?: boolean
): VideoHTMLAttributes<HTMLVideoElement> {
  switch (variant) {
    case 'hero':
      return {
        controls: false,
        controlsList: 'nodownload nofullscreen noremoteplayback',
        autoPlay: autoPlay ?? true,
        muted: true,
        loop: true,
        playsInline: true,
      }
    case 'inline':
    default:
      return {
        controls: isInteractedWith,
        controlsList: 'nodownload noremoteplayback',
        autoPlay: autoPlay ?? isInteractedWith,
        muted: true,
        loop: false,
        playsInline: true,
      }
  }
}
