'use client'

import { useCallback, useEffect, useMemo, useRef, useState, type MouseEvent as ReactMouseEvent } from 'react'
import Hls from 'hls.js'
import { FaPause, FaPlay } from 'react-icons/fa'
import { FiVolume2, FiVolumeX } from 'react-icons/fi'
import type { CatchVideo } from './types'

function isHlsSource(src: string): boolean {
    const clean = src.split('#')[0]?.split('?')[0]?.toLowerCase() ?? ''
    return clean.endsWith('.m3u8')
}

function cx(...classes: Array<string | false | null | undefined>) {
    return classes.filter(Boolean).join(' ')
}

function clamp(n: number, min: number, max: number) {
    return Math.min(max, Math.max(min, n))
}

function formatTime(sec: number) {
    if (!Number.isFinite(sec) || sec < 0) return '00:00'
    const s = Math.floor(sec)
    const h = Math.floor(s / 3600)
    const m = Math.floor((s % 3600) / 60)
    const ss = s % 60
    if (h > 0) return `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}:${String(ss).padStart(2, '0')}`
    return `${String(m).padStart(2, '0')}:${String(ss).padStart(2, '0')}`
}

type Props = {
    video: CatchVideo
    src?: string
    isCurrent: boolean
    className?: string
    isPlaying?: boolean
    volume?: number
    isMuted?: boolean
    onReady?: (player: HTMLVideoElement) => void
    onPlayStateChange?: (isPlaying: boolean) => void
    onVolumeChange?: (volume: number) => void
    onMuteChange?: (isMuted: boolean) => void
    onControlsVisibilityChange?: (visible: boolean) => void
    forceControlsVisible?: boolean
}

export default function CatchVideoPlayer({
    video,
    src,
    isCurrent,
    className,
    isPlaying: externalIsPlaying,
    volume: externalVolume,
    isMuted: externalIsMuted,
    onReady,
    onPlayStateChange,
    onVolumeChange,
    onMuteChange,
    onControlsVisibilityChange,
    forceControlsVisible,
}: Props) {
    const containerRef = useRef<HTMLDivElement | null>(null)
    const mountRef = useRef<HTMLDivElement | null>(null)
    const videoElRef = useRef<HTMLVideoElement | null>(null)
    const hlsRef = useRef<Hls | null>(null)
    const [ready, setReady] = useState(false)
    const [isPlaying, setIsPlaying] = useState(false)
    const isPlayingRef = useRef(false)
    const [duration, setDuration] = useState<number>(0)
    const [currentTime, setCurrentTime] = useState<number>(0)
    const [volume, setVolume] = useState<number>(externalVolume ?? 1)
    const [muted, setMuted] = useState<boolean>(externalIsMuted ?? false)
    const pointerInsideRef = useRef(false)
    const hideControlsTimerRef = useRef<number | null>(null)
    const [controlsVisible, setControlsVisible] = useState(true)
    const [seekingPercent, setSeekingPercent] = useState<number | null>(null)
    const progressTrackRef = useRef<HTMLDivElement | null>(null)
    const draggingRef = useRef(false)
    const volTrackRef = useRef<HTMLDivElement | null>(null)
    const volDraggingRef = useRef(false)
    const [volBarActive, setVolBarActive] = useState(false)
    const ignoreClickRef = useRef(false)
    const userPausedRef = useRef(false)
    const currentSrcRef = useRef<string | undefined>(undefined)
    const [centerToastVisible, setCenterToastVisible] = useState(false)
    const [centerToastIcon, setCenterToastIcon] = useState<'play' | 'pause'>('play')
    const centerToastTimerRef = useRef<number | null>(null)
    const userMuteActionRef = useRef(false)
    const lastSyncedMutedRef = useRef<boolean | undefined>(undefined)
    const lastSyncedVolumeRef = useRef<number | undefined>(undefined)

    const onReadyRef = useRef<Props['onReady']>(onReady)
    useEffect(() => {
        onReadyRef.current = onReady
    }, [onReady])

    const onPlayStateChangeRef = useRef<Props['onPlayStateChange']>(onPlayStateChange)
    useEffect(() => {
        onPlayStateChangeRef.current = onPlayStateChange
    }, [onPlayStateChange])

    const onVolumeChangeRef = useRef<Props['onVolumeChange']>(onVolumeChange)
    useEffect(() => {
        onVolumeChangeRef.current = onVolumeChange
    }, [onVolumeChange])

    const onMuteChangeRef = useRef<Props['onMuteChange']>(onMuteChange)
    useEffect(() => {
        onMuteChangeRef.current = onMuteChange
    }, [onMuteChange])

    const onControlsVisibilityChangeRef = useRef<Props['onControlsVisibilityChange']>(onControlsVisibilityChange)
    useEffect(() => {
        onControlsVisibilityChangeRef.current = onControlsVisibilityChange
    }, [onControlsVisibilityChange])

    const forceControlsVisibleRef = useRef<Props['forceControlsVisible']>(forceControlsVisible)
    useEffect(() => {
        forceControlsVisibleRef.current = forceControlsVisible
    }, [forceControlsVisible])

    const getSeekableRange = useCallback(() => {
        const videoEl = videoElRef.current
        if (!videoEl) return null

        const playerDuration = videoEl.duration
        if (typeof playerDuration === 'number' && Number.isFinite(playerDuration) && playerDuration > 0) {
            return { start: 0, end: playerDuration }
        }

        if (Number.isFinite(duration) && duration > 0) return { start: 0, end: duration }
        return null
    }, [duration])

    const progressValue = useMemo(() => {
        if (seekingPercent !== null) return clamp(seekingPercent, 0, 100)

        const range = getSeekableRange()
        if (!range) return 0

        const span = range.end - range.start
        if (!Number.isFinite(span) || span <= 0) return 0

        return clamp(((currentTime - range.start) / span) * 100, 0, 100)
    }, [currentTime, getSeekableRange, seekingPercent])

    const volPct = useMemo(() => clamp((muted ? 0 : volume) * 100, 0, 100), [muted, volume])

    const scheduleHideControls = useCallback(() => {
        if (hideControlsTimerRef.current) {
            window.clearTimeout(hideControlsTimerRef.current)
            hideControlsTimerRef.current = null
        }
        if (pointerInsideRef.current) return
        if (forceControlsVisibleRef.current) return
        if (!isPlayingRef.current) return
        hideControlsTimerRef.current = window.setTimeout(() => {
            setControlsVisible(false)
            onControlsVisibilityChangeRef.current?.(false)
        }, 2500)
    }, [])

    const showControls = useCallback(() => {
        setControlsVisible(true)
        onControlsVisibilityChangeRef.current?.(true)
        if (!pointerInsideRef.current) {
            scheduleHideControls()
        }
    }, [scheduleHideControls])

    useEffect(() => {
        if (!mountRef.current) return
        if (videoElRef.current) return

        const mountEl = mountRef.current

        const el = document.createElement('video')
        el.setAttribute('tabindex', '-1')
        el.classList.add('focus:outline-none')
        el.style.outline = 'none'
        el.setAttribute('playsinline', 'true')
        el.style.width = '100%'
        el.style.height = '100%'
        el.style.objectFit = 'contain'
        try {
            el.muted = true
            el.setAttribute('muted', '')
        } catch {
            // ignore
        }
        mountEl.appendChild(el)
        videoElRef.current = el

        let hls: Hls | null = null
        if (src && isHlsSource(src)) {
            if (Hls.isSupported()) {
                hls = new Hls({
                    enableWorker: true,
                    lowLatencyMode: false,
                    backBufferLength: 0,
                })
                hlsRef.current = hls
                hls.loadSource(src)
                hls.attachMedia(el)
            } else if (el.canPlayType('application/vnd.apple.mpegurl')) {
                el.src = src
            }
        } else if (src) {
            el.src = src
            if (isCurrent) {
                el.play().catch(() => {
                })
            }
        }

        onReadyRef.current?.(el)
        setReady(true)

        const onPlay = () => {
            isPlayingRef.current = true
            setIsPlaying(true)
            userPausedRef.current = false
            onPlayStateChangeRef.current?.(true)
        }
        const onPause = () => {
            isPlayingRef.current = false
            setIsPlaying(false)
            if (!userPausedRef.current) {
                const videoEl = videoElRef.current
                if (videoEl && videoEl.paused) {
                }
            }
            onPlayStateChangeRef.current?.(false)
        }
        const onVolume = () => {
            try {
                const v = el.volume
                const m = el.muted
                if (typeof v === 'number') {
                    setVolume(v)
                    if (!userMuteActionRef.current && v !== lastSyncedVolumeRef.current) {
                        lastSyncedVolumeRef.current = v
                        onVolumeChangeRef.current?.(v)
                    }
                }
                if (typeof m === 'boolean') {
                    setMuted(m)
                    if (!userMuteActionRef.current && m !== lastSyncedMutedRef.current) {
                        lastSyncedMutedRef.current = m
                        onMuteChangeRef.current?.(m)
                    }
                }
            } catch {
                // ignore
            }
        }
        const onTime = () => {
            try {
                const t = el.currentTime
                if (typeof t === 'number') setCurrentTime(t)
            } catch {
                // ignore
            }
        }
        const onDuration = () => {
            try {
                const d = el.duration
                if (typeof d === 'number') {
                    setDuration(d)
                    if (!isPlayingRef.current && el.paused) {
                        generateThumbnail()
                    }
                }
            } catch {
                // ignore
            }
        }
        const generateThumbnail = () => {
            if (!el.src || el.readyState < 2) return
            try {
                const duration = el.duration
                if (duration && duration > 0) {
                    const previewTime = Math.min(1, duration * 0.01)
                    el.currentTime = previewTime
                    el.pause()
                }
            } catch {
                // ignore
            }
        }
        const onLoadedMetadata = () => {
            try {
                const d = el.duration
                if (typeof d === 'number') {
                    setDuration(d)
                    if (!isPlayingRef.current && el.paused) {
                        generateThumbnail()
                    }
                }
            } catch {
                // ignore
            }
        }

        el.addEventListener('play', onPlay)
        el.addEventListener('pause', onPause)
        el.addEventListener('timeupdate', onTime)
        el.addEventListener('durationchange', onDuration)
        el.addEventListener('loadedmetadata', onLoadedMetadata)
        el.addEventListener('volumechange', onVolume)

        return () => {
            if (hlsRef.current) {
                try {
                    hlsRef.current.destroy()
                } catch {
                    // ignore
                }
                hlsRef.current = null
            }
            try {
                el.removeEventListener('play', onPlay)
                el.removeEventListener('pause', onPause)
                el.removeEventListener('timeupdate', onTime)
                el.removeEventListener('durationchange', onDuration)
                el.removeEventListener('loadedmetadata', onLoadedMetadata)
                el.removeEventListener('volumechange', onVolume)
            } catch {
                // ignore
            }
            try {
                if (el && mountEl?.contains(el)) {
                    mountEl.removeChild(el)
                }
            } catch {
                // ignore
            }
            videoElRef.current = null
        }
    }, [src, isCurrent])

    useEffect(() => {
        const videoEl = videoElRef.current
        const hls = hlsRef.current
        if (!videoEl) return

        if (currentSrcRef.current === src) {
            return
        }

        if (!src) {
            currentSrcRef.current = undefined
            if (hls) {
                try {
                    hls.destroy()
                } catch {
                    // ignore
                }
                hlsRef.current = null
            }
            try {
                videoEl.src = ''
                videoEl.load()
            } catch {
                // ignore
            }
            return
        }

        const savedCurrentTime = videoEl.currentTime || 0

        if (hls) {
            try {
                hls.destroy()
            } catch {
                // ignore
            }
            hlsRef.current = null
        }

        if (isHlsSource(src)) {
            if (Hls.isSupported()) {
                const newHls = new Hls({
                    enableWorker: true,
                    lowLatencyMode: false,
                    backBufferLength: 0,
                })
                hlsRef.current = newHls
                newHls.loadSource(src)
                newHls.attachMedia(videoEl)

                newHls.on(Hls.Events.MANIFEST_PARSED, () => {
                    if (savedCurrentTime > 0 && savedCurrentTime < videoEl.duration) {
                        try {
                            videoEl.currentTime = savedCurrentTime
                        } catch {
                            // ignore
                        }
                    }
                    if (externalIsPlaying && videoEl.paused && !userPausedRef.current) {
                        videoEl.play().catch(() => { })
                    }
                })
            } else if (videoEl.canPlayType('application/vnd.apple.mpegurl')) {
                videoEl.src = src
                videoEl.addEventListener('loadedmetadata', () => {
                    if (savedCurrentTime > 0 && savedCurrentTime < videoEl.duration) {
                        try {
                            videoEl.currentTime = savedCurrentTime
                        } catch {
                            // ignore
                        }
                    }
                }, { once: true })
                if (externalIsPlaying && videoEl.paused && !userPausedRef.current) {
                    videoEl.play().catch(() => { })
                }
            }
        } else {
            videoEl.src = src
            videoEl.addEventListener('loadedmetadata', () => {
                if (savedCurrentTime > 0 && savedCurrentTime < videoEl.duration) {
                    try {
                        videoEl.currentTime = savedCurrentTime
                    } catch {
                        // ignore
                    }
                }
            }, { once: true })
            if (externalIsPlaying && videoEl.paused && !userPausedRef.current) {
                videoEl.play().catch(() => { })
            }
        }

        currentSrcRef.current = src
    }, [src, externalIsPlaying])

    useEffect(() => {
        if (!isPlaying) {
            setControlsVisible(true)
            onControlsVisibilityChangeRef.current?.(true)
            return
        }
        if (!pointerInsideRef.current) {
            scheduleHideControls()
        }
    }, [isPlaying, scheduleHideControls])

    const showCenterToast = useCallback((icon: 'play' | 'pause') => {
        setCenterToastIcon(icon)
        setCenterToastVisible(true)
        if (centerToastTimerRef.current !== null) {
            window.clearTimeout(centerToastTimerRef.current)
            centerToastTimerRef.current = null
        }
        centerToastTimerRef.current = window.setTimeout(() => {
            setCenterToastVisible(false)
            centerToastTimerRef.current = null
        }, 300)
    }, [])

    const togglePlay = useCallback(() => {
        const videoEl = videoElRef.current
        if (!videoEl) return
        if (videoEl.paused) {
            userPausedRef.current = false
            showCenterToast('play')
            try {
                videoEl.play().catch(() => { })
            } catch {
                // ignore
            }
        } else {
            userPausedRef.current = true
            showCenterToast('pause')
            try {
                videoEl.pause()
            } catch {
                // ignore
            }
        }
    }, [showCenterToast])

    const toggleMute = useCallback(() => {
        const videoEl = videoElRef.current
        if (!videoEl) return
        const newMuted = !videoEl.muted
        userMuteActionRef.current = true
        videoEl.muted = newMuted
        lastSyncedMutedRef.current = newMuted
        onMuteChangeRef.current?.(newMuted)
        setTimeout(() => {
            userMuteActionRef.current = false
        }, 100)
    }, [])

    const setPlayerVolume = (v: number) => {
        const videoEl = videoElRef.current
        if (!videoEl) return
        const clampedVol = clamp(v, 0, 1)
        videoEl.volume = clampedVol
        lastSyncedVolumeRef.current = clampedVol
        if (videoEl.muted && clampedVol > 0) {
            userMuteActionRef.current = true
            videoEl.muted = false
            lastSyncedMutedRef.current = false
            onMuteChangeRef.current?.(false)
            setTimeout(() => {
                userMuteActionRef.current = false
            }, 100)
        }
        onVolumeChangeRef.current?.(clampedVol)
    }

    const getPctFromClientX = (clientX: number) => {
        const el = progressTrackRef.current
        if (!el) return 0
        const r = el.getBoundingClientRect()
        const x = clamp(clientX - r.left, 0, r.width)
        return r.width > 0 ? (x / r.width) * 100 : 0
    }

    const getVolPctFromClientX = (clientX: number) => {
        const el = volTrackRef.current
        if (!el) return 0
        const r = el.getBoundingClientRect()
        const x = clamp(clientX - r.left, 0, r.width)
        return r.width > 0 ? (x / r.width) * 100 : 0
    }

    const commitSeekPercent = (pct: number) => {
        const videoEl = videoElRef.current
        if (!videoEl) return

        const range = getSeekableRange()
        if (!range) return

        const span = range.end - range.start
        if (!Number.isFinite(span) || span <= 0) return

        const t = clamp(range.start + (pct / 100) * span, range.start, range.end)
        setCurrentTime(t)
        try {
            videoEl.currentTime = t
        } catch {
            // ignore
        }
    }

    useEffect(() => {
        const videoEl = videoElRef.current
        if (!videoEl || !isCurrent || externalIsPlaying === undefined) return

        const shouldPlay = externalIsPlaying
        const isCurrentlyPaused = videoEl.paused

        if (shouldPlay && isCurrentlyPaused) {
            if (userPausedRef.current) {
                userPausedRef.current = false
            }
            try {
                videoEl.play().catch(() => { })
            } catch {
                // ignore
            }
        } else if (!shouldPlay && !isCurrentlyPaused) {
            userPausedRef.current = true
            try {
                videoEl.pause()
            } catch {
                // ignore
            }
        }
    }, [isCurrent, externalIsPlaying])

    useEffect(() => {
        const videoEl = videoElRef.current
        if (!videoEl || !isCurrent) return

        if (userMuteActionRef.current) return

        if (externalVolume !== undefined && externalVolume !== lastSyncedVolumeRef.current) {
            videoEl.volume = externalVolume
            lastSyncedVolumeRef.current = externalVolume
        }
        if (externalIsMuted !== undefined && externalIsMuted !== lastSyncedMutedRef.current) {
            videoEl.muted = externalIsMuted
            lastSyncedMutedRef.current = externalIsMuted
        }
    }, [isCurrent, externalVolume, externalIsMuted])

    useEffect(() => {
        return () => {
            if (centerToastTimerRef.current !== null) {
                window.clearTimeout(centerToastTimerRef.current)
                centerToastTimerRef.current = null
            }
        }
    }, [])

    if (!isCurrent) {
        return (
            <div ref={containerRef} className={cx('w-full h-full bg-black', className)}>
                <div className="w-full overflow-hidden h-full">
                    <div className="relative w-full h-full">
                        <div ref={mountRef} className="absolute inset-0 w-full h-full" />
                    </div>
                </div>
            </div>
        )
    }

    return (
        <div
            ref={containerRef}
            className={cx('w-full h-full bg-black', className)}
            onMouseEnter={() => {
                pointerInsideRef.current = true
                setControlsVisible(true)
                onControlsVisibilityChangeRef.current?.(true)
                if (hideControlsTimerRef.current) {
                    window.clearTimeout(hideControlsTimerRef.current)
                    hideControlsTimerRef.current = null
                }
            }}
            onMouseLeave={() => {
                pointerInsideRef.current = false
                if (isPlaying) {
                    scheduleHideControls()
                } else {
                    setControlsVisible(true)
                    onControlsVisibilityChangeRef.current?.(true)
                }
            }}
            onClick={(e) => {
                if (ignoreClickRef.current) {
                    ignoreClickRef.current = false
                    return
                }
                const target = e.target as HTMLElement
                const progressTrack = progressTrackRef.current
                if (progressTrack && (progressTrack.contains(target) || target.closest('.group.relative.pt-2.pb-2'))) {
                    return
                }
                showCenterToast(isPlaying ? 'pause' : 'play')
                togglePlay()
                showControls()
            }}
        >
            <div className="w-full overflow-hidden h-full">
                <div className="relative w-full h-full">
                    <div ref={mountRef} className="absolute inset-0 w-full h-full" />

                    {/* 中間暫停 / 播放（fadeIn / fadeOut） */}
                    <div
                        className={cx(
                            'absolute left-1/2 top-1/2 z-25 w-20 h-20 -ml-10 -mt-10 rounded-[28px] bg-[rgba(23,25,28,0.9)]',
                            'transition-all duration-200 ease-[cubic-bezier(0.56,0.12,0.12,0.98)]',
                            centerToastVisible ? 'opacity-100 scale-100 pointer-events-auto' : 'opacity-0 scale-125 pointer-events-none'
                        )}
                        aria-hidden={!centerToastVisible}
                    >
                        <button
                            type="button"
                            className="w-full h-full inline-flex items-center justify-center"
                            onClick={(e) => {
                                e.stopPropagation()
                                showCenterToast(isPlaying ? 'pause' : 'play')
                                togglePlay()
                                showControls()
                            }}
                            aria-label={isPlaying ? '暫停' : '播放'}
                        >
                            {centerToastIcon === 'pause' ? <FaPause className="w-10 h-10 text-white" /> : <FaPlay className="w-10 h-10 text-white" />}
                        </button>
                    </div>

                    {/* 底部控制列 */}
                    <div
                        className={cx(
                            'absolute inset-x-0 bottom-0 z-50 pointer-events-none',
                            'transition-[opacity,transform] duration-200 ease-out',
                            controlsVisible ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-6'
                        )}
                        onMouseEnter={() => {
                            pointerInsideRef.current = true
                            setControlsVisible(true)
                            onControlsVisibilityChangeRef.current?.(true)
                            if (hideControlsTimerRef.current) {
                                window.clearTimeout(hideControlsTimerRef.current)
                                hideControlsTimerRef.current = null
                            }
                        }}
                        onMouseLeave={() => {
                        }}
                        onClick={(e) => e.stopPropagation()}
                        onMouseDown={(e) => e.stopPropagation()}
                    >
                        <div className="relative flex flex-col justify-end h-auto pointer-events-none">
                            {/* 進度條 - YouTube 風格 */}
                            {duration > 0 && (
                                <div className="pointer-events-auto w-full">
                                    <div
                                        ref={progressTrackRef}
                                        className="group relative pt-2 cursor-pointer w-full"
                                        onClick={(e) => {
                                            e.stopPropagation()
                                            if (draggingRef.current) return
                                            const pct = getPctFromClientX(e.clientX)
                                            commitSeekPercent(pct)
                                            setSeekingPercent(null)
                                            showControls()
                                        }}
                                        onPointerDown={(e) => {
                                            if (e.button !== 0) return
                                            e.stopPropagation()
                                            draggingRef.current = true
                                                ; (e.currentTarget as HTMLDivElement).setPointerCapture(e.pointerId)
                                            const pct = getPctFromClientX(e.clientX)
                                            setSeekingPercent(pct)
                                            showControls()
                                        }}
                                        onPointerMove={(e) => {
                                            if (!draggingRef.current) return
                                            e.stopPropagation()
                                            const pct = getPctFromClientX(e.clientX)
                                            setSeekingPercent(pct)
                                        }}
                                        onPointerUp={(e) => {
                                            if (!draggingRef.current) return
                                            e.stopPropagation()
                                            draggingRef.current = false
                                            const pct = getPctFromClientX(e.clientX)
                                            commitSeekPercent(pct)
                                            setSeekingPercent(null)
                                            showControls()
                                        }}
                                        onPointerCancel={() => {
                                            draggingRef.current = false
                                            setSeekingPercent(null)
                                        }}
                                        onMouseDown={(e) => e.stopPropagation()}
                                    >
                                        <div className="relative h-[3px] group-hover:h-[5px] transition-[height] duration-150 bg-white/20">
                                            <div
                                                className="absolute inset-y-0 left-0 bg-[#6fb9ff]"
                                                style={{ width: `${progressValue}%` }}
                                            />
                                        </div>
                                    </div>
                                </div>
                            )}
                        </div>
                    </div>
                </div>
            </div>
        </div>
    )
}


