'use client'

import { useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import type { StreamPlaybackStats } from '@/lib/media/streamPlaybackStats'

type Props = {
    stats: StreamPlaybackStats
    onClose: () => void
}

type StatRow =
    | { key: string; label: string; value: string }
    | {
          key: string
          label: string
          value: string
          chart: 'bitrate' | 'latency'
      }
    | {
          key: string
          label: string
          value: string
          barFill: number
          barMax: number
          barClassName: string
      }

const CHART_HISTORY_LEN = 60
const CHART_HEIGHT = 32
const BUFFER_BAR_MAX_SEC = 30

function formatResolution(w: number | null, h: number | null, fps?: number | null): string {
    if (!w || !h) return '—'
    const base = `${w}×${h}`
    if (fps != null && Number.isFinite(fps)) return `${base}@${Math.round(fps)}`
    return base
}

function formatKbps(value: number | null): string {
    if (value == null || !Number.isFinite(value)) return '—'
    return `${value} Kbps`
}

function formatLatency(value: number | null, unit: 'ms' | 's'): string {
    if (value == null || !Number.isFinite(value)) return '—'
    if (unit === 'ms') return `${Math.round(value)} ms`
    return `${value.toFixed(2)} s`
}

function formatPercent(value: number | null): string {
    if (value == null || !Number.isFinite(value)) return '—'
    return `${value.toFixed(1)}%`
}

function truncateSessionId(id: string): string {
    if (id.length <= 24) return id
    return `${id.slice(0, 10)}…${id.slice(-8)}`
}

function pushHistory(prev: number[], value: number): number[] {
    const next = [...prev, value]
    if (next.length > CHART_HISTORY_LEN) return next.slice(-CHART_HISTORY_LEN)
    return next
}

function transportLabel(
    transport: StreamPlaybackStats['transport'],
    t: (key: string) => string
): string {
    switch (transport) {
        case 'webrtc':
            return t('room.player.streamStats.transportWebrtc')
        case 'hls':
            return t('room.player.streamStats.transportHls')
        case 'native':
            return t('room.player.streamStats.transportNative')
        default:
            return t('room.player.streamStats.unavailable')
    }
}

/** YouTube 風格：時間序列區域折線圖（Canvas） */
function StatsTimelineChart({
    samples,
    stroke,
    fill,
}: {
    samples: number[]
    stroke: string
    fill: string
}) {
    const canvasRef = useRef<HTMLCanvasElement>(null)

    useEffect(() => {
        const canvas = canvasRef.current
        if (!canvas || samples.length === 0) return

        const draw = () => {
            const cssWidth = canvas.clientWidth
            const cssHeight = canvas.clientHeight
            if (cssWidth <= 0 || cssHeight <= 0) return

            const dpr = window.devicePixelRatio || 1
            canvas.width = Math.round(cssWidth * dpr)
            canvas.height = Math.round(cssHeight * dpr)

            const ctx = canvas.getContext('2d')
            if (!ctx) return

            ctx.setTransform(dpr, 0, 0, dpr, 0, 0)
            ctx.clearRect(0, 0, cssWidth, cssHeight)

            if (samples.length === 1) {
                ctx.fillStyle = stroke
                ctx.beginPath()
                ctx.arc(cssWidth / 2, cssHeight * 0.35, 1.2, 0, Math.PI * 2)
                ctx.fill()
                return
            }

            const max = Math.max(...samples, 1)
            const len = samples.length
            const coords = samples.map((v, i) => ({
                x: (i / (len - 1)) * cssWidth,
                y: cssHeight - 1 - (v / max) * (cssHeight - 3),
            }))

            ctx.beginPath()
            ctx.moveTo(coords[0].x, coords[0].y)
            for (let i = 1; i < coords.length; i++) {
                ctx.lineTo(coords[i].x, coords[i].y)
            }
            ctx.lineTo(cssWidth, cssHeight)
            ctx.lineTo(0, cssHeight)
            ctx.closePath()
            ctx.fillStyle = fill
            ctx.fill()

            ctx.beginPath()
            ctx.moveTo(coords[0].x, coords[0].y)
            for (let i = 1; i < coords.length; i++) {
                ctx.lineTo(coords[i].x, coords[i].y)
            }
            ctx.strokeStyle = stroke
            ctx.lineWidth = 0.75
            ctx.stroke()
        }

        draw()
        const ro = new ResizeObserver(draw)
        ro.observe(canvas)
        return () => ro.disconnect()
    }, [samples, stroke, fill])

    if (samples.length === 0) {
        return <div className="mt-1 h-8 w-full rounded-sm bg-white/[0.06]" aria-hidden />
    }

    return (
        <canvas
            ref={canvasRef}
            className="mt-1 block h-8 w-full"
            style={{ height: CHART_HEIGHT }}
            aria-hidden
        />
    )
}

function StatsBar({
    fill,
    max,
    className,
}: {
    fill: number
    max: number
    className: string
}) {
    const pct = max > 0 ? Math.min(100, Math.max(0, (fill / max) * 100)) : 0
    return (
        <div className="mt-0.5 h-1 w-full overflow-hidden rounded-sm bg-white/15" aria-hidden>
            <div
                className={`h-full rounded-sm transition-[width] duration-300 ${className}`}
                style={{ width: `${pct}%` }}
            />
        </div>
    )
}

function buildRows(
    stats: StreamPlaybackStats,
    t: (key: string, opts?: Record<string, string>) => string,
    locale: string
): StatRow[] {
    const rows: StatRow[] = []

    if (stats.transport !== 'none') {
        const transport = transportLabel(stats.transport, t)
        const value =
            stats.transport === 'webrtc' && stats.connectionSummary
                ? `${transport} · ${stats.connectionSummary}`
                : transport
        rows.push({
            key: 'mode',
            label: t('room.player.streamStats.playbackMode'),
            value,
        })
    }

    if (stats.sessionId) {
        rows.push({
            key: 'session',
            label: t('room.player.streamStats.session'),
            value: truncateSessionId(stats.sessionId),
        })
    }

    if (stats.viewportWidth && stats.viewportHeight) {
        let framesValue = '—'
        if (stats.framesDropped != null && stats.framesDecoded != null) {
            framesValue = t('room.player.streamStats.droppedOfTotal', {
                dropped: String(stats.framesDropped),
                total: String(stats.framesDecoded),
            })
        } else if (stats.framesDropped != null) {
            framesValue = String(stats.framesDropped)
        }
        rows.push({
            key: 'viewport',
            label: t('room.player.streamStats.viewportFrames'),
            value: `${stats.viewportWidth}×${stats.viewportHeight} / ${framesValue}`,
        })
    }

    const currentRes = formatResolution(stats.videoWidth, stats.videoHeight, stats.fps)
    const optimalRes = formatResolution(
        stats.optimalVideoWidth,
        stats.optimalVideoHeight,
        stats.optimalFps
    )
    if (currentRes !== '—' || optimalRes !== '—') {
        rows.push({
            key: 'resolution',
            label: t('room.player.streamStats.resolutionCurrent'),
            value: optimalRes !== '—' && optimalRes !== currentRes ? `${currentRes} / ${optimalRes}` : currentRes,
        })
    }

    const codecParts = [stats.videoCodec, stats.audioCodec].filter(Boolean) as string[]
    if (codecParts.length > 0) {
        rows.push({
            key: 'codecs',
            label: t('room.player.streamStats.codecs'),
            value: codecParts.join(' / '),
        })
    }

    if (stats.inboundBitrateKbps != null) {
        rows.push({
            key: 'bitrate',
            label: t('room.player.streamStats.connectionSpeed'),
            value: formatKbps(stats.inboundBitrateKbps),
            chart: 'bitrate',
        })
    }

    if (stats.latencySec != null) {
        rows.push({
            key: 'buffer',
            label: t('room.player.streamStats.bufferHealth'),
            value: formatLatency(stats.latencySec, 's'),
            barFill: stats.latencySec,
            barMax: BUFFER_BAR_MAX_SEC,
            barClassName: 'bg-amber-400/90',
        })
    }

    if (stats.transport === 'webrtc' && stats.rttMs != null) {
        rows.push({
            key: 'liveLatency',
            label: t('room.player.streamStats.liveLatency'),
            value: formatLatency(stats.rttMs, 'ms'),
            chart: 'latency',
        })
    }

    if (
        stats.transport === 'webrtc' &&
        stats.packetLossPercent != null &&
        Number.isFinite(stats.packetLossPercent)
    ) {
        rows.push({
            key: 'packetLoss',
            label: t('room.player.streamStats.packetLoss'),
            value: formatPercent(stats.packetLossPercent),
        })
    }

    if (stats.transport === 'webrtc' && stats.jitterMs != null) {
        rows.push({
            key: 'jitter',
            label: t('room.player.streamStats.jitter'),
            value: formatLatency(stats.jitterMs, 'ms'),
        })
    }

    rows.push({
        key: 'date',
        label: t('room.player.streamStats.date'),
        value: new Date(stats.capturedAt).toLocaleString(locale, {
            year: 'numeric',
            month: 'numeric',
            day: 'numeric',
            hour: 'numeric',
            minute: '2-digit',
            second: '2-digit',
        }),
    })

    return rows
}

export default function PlayerStreamStatsMenu({ stats, onClose }: Props) {
    const { t, i18n } = useTranslation()
    const locale = i18n.resolvedLanguage || i18n.language || 'en'
    const [bitrateHistory, setBitrateHistory] = useState<number[]>([])
    const [latencyHistory, setLatencyHistory] = useState<number[]>([])

    useEffect(() => {
        if (stats.inboundBitrateKbps != null && Number.isFinite(stats.inboundBitrateKbps)) {
            setBitrateHistory((prev) => pushHistory(prev, stats.inboundBitrateKbps!))
        }
    }, [stats.inboundBitrateKbps])

    useEffect(() => {
        if (stats.rttMs != null && Number.isFinite(stats.rttMs)) {
            setLatencyHistory((prev) => pushHistory(prev, stats.rttMs!))
        }
    }, [stats.rttMs])

    const rows = useMemo(() => buildRows(stats, t, locale), [stats, t, locale])

    return (
        <div
            className="pointer-events-auto absolute top-2 left-2 z-[60] w-max max-w-[calc(100%-16px)] min-w-[240px] whitespace-nowrap rounded border border-white/10 bg-black/80 px-3 py-2 text-left shadow-lg backdrop-blur-[2px]"
            role="dialog"
            aria-label={t('room.player.streamStats.title')}
            onContextMenu={(e) => e.preventDefault()}
            onClick={(e) => e.stopPropagation()}
            onDoubleClick={(e) => e.stopPropagation()}
        >
            <div className="mb-2 flex flex-nowrap items-center justify-between gap-2 border-b border-white/10 pb-1.5">
                <span className="whitespace-nowrap text-[11px] font-semibold tracking-wide text-white/90">
                    {t('room.player.streamStats.title')}
                </span>
                <button
                    type="button"
                    className="flex h-5 min-w-5 items-center justify-center rounded px-1 text-xs leading-none text-white/70 transition-colors hover:bg-white/10 hover:text-white"
                    aria-label={t('common.cancel')}
                    onClick={onClose}
                >
                    ×
                </button>
            </div>

            <dl className="space-y-2 font-mono text-[11px] leading-none">
                {rows.map((row) => (
                    <div key={row.key}>
                        <div className="flex flex-nowrap items-baseline justify-between gap-4">
                            <dt className="shrink-0 whitespace-nowrap text-white/50">{row.label}</dt>
                            <dd className="shrink-0 whitespace-nowrap text-right text-white/90 tabular-nums">
                                {row.value}
                            </dd>
                        </div>
                        {'chart' in row && row.chart === 'bitrate' && (
                            <StatsTimelineChart
                                samples={bitrateHistory}
                                stroke="rgb(34 211 238)"
                                fill="rgba(34, 211, 238, 0.35)"
                            />
                        )}
                        {'chart' in row && row.chart === 'latency' && (
                            <StatsTimelineChart
                                samples={latencyHistory}
                                stroke="rgb(250 204 21)"
                                fill="rgba(250, 204, 21, 0.35)"
                            />
                        )}
                        {'barFill' in row && (
                            <StatsBar fill={row.barFill} max={row.barMax} className={row.barClassName} />
                        )}
                    </div>
                ))}
            </dl>
        </div>
    )
}
