import React, { useEffect, useCallback, useState } from 'react'
import { usePlayerContext } from '../contexts/PlayerContext'
import type { SubtitleTrack, AudioTrack } from '../types'
import { validateVideoURL, getCORSErrorMessage, isCORSError } from '../utils/corsHelper'
import { getHlsAudioTracks, setHlsAudioTrack } from '../utils/hlsLoader'
import './VideoElement.css'

interface VideoElementProps {
  src: string
  poster?: string
  autoplay?: boolean
  loop?: boolean
  muted?: boolean
  subtitles?: SubtitleTrack[]
  onPlay?: () => void
  onPause?: () => void
  onEnded?: () => void
  onTimeUpdate?: (currentTime: number) => void
  onVolumeChange?: (volume: number) => void
  onError?: (error: Error) => void
  onLoadedMetadata?: () => void
  onSeeking?: () => void
  onSeeked?: () => void
  onAudioTracksLoaded?: (tracks: AudioTrack[]) => void
}

export const VideoElement: React.FC<VideoElementProps> = ({
  src,
  poster,
  autoplay = false,
  loop = false,
  muted = false,
  subtitles = [],
  onPlay,
  onPause,
  onEnded,
  onTimeUpdate,
  onVolumeChange,
  onError,
  onLoadedMetadata,
  onSeeking,
  onSeeked,
  onAudioTracksLoaded,
}) => {
  const { videoRef, setVideoState, toggleFullscreen, settings } = usePlayerContext()
  const lastClickTimeRef = React.useRef<number>(0)
  const [availableAudioTracks, setAvailableAudioTracks] = useState<AudioTrack[]>([])

  // Handle video events
  const handlePlay = useCallback(() => {
    setVideoState((prev) => ({ ...prev, playing: true }))
    onPlay?.()
  }, [setVideoState, onPlay])

  const handlePause = useCallback(() => {
    setVideoState((prev) => ({ ...prev, playing: false }))
    onPause?.()
  }, [setVideoState, onPause])

  const handleTimeUpdate = useCallback(() => {
    const video = videoRef.current
    if (!video) return

    const currentTime = video.currentTime
    const buffered = video.buffered.length > 0 ? video.buffered.end(video.buffered.length - 1) : 0

    setVideoState((prev) => ({
      ...prev,
      currentTime,
      buffered,
    }))

    onTimeUpdate?.(currentTime)
  }, [videoRef, setVideoState, onTimeUpdate])

  const handleLoadedMetadata = useCallback(() => {
    const video = videoRef.current
    if (!video) return

    setVideoState((prev) => ({
      ...prev,
      duration: video.duration,
      volume: video.volume,
      muted: video.muted,
    }))

    onLoadedMetadata?.()
  }, [videoRef, setVideoState, onLoadedMetadata])

  const handleVolumeChange = useCallback(() => {
    const video = videoRef.current
    if (!video) return

    setVideoState((prev) => ({
      ...prev,
      volume: video.volume,
      muted: video.muted,
    }))

    onVolumeChange?.(video.volume)
  }, [videoRef, setVideoState, onVolumeChange])

  const handleSeeking = useCallback(() => {
    setVideoState((prev) => ({ ...prev, seeking: true }))
    onSeeking?.()
  }, [setVideoState, onSeeking])

  const handleSeeked = useCallback(() => {
    setVideoState((prev) => ({ ...prev, seeking: false }))
    onSeeked?.()
  }, [setVideoState, onSeeked])

  const handleWaiting = useCallback(() => {
    setVideoState((prev) => ({ ...prev, loading: true }))
  }, [setVideoState])

  const handleCanPlay = useCallback(() => {
    setVideoState((prev) => ({ ...prev, loading: false }))
  }, [setVideoState])

  const handleEnded = useCallback(() => {
    setVideoState((prev) => ({ ...prev, playing: false }))
    onEnded?.()
  }, [setVideoState, onEnded])

  const handleError = useCallback(() => {
    const video = videoRef.current
    if (!video || !video.error) return

    let errorMessage = `Video error: ${video.error.message}`

    // Check if it's a CORS-related error
    const videoError = video.error
    if (
      videoError.code === MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED ||
      videoError.code === MediaError.MEDIA_ERR_NETWORK
    ) {
      // Could be a CORS issue
      errorMessage = getCORSErrorMessage(video.src || src)
    }

    const error = new Error(errorMessage)
    setVideoState((prev) => ({ ...prev, error, loading: false }))
    onError?.(error)
  }, [videoRef, setVideoState, onError, src])

  // Handle double-click on video for fullscreen toggle
  const handleVideoClick = useCallback(
    (e: React.MouseEvent<HTMLVideoElement>) => {
      const now = Date.now()
      const timeSinceLastClick = now - lastClickTimeRef.current

      if (timeSinceLastClick < 300) {
        // Double click - toggle fullscreen
        e.preventDefault()
        toggleFullscreen()
        lastClickTimeRef.current = 0
      } else {
        // Single click - record time
        lastClickTimeRef.current = now
      }
    },
    [toggleFullscreen]
  )

  // Handle fullscreen changes
  useEffect(() => {
    const handleFullscreenChange = () => {
      const isFullscreen = !!document.fullscreenElement
      setVideoState((prev) => ({ ...prev, fullscreen: isFullscreen }))
    }

    document.addEventListener('fullscreenchange', handleFullscreenChange)
    return () => {
      document.removeEventListener('fullscreenchange', handleFullscreenChange)
    }
  }, [setVideoState])

  // Handle PIP changes
  useEffect(() => {
    const handlePIPChange = () => {
      const isPIP = !!document.pictureInPictureElement
      setVideoState((prev) => ({ ...prev, pictureInPicture: isPIP }))
    }

    document.addEventListener('enterpictureinpicture', handlePIPChange)
    document.addEventListener('leavepictureinpicture', handlePIPChange)

    return () => {
      document.removeEventListener('enterpictureinpicture', handlePIPChange)
      document.removeEventListener('leavepictureinpicture', handlePIPChange)
    }
  }, [setVideoState])

  // Detect HLS source and load hls.js if needed
  useEffect(() => {
    const video = videoRef.current
    if (!video) return

    // Validate video URL first
    const validation = validateVideoURL(src)
    if (!validation.valid) {
      const error = new Error(validation.error || 'Invalid video URL')
      setVideoState((prev) => ({ ...prev, error, loading: false }))
      onError?.(error)
      return
    }

    const isHLS = src.includes('.m3u8')
    let cleanupFn: (() => void) | null = null

    const setupHls = async () => {
      if (isHLS && video.canPlayType('application/vnd.apple.mpegurl') === '') {
        // Browser doesn't support HLS natively, load hls.js
        try {
          // Dynamic import with CDN fallback
          const { loadHls, isHlsSupported } = await import('../utils/hlsLoader')
          const Hls = await loadHls()

          if (!isHlsSupported(Hls)) {
            throw new Error('HLS.js is not supported in this browser')
          }

          const hls = new Hls({
            enableWorker: true,
            lowLatencyMode: false,
          })

          hls.loadSource(src)
          hls.attachMedia(video)

          hls.on(Hls.Events.MANIFEST_PARSED, () => {
            // Extract audio tracks after manifest is parsed
            // Sometimes audio tracks are not immediately available, so we try with a small delay
            setTimeout(() => {
              const tracks = getHlsAudioTracks(hls)

              if (tracks.length > 0) {
                setAvailableAudioTracks(tracks)
                onAudioTracksLoaded?.(tracks)
              }
            }, 100)

            if (autoplay) {
              void video.play().catch(() => undefined)
            }
          })

          // Also listen to audio track updates
          hls.on(Hls.Events.AUDIO_TRACKS_UPDATED, () => {
            const tracks = getHlsAudioTracks(hls)
            if (tracks.length > 0) {
              setAvailableAudioTracks(tracks)
              onAudioTracksLoaded?.(tracks)
            }
          })

          hls.on(Hls.Events.ERROR, (_event: any, data: any) => {
            if (data.fatal) {
              switch (data.type) {
                case Hls.ErrorTypes.NETWORK_ERROR:
                  hls.startLoad()
                  break
                case Hls.ErrorTypes.MEDIA_ERROR:
                  hls.recoverMediaError()
                  break
                default:
                  handleError()
                  break
              }
            }
          })

          // Store hls instance for cleanup
          ;(video as any).__hlsInstance = hls

          // Setup cleanup function
          cleanupFn = () => {
            if (hls) {
              hls.destroy()
            }
            delete (video as any).__hlsInstance
          }
        } catch (err) {
          let error: Error
          if (err instanceof Error && isCORSError(err)) {
            const corsMessage = getCORSErrorMessage(src)
            error = new Error(corsMessage)
          } else {
            error = err instanceof Error ? err : new Error('Failed to load HLS')
          }
          setVideoState((prev) => ({
            ...prev,
            error,
            loading: false,
          }))
          onError?.(error)
        }
      } else {
        // Native support or regular video
        video.src = src
        if (autoplay) {
          void video.play().catch(() => undefined)
        }
      }
    }

    setupHls()

    // Cleanup function
    return () => {
      if (cleanupFn) {
        cleanupFn()
      }
      // Also check for any lingering HLS instance
      if ((video as any).__hlsInstance) {
        const hls = (video as any).__hlsInstance
        if (hls && typeof hls.destroy === 'function') {
          hls.destroy()
        }
        delete (video as any).__hlsInstance
      }
    }
  }, [src, autoplay, videoRef, handleError, setVideoState, onError, onAudioTracksLoaded])

  // Handle audio track changes
  useEffect(() => {
    const video = videoRef.current
    if (!video || !settings.audioTrack) return

    const hlsInstance = (video as any).__hlsInstance
    if (!hlsInstance) return

    // Find the index of the selected audio track
    const trackIndex = availableAudioTracks.findIndex(
      (track) => track.language === settings.audioTrack?.language
    )

    if (trackIndex !== -1) {
      setHlsAudioTrack(hlsInstance, trackIndex)
    }
  }, [settings.audioTrack, availableAudioTracks, videoRef])

  return (
    <div className="video-container">
      <video
        ref={videoRef}
        className="video-element"
        poster={poster}
        loop={loop}
        muted={muted}
        playsInline
        preload="metadata"
        onPlay={handlePlay}
        onPause={handlePause}
        onTimeUpdate={handleTimeUpdate}
        onLoadedMetadata={handleLoadedMetadata}
        onVolumeChange={handleVolumeChange}
        onSeeking={handleSeeking}
        onSeeked={handleSeeked}
        onWaiting={handleWaiting}
        onCanPlay={handleCanPlay}
        onEnded={handleEnded}
        onError={handleError}
        onClick={handleVideoClick}
      >
        {subtitles.map((subtitle, index) => (
          <track
            key={index}
            kind="subtitles"
            src={subtitle.src}
            srcLang={subtitle.lang}
            label={subtitle.label}
            default={subtitle.default}
          />
        ))}
      </video>
    </div>
  )
}
