/*
 * Playback capability probing adapted from Wholphin's
 * MediaCodecCapabilitiesTest.kt, which is itself derived from Jellyfin Android TV.
 *
 * Wholphin: https://github.com/damontecres/Wholphin
 * Jellyfin Android TV: https://github.com/jellyfin/jellyfin-androidtv
 *
 * Modifications Copyright (C) 2026 Memby contributors
 * SPDX-License-Identifier: GPL-2.0-only
 */

package com.ponzischeme89.memby.data.playback

import android.media.MediaCodecInfo.CodecProfileLevel
import android.media.MediaCodecList
import android.media.MediaFormat
import android.os.Build

data class VideoDecoderCapabilities(
    val supported: Boolean = false,
    val profiles: Set<String> = emptySet(),
    val mainLevel: Int = 0,
    val tenBitLevel: Int = 0,
    val maxWidth: Int = 0,
    val maxHeight: Int = 0,
    val hdr10: Boolean = false,
    val hdr10Plus: Boolean = false,
    val dolbyVision: Boolean = false,
)

data class DevicePlaybackCapabilities(
    val h264: VideoDecoderCapabilities = VideoDecoderCapabilities(supported = true),
    val hevc: VideoDecoderCapabilities = VideoDecoderCapabilities(),
)

/**
 * The expensive platform query is performed once, off the UI thread on first network or
 * direct-play negotiation. A single immutable result then describes this installed build
 * to both the Memby gateway and Emby itself.
 */
val devicePlaybackCapabilities: DevicePlaybackCapabilities by lazy {
    runCatching { AndroidVideoCapabilityProbe().probe() }
        // H.264 is Android's baseline playback format and remains the safe fallback when
        // a vendor codec exposes incomplete or broken MediaCodec metadata.
        .getOrElse { DevicePlaybackCapabilities() }
}

internal fun DevicePlaybackCapabilities.gatewayCapabilityTokens(): List<String> = buildList {
    if (h264.supported) add("video_h264_decode")
    h264.profiles.sorted().forEach { add("video_h264_profile_$it") }
    if (h264.mainLevel > 0) add("video_h264_level_${h264.mainLevel}")
    if (h264.tenBitLevel > 0) add("video_h264_high10_level_${h264.tenBitLevel}")
    addResolution("video_h264", h264)

    if (hevc.supported) add("video_hevc_decode")
    hevc.profiles.sorted().forEach { add("video_hevc_profile_$it") }
    if (hevc.mainLevel > 0) add("video_hevc_main_level_${hevc.mainLevel}")
    if (hevc.tenBitLevel > 0) add("video_hevc_main10_level_${hevc.tenBitLevel}")
    addResolution("video_hevc", hevc)
    if (hevc.hdr10) add("video_hevc_hdr10")
    if (hevc.hdr10Plus) add("video_hevc_hdr10plus")
    if (hevc.dolbyVision) add("video_hevc_dolby_vision")
}

private fun MutableList<String>.addResolution(prefix: String, codec: VideoDecoderCapabilities) {
    if (codec.maxWidth > 0 && codec.maxHeight > 0) {
        add("${prefix}_max_${codec.maxWidth}x${codec.maxHeight}")
    }
}

private class AndroidVideoCapabilityProbe {
    private val codecInfos by lazy { MediaCodecList(MediaCodecList.REGULAR_CODECS).codecInfos }

    fun probe(): DevicePlaybackCapabilities = DevicePlaybackCapabilities(
        h264 = probeH264(),
        hevc = probeHevc(),
    )

    private fun probeH264(): VideoDecoderCapabilities {
        val mime = MediaFormat.MIMETYPE_VIDEO_AVC
        val supported = hasCodecForMime(mime)
        // As in Wholphin, ordinary AVC support covers the backwards-compatible
        // baseline/main/high family even when vendor metadata lists only its highest
        // profile. High10 remains opt-in because it is not universally supported.
        val high10Level = mappedLevel(
            mime,
            setOf(CodecProfileLevel.AVCProfileHigh10),
            AVC_LEVELS,
        )
        val profiles = buildSet {
            if (supported) addAll(listOf("baseline", "constrained_baseline", "main", "high"))
            if (high10Level > 0) add("high10")
        }
        val (maxWidth, maxHeight) = maxResolution(mime)
        return VideoDecoderCapabilities(
            supported = supported,
            profiles = profiles,
            mainLevel = mappedLevel(
                mime,
                setOf(
                    CodecProfileLevel.AVCProfileBaseline,
                    CodecProfileLevel.AVCProfileMain,
                    CodecProfileLevel.AVCProfileHigh,
                ),
                AVC_LEVELS,
            ),
            tenBitLevel = high10Level,
            maxWidth = maxWidth,
            maxHeight = maxHeight,
        )
    }

    private fun probeHevc(): VideoDecoderCapabilities {
        val mime = MediaFormat.MIMETYPE_VIDEO_HEVC
        val supported = hasCodecForMime(mime)
        val mainLevel = mappedLevel(
            mime,
            setOf(CodecProfileLevel.HEVCProfileMain),
            HEVC_LEVELS,
        )
        val main10Level = mappedLevel(
            mime,
            setOf(CodecProfileLevel.HEVCProfileMain10),
            HEVC_LEVELS,
        )
        val (maxWidth, maxHeight) = maxResolution(mime)
        return VideoDecoderCapabilities(
            supported = supported,
            profiles = buildSet {
                if (supported) add("main")
                if (main10Level > 0) add("main10")
            },
            mainLevel = mainLevel,
            tenBitLevel = main10Level,
            maxWidth = maxWidth,
            maxHeight = maxHeight,
            hdr10 = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N &&
                hasProfile(mime, CodecProfileLevel.HEVCProfileMain10HDR10),
            hdr10Plus = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q &&
                hasProfile(mime, CodecProfileLevel.HEVCProfileMain10HDR10Plus),
            dolbyVision = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N &&
                hasCodecForMime(MediaFormat.MIMETYPE_VIDEO_DOLBY_VISION),
        )
    }

    private fun hasCodecForMime(mime: String): Boolean = codecInfos.any { info ->
        !info.isEncoder && info.supportedTypes.any { it.equals(mime, ignoreCase = true) }
    }

    private fun hasProfile(mime: String, profile: Int): Boolean =
        maxPlatformLevel(mime, setOf(profile)) > 0

    private fun mappedLevel(
        mime: String,
        profiles: Set<Int>,
        levels: List<Pair<Int, Int>>,
    ): Int {
        val platformLevel = maxPlatformLevel(mime, profiles)
        return levels.asReversed().firstOrNull { platformLevel >= it.first }?.second ?: 0
    }

    private fun maxPlatformLevel(mime: String, profiles: Set<Int>): Int {
        var maximum = 0
        codecInfos.asSequence().filterNot { it.isEncoder }.forEach { info ->
            runCatching { info.getCapabilitiesForType(mime) }.getOrNull()
                ?.profileLevels
                ?.filter { it.profile in profiles }
                ?.forEach { maximum = maxOf(maximum, it.level) }
        }
        return maximum
    }

    private fun maxResolution(mime: String): Pair<Int, Int> {
        var maxWidth = 0
        var maxHeight = 0
        codecInfos.asSequence().filterNot { it.isEncoder }.forEach { info ->
            val video = runCatching { info.getCapabilitiesForType(mime).videoCapabilities }.getOrNull()
                ?: return@forEach
            maxWidth = maxOf(maxWidth, video.supportedWidths?.upper ?: 0)
            maxHeight = maxOf(maxHeight, video.supportedHeights?.upper ?: 0)
        }
        return maxWidth to maxHeight
    }

    companion object {
        // ffprobe/Emby represent AVC levels multiplied by 10 (4.1 -> 41).
        private val AVC_LEVELS = listOf(
            CodecProfileLevel.AVCLevel1b to 9,
            CodecProfileLevel.AVCLevel1 to 10,
            CodecProfileLevel.AVCLevel11 to 11,
            CodecProfileLevel.AVCLevel12 to 12,
            CodecProfileLevel.AVCLevel13 to 13,
            CodecProfileLevel.AVCLevel2 to 20,
            CodecProfileLevel.AVCLevel21 to 21,
            CodecProfileLevel.AVCLevel22 to 22,
            CodecProfileLevel.AVCLevel3 to 30,
            CodecProfileLevel.AVCLevel31 to 31,
            CodecProfileLevel.AVCLevel32 to 32,
            CodecProfileLevel.AVCLevel4 to 40,
            CodecProfileLevel.AVCLevel41 to 41,
            CodecProfileLevel.AVCLevel42 to 42,
            CodecProfileLevel.AVCLevel5 to 50,
            CodecProfileLevel.AVCLevel51 to 51,
            CodecProfileLevel.AVCLevel52 to 52,
        )

        // ffprobe/Emby represent HEVC levels multiplied by 30 (4.1 -> 123).
        private val HEVC_LEVELS = listOf(
            CodecProfileLevel.HEVCMainTierLevel1 to 30,
            CodecProfileLevel.HEVCMainTierLevel2 to 60,
            CodecProfileLevel.HEVCMainTierLevel21 to 63,
            CodecProfileLevel.HEVCMainTierLevel3 to 90,
            CodecProfileLevel.HEVCMainTierLevel31 to 93,
            CodecProfileLevel.HEVCMainTierLevel4 to 120,
            CodecProfileLevel.HEVCMainTierLevel41 to 123,
            CodecProfileLevel.HEVCMainTierLevel5 to 150,
            CodecProfileLevel.HEVCMainTierLevel51 to 153,
            CodecProfileLevel.HEVCMainTierLevel52 to 156,
            CodecProfileLevel.HEVCMainTierLevel6 to 180,
            CodecProfileLevel.HEVCMainTierLevel61 to 183,
            CodecProfileLevel.HEVCMainTierLevel62 to 186,
        )
    }
}
