/** WebRTC 訊號格數（1 最弱，4 最強）；null 表示直播中但尚無 stats（例如 HLS） */
export type WebRtcSignalLevel = 1 | 2 | 3 | 4

export type WebRtcSignalSnapshot = {
    level: WebRtcSignalLevel
    rttMs: number | null
    packetLossPercent: number | null
    inboundBitrateKbps: number | null
    iceConnectionState: RTCIceConnectionState | null
    connectionState: RTCPeerConnectionState | null
}

export type RtpAccumulator = {
    packetsReceived: number
    packetsLost: number
    bytesReceived: number
    timestampMs: number
}

export function initRtpAccumulator(): RtpAccumulator {
    return { packetsReceived: 0, packetsLost: 0, bytesReceived: 0, timestampMs: 0 }
}

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

function sumInboundVideo(report: RTCStatsReport): {
    packetsReceived: number
    packetsLost: number
    bytesReceived: number
    jitter: number | null
} {
    let packetsReceived = 0
    let packetsLost = 0
    let bytesReceived = 0
    let jitter: number | null = null
    report.forEach((r) => {
        if (r.type !== 'inbound-rtp') return
        const inbound = r as RTCStats & {
            kind?: string
            packetsReceived?: number
            packetsLost?: number
            bytesReceived?: number
            jitter?: number
        }
        if (inbound.kind && inbound.kind !== 'video') return
        packetsReceived += Number(inbound.packetsReceived ?? 0)
        packetsLost += Number(inbound.packetsLost ?? 0)
        bytesReceived += Number(inbound.bytesReceived ?? 0)
        if (typeof inbound.jitter === 'number' && Number.isFinite(inbound.jitter)) {
            jitter = inbound.jitter
        }
    })
    return { packetsReceived, packetsLost, bytesReceived, jitter }
}

export function computeSignalLevel(
    rttMs: number | null,
    packetLossPercent: number | null,
    iceState: RTCIceConnectionState | null
): WebRtcSignalLevel {
    if (iceState === 'failed' || iceState === 'closed' || iceState === 'disconnected') {
        return 1
    }
    const rtt = rttMs ?? 999
    const loss = packetLossPercent ?? 100
    if (rtt < 80 && loss < 1) return 4
    if (rtt < 150 && loss < 3) return 3
    if (rtt < 250 && loss < 7) return 2
    return 1
}

export async function sampleWebRtcSignal(
    pc: RTCPeerConnection,
    acc: RtpAccumulator
): Promise<WebRtcSignalSnapshot> {
    const report = await pc.getStats()
    const pair = pickActiveCandidatePair(report)
    const inbound = sumInboundVideo(report)

    const rttRaw = (pair as RTCStats & { currentRoundTripTime?: number } | null)?.currentRoundTripTime
    const rttMs =
        typeof rttRaw === 'number' && Number.isFinite(rttRaw) && rttRaw > 0
            ? Math.round(rttRaw * 1000)
            : null

    const now = Date.now()
    let packetLossPercent: number | null = null
    let inboundBitrateKbps: number | null = null

    if (acc.timestampMs > 0) {
        const dtSec = (now - acc.timestampMs) / 1000
        if (dtSec > 0) {
            const dRecv = Math.max(0, inbound.packetsReceived - acc.packetsReceived)
            const dLost = Math.max(0, inbound.packetsLost - acc.packetsLost)
            const total = dRecv + dLost
            if (total > 0) {
                packetLossPercent = (dLost / total) * 100
            }
            const dBytes = Math.max(0, inbound.bytesReceived - acc.bytesReceived)
            inboundBitrateKbps = Math.round((dBytes * 8) / dtSec / 1000)
        }
    }

    acc.packetsReceived = inbound.packetsReceived
    acc.packetsLost = inbound.packetsLost
    acc.bytesReceived = inbound.bytesReceived
    acc.timestampMs = now

    const iceConnectionState = pc.iceConnectionState ?? null
    const connectionState = pc.connectionState ?? null
    const level = computeSignalLevel(rttMs, packetLossPercent, iceConnectionState)

    return {
        level,
        rttMs,
        packetLossPercent,
        inboundBitrateKbps,
        iceConnectionState,
        connectionState,
    }
}

export type WebRtcSignalMonitorOptions = {
    intervalMs?: number
    onUpdate: (snapshot: WebRtcSignalSnapshot) => void
    onInactive?: () => void
}

/** 輪詢 RTCPeerConnection.getStats() 並回報訊號格數 */
export function startWebRtcSignalMonitor(
    getPeerConnection: () => RTCPeerConnection | null,
    options: WebRtcSignalMonitorOptions
): () => void {
    const intervalMs = options.intervalMs ?? 1000
    const acc = initRtpAccumulator()
    let disposed = false
    let inFlight = false

    const tick = async () => {
        if (disposed || inFlight) return
        const pc = getPeerConnection()
        if (!pc || pc.connectionState === 'closed') {
            options.onInactive?.()
            acc.timestampMs = 0
            return
        }
        inFlight = true
        try {
            const snapshot = await sampleWebRtcSignal(pc, acc)
            if (!disposed) options.onUpdate(snapshot)
        } catch {
            if (!disposed) options.onInactive?.()
        } finally {
            inFlight = false
        }
    }

    const id = window.setInterval(() => {
        void tick()
    }, intervalMs)
    void tick()

    return () => {
        disposed = true
        window.clearInterval(id)
    }
}
