import type Hls from 'hls.js'
import {
    initRtpAccumulator,
    sampleWebRtcSignal,
    type RtpAccumulator,
} from '@/lib/media/webrtcSignalMonitor'

export type StreamPlaybackStats = {
    transport: 'webrtc' | 'hls' | 'native' | 'none'
    sessionId: string | null
    capturedAt: number
    viewportWidth: number | null
    viewportHeight: number | null
    rttMs: number | null
    packetLossPercent: number | null
    inboundBitrateKbps: number | null
    jitterMs: number | null
    fps: number | null
    framesDropped: number | null
    framesDecoded: number | null
    videoWidth: number | null
    videoHeight: number | null
    optimalVideoWidth: number | null
    optimalVideoHeight: number | null
    optimalFps: number | null
    latencySec: number | null
    volumePercent: number | null
    muted: boolean
    videoCodec: string | null
    audioCodec: string | null
    /** WebRTC：連線狀態摘要（不另列 ICE / candidate） */
    connectionSummary: string | null
}

export type StreamStatsContext = {
    video: HTMLVideoElement | null
    viewportEl: HTMLElement | null
    sessionId: string | null
    hls?: Hls | null
}

export function createStreamStatsAccumulator(): RtpAccumulator {
    return initRtpAccumulator()
}

export function shortenCodecMime(mime: string | null | undefined): string | null {
    if (!mime) return null
    const raw = mime.includes('/') ? mime.split('/').pop()! : mime
    const base = raw.split(';')[0].trim()
    if (!base) return null
    if (base.length <= 12) return base
    return base.slice(0, 12)
}

function pickActiveCandidatePair(report: RTCStatsReport) {
    let nominated: (RTCStats & { remoteCandidateId?: string }) | null = null
    let succeeded: (RTCStats & { remoteCandidateId?: string }) | null = null
    report.forEach((r) => {
        if (r.type !== 'candidate-pair') return
        const pair = r as RTCStats & { state?: string; nominated?: boolean }
        if (pair.state === 'succeeded') {
            succeeded = pair
            if (pair.nominated) nominated = pair
        }
    })
    return nominated ?? succeeded
}

function readCandidateType(
    report: RTCStatsReport,
    pair: RTCStats & { remoteCandidateId?: string } | null
): string | null {
    const id = pair?.remoteCandidateId
    if (!id) return null
    const remote = report.get(id) as RTCStats & { candidateType?: string } | undefined
    return remote?.candidateType ? String(remote.candidateType) : null
}

function buildWebRtcConnectionSummary(
    ice: string | null,
    conn: string | null,
    candidate: string | null
): string | null {
    const parts: string[] = []
    const iceNorm = ice?.toLowerCase() ?? ''
    const connNorm = conn?.toLowerCase() ?? ''

    if (iceNorm && connNorm && iceNorm === connNorm) {
        if (iceNorm !== 'connected' && iceNorm !== 'completed') {
            parts.push(ice!)
        }
    } else {
        if (conn) parts.push(conn)
        else if (ice) parts.push(ice)
    }

    if (candidate) parts.push(candidate)
    return parts.length > 0 ? parts.join(' · ') : null
}

function readVideoLatencySec(video: HTMLVideoElement): number | null {
    try {
        if (!video.buffered.length) return null
        const edge = video.buffered.end(video.buffered.length - 1)
        const lat = edge - video.currentTime
        if (!Number.isFinite(lat) || lat < 0) return null
        return Math.round(lat * 100) / 100
    } catch {
        return null
    }
}

function readVideoDimensions(video: HTMLVideoElement): { w: number | null; h: number | null } {
    try {
        const w = video.videoWidth > 0 ? video.videoWidth : null
        const h = video.videoHeight > 0 ? video.videoHeight : null
        return { w, h }
    } catch {
        return { w: null, h: null }
    }
}

/** 實際正在播放的 HLS level（含自動畫質時用 loadLevel） */
export function resolveHlsPlayingLevelIndex(hls: Hls): number | null {
    if (!hls.levels?.length) return null
    if (hls.currentLevel >= 0 && hls.currentLevel < hls.levels.length) return hls.currentLevel
    if (hls.loadLevel >= 0 && hls.loadLevel < hls.levels.length) return hls.loadLevel
    return null
}

type LevelMetrics = {
    w: number | null
    h: number | null
    fps: number | null
    bitrateKbps: number | null
}

function readHlsLevelMetrics(
    level: Hls['levels'][number] | undefined,
    video?: HTMLVideoElement | null
): LevelMetrics {
    if (!level) return { w: null, h: null, fps: null, bitrateKbps: null }

    let h = level.height > 0 ? level.height : null
    let w = level.width > 0 ? level.width : null
    const fps = typeof level.frameRate === 'number' && level.frameRate > 0 ? level.frameRate : null
    const bitrateKbps = level.bitrate ? Math.round(level.bitrate / 1000) : null

    if (video && h && (!w || w <= 0)) {
        const vd = readVideoDimensions(video)
        if (vd.w && vd.h) {
            w = Math.round((vd.w / vd.h) * h)
        }
    }

    if ((!w || !h) && video) {
        const vd = readVideoDimensions(video)
        w = w ?? vd.w
        h = h ?? vd.h
    }

    return { w, h, fps, bitrateKbps }
}

function readHlsOptimal(hls: Hls | null | undefined): LevelMetrics {
    if (!hls?.levels?.length) {
        return { w: null, h: null, fps: null, bitrateKbps: null }
    }
    const best = hls.levels.reduce((acc, lvl) => (lvl.height > acc.height ? lvl : acc), hls.levels[0])
    return readHlsLevelMetrics(best)
}

function readHlsPlaybackResolution(
    hls: Hls | null | undefined,
    video: HTMLVideoElement | null
): { current: LevelMetrics; optimal: LevelMetrics } {
    const optimal = readHlsOptimal(hls)
    if (!hls) {
        const vd = video ? readVideoDimensions(video) : { w: null, h: null }
        return {
            current: { w: vd.w, h: vd.h, fps: null, bitrateKbps: null },
            optimal,
        }
    }

    const playingIndex = resolveHlsPlayingLevelIndex(hls)
    const current =
        playingIndex != null
            ? readHlsLevelMetrics(hls.levels[playingIndex], video)
            : readHlsLevelMetrics(undefined, video)

    return { current, optimal }
}

function readHlsCurrentCodecs(hls: Hls | null | undefined): {
    videoCodec: string | null
    audioCodec: string | null
    fps: number | null
} {
    if (!hls) return { videoCodec: null, audioCodec: null, fps: null }
    const index = resolveHlsPlayingLevelIndex(hls)
    if (index == null || !hls.levels[index]) {
        return { videoCodec: null, audioCodec: null, fps: null }
    }
    const level = hls.levels[index]
    return {
        videoCodec: shortenCodecMime(level.videoCodec),
        audioCodec: shortenCodecMime(level.audioCodec),
        fps: typeof level.frameRate === 'number' && level.frameRate > 0 ? level.frameRate : null,
    }
}

/** 合併播放器 DOM 狀態（視窗、音量、工作階段、HLS 最佳畫質） */
export function enrichStreamPlaybackStats(
    stats: StreamPlaybackStats,
    ctx: StreamStatsContext
): StreamPlaybackStats {
    const { video, viewportEl, sessionId, hls } = ctx
    const viewportW =
        viewportEl && viewportEl.clientWidth > 0
            ? viewportEl.clientWidth
            : video && video.clientWidth > 0
              ? video.clientWidth
              : null
    const viewportH =
        viewportEl && viewportEl.clientHeight > 0
            ? viewportEl.clientHeight
            : video && video.clientHeight > 0
              ? video.clientHeight
              : null

    const hlsCodecs = readHlsCurrentCodecs(hls)
    const hlsRes = readHlsPlaybackResolution(hls, video)

    const playingIndex = hls ? resolveHlsPlayingLevelIndex(hls) : null
    const levelBitrate =
        playingIndex != null && hls?.levels[playingIndex]?.bitrate
            ? Math.round(hls.levels[playingIndex].bitrate / 1000)
            : null

    return {
        ...stats,
        capturedAt: Date.now(),
        sessionId: sessionId ?? stats.sessionId,
        viewportWidth: viewportW,
        viewportHeight: viewportH,
        volumePercent: video ? Math.round(video.volume * 100) : stats.volumePercent,
        muted: video?.muted ?? stats.muted,
        videoWidth: hls ? (hlsRes.current.w ?? stats.videoWidth) : stats.videoWidth,
        videoHeight: hls ? (hlsRes.current.h ?? stats.videoHeight) : stats.videoHeight,
        fps: hlsRes.current.fps ?? stats.fps ?? hlsCodecs.fps,
        optimalVideoWidth: hls ? hlsRes.optimal.w : stats.optimalVideoWidth,
        optimalVideoHeight: hls ? hlsRes.optimal.h : stats.optimalVideoHeight,
        optimalFps: hls ? hlsRes.optimal.fps : stats.optimalFps,
        videoCodec: stats.videoCodec ?? hlsCodecs.videoCodec,
        audioCodec: stats.audioCodec ?? hlsCodecs.audioCodec,
        inboundBitrateKbps: stats.inboundBitrateKbps ?? levelBitrate ?? hlsRes.current.bitrateKbps,
    }
}

/** 從 RTCPeerConnection.getStats() 收集播放品質（WHEP / WebRTC） */
export async function collectWebRtcPlaybackStats(
    pc: RTCPeerConnection,
    acc: RtpAccumulator,
    video?: HTMLVideoElement | null
): Promise<StreamPlaybackStats> {
    const signal = await sampleWebRtcSignal(pc, acc)
    const report = await pc.getStats()
    const pair = pickActiveCandidatePair(report)
    const candidateType = readCandidateType(report, pair)

    let jitterMs: number | null = null
    let fps: number | null = null
    let framesDropped: number | null = null
    let framesDecoded: number | null = null
    let videoCodec: string | null = null
    let audioCodec: string | null = null
    let rtpVideoWidth: number | null = null
    let rtpVideoHeight: number | null = null

    report.forEach((r) => {
        if (r.type === 'inbound-rtp') {
            const inbound = r as RTCStats & {
                kind?: string
                jitter?: number
                framesPerSecond?: number
                framesDropped?: number
                framesDecoded?: number
                frameWidth?: number
                frameHeight?: number
                codecId?: string
            }
            const kind = inbound.kind ?? 'video'
            if (kind === 'video') {
                if (typeof inbound.jitter === 'number' && Number.isFinite(inbound.jitter)) {
                    jitterMs = Math.round(inbound.jitter * 1000)
                }
                if (typeof inbound.framesPerSecond === 'number' && Number.isFinite(inbound.framesPerSecond)) {
                    fps = Math.round(inbound.framesPerSecond * 10) / 10
                }
                if (typeof inbound.framesDropped === 'number') {
                    framesDropped = inbound.framesDropped
                }
                if (typeof inbound.framesDecoded === 'number') {
                    framesDecoded = inbound.framesDecoded
                }
                if (typeof inbound.frameWidth === 'number' && inbound.frameWidth > 0) {
                    rtpVideoWidth = inbound.frameWidth
                }
                if (typeof inbound.frameHeight === 'number' && inbound.frameHeight > 0) {
                    rtpVideoHeight = inbound.frameHeight
                }
            }
            if (inbound.codecId) {
                const codecStat = report.get(inbound.codecId) as RTCStats & { mimeType?: string } | undefined
                const short = shortenCodecMime(codecStat?.mimeType)
                if (short) {
                    if (kind === 'audio') audioCodec = short
                    else videoCodec = short
                }
            }
        }
    })

    const fromVideo = video ? readVideoDimensions(video) : { w: null, h: null }
    const videoWidth = rtpVideoWidth ?? fromVideo.w
    const videoHeight = rtpVideoHeight ?? fromVideo.h
    const latencySec = video ? readVideoLatencySec(video) : null

    return {
        transport: 'webrtc',
        sessionId: null,
        capturedAt: Date.now(),
        viewportWidth: null,
        viewportHeight: null,
        rttMs: signal.rttMs,
        packetLossPercent: signal.packetLossPercent,
        inboundBitrateKbps: signal.inboundBitrateKbps,
        jitterMs,
        fps,
        framesDropped,
        framesDecoded,
        videoWidth,
        videoHeight,
        optimalVideoWidth: videoWidth,
        optimalVideoHeight: videoHeight,
        optimalFps: fps,
        latencySec,
        volumePercent: null,
        muted: false,
        videoCodec,
        audioCodec,
        connectionSummary: buildWebRtcConnectionSummary(
            signal.iceConnectionState,
            signal.connectionState,
            candidateType
        ),
    }
}

/** HLS / 原生 video 標籤播放 */
export function collectVideoPlaybackStats(
    video: HTMLVideoElement,
    transport: 'hls' | 'native',
    hls?: Hls | null
): StreamPlaybackStats {
    const latencySec = readVideoLatencySec(video)
    const hlsRes = readHlsPlaybackResolution(hls ?? null, video)
    const hlsCodecs = readHlsCurrentCodecs(hls)

    const inboundBitrateKbps =
        hlsRes.current.bitrateKbps ??
        (() => {
            if (!hls) return null
            const idx = resolveHlsPlayingLevelIndex(hls)
            if (idx == null || !hls.levels[idx]?.bitrate) return null
            return Math.round(hls.levels[idx].bitrate / 1000)
        })()

    let framesDropped: number | null = null
    let framesDecoded: number | null = null
    const quality = video.getVideoPlaybackQuality?.()
    if (quality) {
        framesDropped = quality.droppedVideoFrames ?? null
        framesDecoded = quality.totalVideoFrames ?? null
    }

    return {
        transport,
        sessionId: null,
        capturedAt: Date.now(),
        viewportWidth: null,
        viewportHeight: null,
        rttMs: null,
        packetLossPercent: null,
        inboundBitrateKbps,
        jitterMs: null,
        fps: hlsRes.current.fps ?? hlsCodecs.fps,
        framesDropped,
        framesDecoded,
        videoWidth: hlsRes.current.w,
        videoHeight: hlsRes.current.h,
        optimalVideoWidth: hlsRes.optimal.w,
        optimalVideoHeight: hlsRes.optimal.h,
        optimalFps: hlsRes.optimal.fps,
        latencySec,
        volumePercent: null,
        muted: false,
        videoCodec: hlsCodecs.videoCodec,
        audioCodec: hlsCodecs.audioCodec,
        connectionSummary: null,
    }
}

export function emptyStreamPlaybackStats(): StreamPlaybackStats {
    return {
        transport: 'none',
        sessionId: null,
        capturedAt: Date.now(),
        viewportWidth: null,
        viewportHeight: null,
        rttMs: null,
        packetLossPercent: null,
        inboundBitrateKbps: null,
        jitterMs: null,
        fps: null,
        framesDropped: null,
        framesDecoded: null,
        videoWidth: null,
        videoHeight: null,
        optimalVideoWidth: null,
        optimalVideoHeight: null,
        optimalFps: null,
        latencySec: null,
        volumePercent: null,
        muted: false,
        videoCodec: null,
        audioCodec: null,
        connectionSummary: null,
    }
}
