﻿'use client'

import { useEffect, useMemo, useRef, useState, type ReactNode } from 'react'
import Image from 'next/image'
import LivePreviewPlayer from '@/components/LivePreviewPlayer'
import { hasStreamThumbnailUrl } from '@/components/StreamCover'
import { enqueueSnapshotCapture, canEnqueueWhepSnapshot } from '@/lib/media/snapshotCaptureQueue'
import {
    getCachedStreamSnapshot,
    setCachedStreamSnapshot,
} from '@/lib/media/streamSnapshotCache'
import {
    buildCloudflareStreamThumbnailUrl,
    type CloudflareStreamThumbnailOptions,
} from '@/lib/streams/cloudflareStreamUrls'

const STREAM_THUMB_HEIGHT: CloudflareStreamThumbnailOptions = { height: 240 }

const SNAPSHOT_TIMEOUT_MS = 8_000

type StreamSnapshotCoverProps = {
    thumbnail?: string | null
    src?: string | null
    isLive?: boolean
    alt: string
    imageClassName?: string
    placeholder?: ReactNode
    /** 進入視窗後才嘗試擷圖（熱門列表、Banner 縮圖） */
    captureWhenVisible?: boolean
}

/**
 * 直播封面：有 thumbnail_url 僅顯示自訂圖；否則先 Cloudflare 縮圖佔位，背景 WHEP 擷帧後替換。
 */
export default function StreamSnapshotCover({
    thumbnail,
    src,
    isLive = true,
    alt,
    imageClassName = 'object-cover pointer-events-none',
    placeholder,
    captureWhenVisible = false,
}: StreamSnapshotCoverProps) {
    const imageAlt = alt?.trim() || 'Live stream cover'
    const rootRef = useRef<HTMLDivElement>(null)
    const [inView, setInView] = useState(!captureWhenVisible)

    const thumb = String(thumbnail ?? '').trim()
    const playbackSrc = String(src ?? '').trim()
    const captureKey = `${playbackSrc}:${isLive}`
    const useCustomThumb = hasStreamThumbnailUrl(thumb)

    const cloudflareThumbUrl = useMemo(
        () =>
            !useCustomThumb && playbackSrc && isLive
                ? buildCloudflareStreamThumbnailUrl(playbackSrc, STREAM_THUMB_HEIGHT)
                : null,
        [playbackSrc, isLive, useCustomThumb]
    )

    const [snapshotUrl, setSnapshotUrl] = useState<string | null>(() => {
        if (useCustomThumb) return null
        return getCachedStreamSnapshot(captureKey)
    })
    const [cloudflareThumbFailed, setCloudflareThumbFailed] = useState(false)
    const [captureFailed, setCaptureFailed] = useState(false)
    const [captureActive, setCaptureActive] = useState(false)
    const [snapshotVisible, setSnapshotVisible] = useState(() =>
        Boolean(!useCustomThumb && getCachedStreamSnapshot(captureKey))
    )
    const finishCaptureRef = useRef<(() => void) | null>(null)

    const shouldCaptureSnapshot =
        Boolean(playbackSrc) &&
        isLive &&
        !useCustomThumb &&
        !snapshotUrl &&
        inView &&
        !captureFailed

    useEffect(() => {
        finishCaptureRef.current = null
        setCloudflareThumbFailed(false)
        if (useCustomThumb) {
            setSnapshotUrl(null)
            setSnapshotVisible(false)
            setCaptureFailed(false)
            setCaptureActive(false)
            return
        }
        const cached = getCachedStreamSnapshot(captureKey)
        if (cached) {
            setSnapshotUrl(cached)
            setSnapshotVisible(true)
            setCaptureFailed(false)
            setCaptureActive(false)
            return
        }
        setSnapshotUrl(null)
        setSnapshotVisible(false)
        setCaptureFailed(false)
        setCaptureActive(false)
    }, [captureKey, useCustomThumb])

    useEffect(() => {
        if (!captureWhenVisible) {
            setInView(true)
            return
        }
        const el = rootRef.current
        if (!el) return
        const observer = new IntersectionObserver(
            ([entry]) => setInView(Boolean(entry?.isIntersecting)),
            { rootMargin: '40px', threshold: 0.05 }
        )
        observer.observe(el)
        return () => observer.disconnect()
    }, [captureWhenVisible, captureKey])

    useEffect(() => {
        if (!shouldCaptureSnapshot) return
        if (getCachedStreamSnapshot(captureKey)) return
        if (!canEnqueueWhepSnapshot()) {
            setCaptureFailed(true)
            return
        }

        let cancelled = false

        void enqueueSnapshotCapture(
            () =>
                new Promise<void>((resolve) => {
                    if (cancelled) {
                        resolve()
                        return
                    }

                    const finish = () => {
                        finishCaptureRef.current = null
                        setCaptureActive(false)
                        resolve()
                    }
                    finishCaptureRef.current = finish

                    setCaptureActive(true)

                    window.setTimeout(() => {
                        if (finishCaptureRef.current !== finish) return
                        setCaptureFailed(true)
                        finish()
                    }, SNAPSHOT_TIMEOUT_MS)
                })
        )

        return () => {
            cancelled = true
            finishCaptureRef.current?.()
            finishCaptureRef.current = null
        }
    }, [captureKey, shouldCaptureSnapshot])

    const completeCapture = (next: { url?: string; failed?: boolean }) => {
        if (next.url) {
            setCachedStreamSnapshot(captureKey, next.url)
            setSnapshotUrl(next.url)
            requestAnimationFrame(() => setSnapshotVisible(true))
        }
        if (next.failed) setCaptureFailed(true)
        finishCaptureRef.current?.()
    }

    const showCfPlaceholder =
        !useCustomThumb && Boolean(cloudflareThumbUrl) && !cloudflareThumbFailed

    let content: ReactNode

    if (useCustomThumb) {
        content = (
            <Image src={thumb} alt={imageAlt} fill className={imageClassName} unoptimized />
        )
    } else {
        content = (
            <>
                {showCfPlaceholder ? (
                    <Image
                        src={cloudflareThumbUrl!}
                        alt={imageAlt}
                        fill
                        className={imageClassName}
                        unoptimized
                        onError={() => setCloudflareThumbFailed(true)}
                    />
                ) : (
                    !snapshotUrl &&
                    (placeholder ?? <div className="absolute inset-0 bg-gray-700" />)
                )}

                {snapshotUrl ? (
                    <Image
                        src={snapshotUrl}
                        alt={imageAlt}
                        fill
                        className={`${imageClassName} transition-opacity duration-300 ${
                            snapshotVisible ? 'opacity-100' : 'opacity-0'
                        }`}
                        unoptimized
                    />
                ) : null}

                {shouldCaptureSnapshot && !captureFailed && captureActive ? (
                    <div
                        className="pointer-events-none absolute -left-[9999px] top-0 h-[72px] w-[128px] overflow-hidden opacity-0"
                        aria-hidden
                    >
                        <LivePreviewPlayer
                            src={playbackSrc}
                            isLive
                            snapshotMode
                            onSnapshot={(dataUrl) => completeCapture({ url: dataUrl })}
                            onSourceError={() => completeCapture({ failed: true })}
                        />
                    </div>
                ) : null}
            </>
        )
    }

    if (captureWhenVisible) {
        return (
            <div ref={rootRef} className="absolute inset-0">
                {content}
            </div>
        )
    }

    return <div className="absolute inset-0">{content}</div>
}
