import { isHlsPlaylistUrl, isImage } from '@/lib/url'
import { cn } from '@/lib/utils'
import { useContentPolicy } from '@/providers/ContentPolicyProvider'
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import AudioPlayer from '../AudioPlayer'
import VideoPlayer from '../VideoPlayer'
import ExternalLink from '../ExternalLink'
import LazyMediaTapPlaceholder, { MediaEmbedBlurFrame } from './LazyMediaTapPlaceholder'

/** Same rules as the metadata probe, but synchronous so the first paint can show the embed stack. */
function embedMediaTypeHintFromUrl(src: string): 'video' | 'audio' | null {
  try {
    const url = new URL(src)
    const extension = url.pathname.split('.').pop()?.toLowerCase()
    if (
      extension &&
      ['mp3', 'wav', 'flac', 'aac', 'm4a', 'opus', 'wma', 'mka', 'oga'].includes(extension)
    ) {
      return 'audio'
    }
    if (extension === 'mkv' || extension === 'ogv') {
      return 'video'
    }
    if (
      extension &&
      ['mp4', 'webm', 'm4v', 'mov', 'avi', '3gp', '3g2', 'm3u8', 'm3u'].includes(extension)
    ) {
      return 'video'
    }
    return null
  } catch {
    return null
  }
}

export default function MediaPlayer({
  src,
  className,
  mustLoad = false,
  poster,
  blurHash,
  fallbackPageUrl
}: {
  src: string
  className?: string
  mustLoad?: boolean
  poster?: string
  /** NIP-94 / imeta blurhash for lazy placeholder when poster is missing */
  blurHash?: string
  /** Passed to {@link VideoPlayer} when HLS/video playback fails (e.g. NIP-53 zap.stream join URL). */
  fallbackPageUrl?: string
}) {
  const { t } = useTranslation()
  const { autoLoadMedia } = useContentPolicy()
  /** Tap-to-load when {@link autoLoadMedia} is off; cleared when policy switches back to never. */
  const [userClickedLoad, setUserClickedLoad] = useState(false)
  const [mediaType, setMediaType] = useState<'video' | 'audio' | null>(null)
  const [probeFailed, setProbeFailed] = useState(false)
  const [embedPainted, setEmbedPainted] = useState(false)
  const readyOnceRef = useRef(false)

  // imeta `thumb` / `image` are sometimes the same .mp4 as `url` — <img> cannot use that, and it
  // would hide the blurhash placeholder in LazyMediaTapPlaceholder.
  const imagePoster = useMemo(() => {
    const p = poster?.trim()
    if (!p) return undefined
    return isImage(p) ? p : undefined
  }, [poster])

  const urlEmbedTypeHint = useMemo(() => embedMediaTypeHintFromUrl(src), [src])
  /** Probe result wins when set (e.g. audio-only mp4); URL hint avoids a blank frame before useEffect runs. */
  const effectiveMediaType = mediaType ?? urlEmbedTypeHint

  const showEmbed = mustLoad || autoLoadMedia || userClickedLoad

  useLayoutEffect(() => {
    if (!autoLoadMedia) setUserClickedLoad(false)
  }, [autoLoadMedia])

  useEffect(() => {
    readyOnceRef.current = false
    setEmbedPainted(false)
    setMediaType(null)
    setProbeFailed(false)
  }, [src])

  useEffect(() => {
    if (!showEmbed) {
      setMediaType(null)
      setProbeFailed(false)
      return
    }
    readyOnceRef.current = false
    setEmbedPainted(false)
    if (!src) {
      setProbeFailed(true)
      return
    }

    setProbeFailed(false)
    setMediaType(null)

    let cancelled = false

    try {
      // Firefox/Chrome do not expose HLS via <video> metadata probe — it fails and looked like “no player”.
      if (isHlsPlaylistUrl(src)) {
        setMediaType('video')
        return
      }

      const url = new URL(src)
      const extension = url.pathname.split('.').pop()?.toLowerCase()

      if (
        extension &&
        ['mp3', 'wav', 'flac', 'aac', 'm4a', 'opus', 'wma', 'mka'].includes(extension)
      ) {
        setMediaType('audio')
        return
      }

      if (extension === 'mkv' || extension === 'ogv') {
        setMediaType('video')
        return
      }

      const video = document.createElement('video')
      video.src = src
      video.preload = 'metadata'

      video.onloadedmetadata = () => {
        if (cancelled) return
        setMediaType(video.videoWidth > 0 || video.videoHeight > 0 ? 'video' : 'audio')
      }

      video.onerror = () => {
        if (cancelled) return
        setProbeFailed(true)
        setMediaType(null)
      }

      return () => {
        cancelled = true
        video.src = ''
      }
    } catch {
      setProbeFailed(true)
    }
  }, [src, showEmbed])

  const onEmbedReady = useCallback(() => {
    if (readyOnceRef.current) return
    readyOnceRef.current = true
    setEmbedPainted(true)
  }, [])

  const blurLoadingHint = useMemo(() => {
    if (!showEmbed) return undefined
    if (effectiveMediaType === null) {
      return t('Preparing player…', { defaultValue: 'Preparing player…' })
    }
    if (!embedPainted) {
      if (isHlsPlaylistUrl(src)) {
        return t('Starting stream…', { defaultValue: 'Starting stream…' })
      }
      return t('Loading media…', { defaultValue: 'Loading media…' })
    }
    return undefined
  }, [showEmbed, effectiveMediaType, embedPainted, src, t])

  if (!mustLoad && !showEmbed) {
    return (
      <LazyMediaTapPlaceholder
        src={src}
        posterUrl={imagePoster}
        blurHash={blurHash}
        onActivate={() => setUserClickedLoad(true)}
        className={className}
      />
    )
  }

  if (probeFailed) {
    return <ExternalLink url={src} />
  }

  if (effectiveMediaType === null) {
    return (
      <MediaEmbedBlurFrame
        src={src}
        posterUrl={imagePoster}
        blurHash={blurHash}
        className={className}
        loadingHint={blurLoadingHint}
      />
    )
  }

  const layerTransition =
    'transition-opacity duration-300 ease-out motion-reduce:transition-none'

  return (
    <div className="not-prose relative w-full max-w-[400px] shrink-0 self-start">
      {!embedPainted ? (
        <div className="relative z-10 w-full">
          <MediaEmbedBlurFrame
            src={src}
            posterUrl={imagePoster}
            blurHash={blurHash}
            className={className}
            loadingHint={blurLoadingHint}
          />
        </div>
      ) : null}
      <div
        className={cn(
          layerTransition,
          embedPainted
            ? 'relative z-20 w-full overflow-hidden opacity-100'
            : 'absolute inset-0 z-0 w-full overflow-hidden opacity-0 pointer-events-none'
        )}
        aria-hidden={!embedPainted}
      >
        {effectiveMediaType === 'video' ? (
          <VideoPlayer
            src={src}
            className={className}
            poster={imagePoster}
            onReady={onEmbedReady}
            fallbackPageUrl={fallbackPageUrl}
          />
        ) : (
          <AudioPlayer
            src={src}
            className={className}
            poster={imagePoster}
            onReady={onEmbedReady}
          />
        )}
      </div>
    </div>
  )
}
