import React, { useEffect, useCallback, useState } from 'react'
import { usePlayerContext } from '../contexts/PlayerContext'
import type { SubtitleTrack, AudioTrack, VideoQuality } from '../types'
import { validateVideoURL, getCORSErrorMessage, isCORSError } from '../utils/corsHelper'
import { setHlsAudioTrack, setHlsQualityLevel } from '../utils/hlsControl'
import { setupHlsInstance } from '../utils/hlsSetup'
import { createSubtitleBlobURL } from '../utils/subtitles'
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
  onQualityLevelsLoaded?: (qualities: VideoQuality[]) => 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,
  onQualityLevelsLoaded,
}) => {
  const { videoRef, setVideoState, toggleFullscreen, settings, setQuality, setSubtitle } = usePlayerContext()
  const lastClickTimeRef = React.useRef<number>(0)
  const [availableAudioTracks, setAvailableAudioTracks] = useState<AudioTrack[]>([])
  const [availableQualities, setAvailableQualities] = useState<VideoQuality[]>([])
  const [processedSubtitles, setProcessedSubtitles] = useState<SubtitleTrack[]>([])
  const subtitleBlobUrlsRef = React.useRef<string[]>([])

  // 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,
    }))

    // Enable default subtitle if specified
    const tracks = video.textTracks
    if (tracks && processedSubtitles.length > 0) {
      const defaultSubtitle = processedSubtitles.find((sub) => sub.default)
      if (defaultSubtitle) {
        // Find the corresponding track and set it as showing
        for (let i = 0; i < tracks.length; i++) {
          const track = tracks[i]
          if (track.language === defaultSubtitle.lang) {
            track.mode = 'showing'
            setSubtitle(defaultSubtitle)
            break
          }
        }
      }
    }

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

  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])

  // Process subtitles - convert SRT to VTT blob URLs
  useEffect(() => {
    // Clean up old blob URLs
    subtitleBlobUrlsRef.current.forEach((url) => URL.revokeObjectURL(url))
    subtitleBlobUrlsRef.current = []

    if (subtitles.length === 0) {
      setProcessedSubtitles([])
      return
    }

    const processSubtitles = async () => {
      const processed = await Promise.all(
        subtitles.map(async (subtitle) => {
          try {
            // Check if it's an SRT file
            if (subtitle.src.endsWith('.srt')) {
              // Fetch and convert SRT to VTT
              const response = await fetch(subtitle.src)
              if (!response.ok) {
                throw new Error(`Failed to fetch subtitle: ${response.status} ${response.statusText}`)
              }
              const srtContent = await response.text()
              const blobUrl = createSubtitleBlobURL(srtContent, 'srt')
              subtitleBlobUrlsRef.current.push(blobUrl)
              console.log(`Processed SRT subtitle "${subtitle.label}": ${subtitle.src} -> ${blobUrl}`)
              return { ...subtitle, src: blobUrl }
            }
            // VTT files can be used directly
            console.log(`Using VTT subtitle "${subtitle.label}": ${subtitle.src}`)
            return subtitle
          } catch (error) {
            console.error(`Failed to process subtitle ${subtitle.label}:`, error)
            return subtitle
          }
        })
      )
      setProcessedSubtitles(processed)
    }

    processSubtitles()

    // Cleanup function
    return () => {
      subtitleBlobUrlsRef.current.forEach((url) => URL.revokeObjectURL(url))
      subtitleBlobUrlsRef.current = []
    }
  }, [subtitles])

  useEffect(() => {
    const video = videoRef.current
    if (!video) return
    if (processedSubtitles.length === 0) return
    if (settings.subtitle) return

    const defaultSubtitle = processedSubtitles.find((subtitle) => subtitle.default)
    if (!defaultSubtitle) return

    const tracks = video.textTracks
    if (!tracks || tracks.length === 0) return

    for (let i = 0; i < tracks.length; i++) {
      const track = tracks[i]
      if (track.language === defaultSubtitle.lang) {
        track.mode = 'showing'
        setSubtitle(defaultSubtitle)
        break
      }
    }
  }, [processedSubtitles, settings.subtitle, setSubtitle, videoRef])

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

    setAvailableAudioTracks([])
    onAudioTracksLoaded?.([])
    setAvailableQualities([])
    onQualityLevelsLoaded?.([])

    // 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') === '') {
        try {
          cleanupFn = await setupHlsInstance({
            video,
            src,
            autoplay,
            onAudioTracksLoaded: (tracks) => {
              setAvailableAudioTracks(tracks)
              onAudioTracksLoaded?.(tracks)
            },
            onQualityLevelsLoaded: (qualities) => {
              setAvailableQualities(qualities)
              onQualityLevelsLoaded?.(qualities)
            },
            onError: handleError,
          })
        } 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 {
        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,
    onQualityLevelsLoaded,
  ])

  // 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])

  // Reset selected quality if it no longer exists
  useEffect(() => {
    if (!settings.quality) return
    if (availableQualities.length === 0) return

    const hasQuality = availableQualities.some((quality) => {
      if (
        typeof settings.quality?.levelIndex === 'number' &&
        typeof quality.levelIndex === 'number' &&
        quality.levelIndex === settings.quality.levelIndex
      ) {
        return true
      }

      if (
        typeof settings.quality?.height === 'number' &&
        typeof quality.height === 'number' &&
        quality.height === settings.quality.height
      ) {
        return true
      }

      return quality.label === settings.quality?.label
    })

    if (!hasQuality) {
      setQuality(null)
    }
  }, [availableQualities, settings.quality, setQuality])

  // Apply selected quality to HLS instance
  useEffect(() => {
    const video = videoRef.current
    if (!video) return

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

    if (!settings.quality) {
      setHlsQualityLevel(hlsInstance, null)
      return
    }

    let targetLevelIndex =
      typeof settings.quality.levelIndex === 'number' ? settings.quality.levelIndex : undefined

    if (typeof targetLevelIndex !== 'number') {
      const matchingQuality = availableQualities.find((quality) => {
        if (
          typeof settings.quality?.levelIndex === 'number' &&
          typeof quality.levelIndex === 'number'
        ) {
          return quality.levelIndex === settings.quality.levelIndex
        }

        if (
          typeof settings.quality?.height === 'number' &&
          typeof quality.height === 'number'
        ) {
          return quality.height === settings.quality.height
        }

        return quality.label === settings.quality?.label
      })

      if (matchingQuality && typeof matchingQuality.levelIndex === 'number') {
        targetLevelIndex = matchingQuality.levelIndex
      }
    }

    setHlsQualityLevel(hlsInstance, targetLevelIndex)
  }, [settings.quality, availableQualities, videoRef])

  // Handle subtitle track changes
  useEffect(() => {
    const video = videoRef.current
    if (!video) return

    const tracks = video.textTracks
    if (!tracks || tracks.length === 0) return

    // Disable all tracks first
    for (let i = 0; i < tracks.length; i++) {
      tracks[i].mode = 'hidden'
    }

    // Enable the selected subtitle track
    if (settings.subtitle) {
      for (let i = 0; i < tracks.length; i++) {
        const track = tracks[i]
        if (track.language === settings.subtitle.lang) {
          track.mode = 'showing'
          console.log(`Enabled subtitle track: ${track.label} (${track.language})`)
          break
        }
      }
    }
  }, [settings.subtitle, videoRef])

  // Debug: Monitor text track loading
  useEffect(() => {
    const video = videoRef.current
    if (!video) return

    const handleTrackLoad = (e: Event) => {
      const track = e.target as HTMLTrackElement
      console.log(`Track loaded: ${track.label} (${track.srclang})`, track.readyState)
    }

    const handleTrackError = (e: Event) => {
      const track = e.target as HTMLTrackElement
      console.error(`Track error: ${track.label} (${track.srclang})`, track.track.cues?.length)
    }

    const trackElements = video.querySelectorAll('track')
    trackElements.forEach((track) => {
      track.addEventListener('load', handleTrackLoad)
      track.addEventListener('error', handleTrackError)
    })

    // Also monitor text tracks
    const textTracks = video.textTracks
    const handleCueChange = () => {
      for (let i = 0; i < textTracks.length; i++) {
        const track = textTracks[i]
        if (track.mode === 'showing') {
          console.log(`Active track: ${track.label}, cues: ${track.cues?.length || 0}, active cues: ${track.activeCues?.length || 0}`)
        }
      }
    }

    for (let i = 0; i < textTracks.length; i++) {
      textTracks[i].addEventListener('cuechange', handleCueChange)
    }

    return () => {
      trackElements.forEach((track) => {
        track.removeEventListener('load', handleTrackLoad)
        track.removeEventListener('error', handleTrackError)
      })
      for (let i = 0; i < textTracks.length; i++) {
        textTracks[i].removeEventListener('cuechange', handleCueChange)
      }
    }
  }, [videoRef, processedSubtitles])

  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}
      >
        {processedSubtitles.map((subtitle, index) => (
          <track
            key={index}
            kind="subtitles"
            src={subtitle.src}
            srcLang={subtitle.lang}
            label={subtitle.label}
            default={subtitle.default}
          />
        ))}
      </video>
    </div>
  )
}
