package com.github.libretube.ui.dialogs

import android.app.Dialog
import android.content.DialogInterface
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.core.os.bundleOf
import androidx.core.view.isGone
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.setFragmentResult
import androidx.lifecycle.lifecycleScope
import com.github.libretube.R
import com.github.libretube.api.MediaServiceRepository
import com.github.libretube.api.obj.PipedStream
import com.github.libretube.api.obj.Streams
import com.github.libretube.constants.IntentData
import com.github.libretube.databinding.DialogDownloadBinding
import com.github.libretube.extensions.TAG
import com.github.libretube.extensions.getWhileDigit
import com.github.libretube.extensions.toastFromMainDispatcher
import com.github.libretube.helpers.DownloadHelper
import com.github.libretube.helpers.PlayerHelper
import com.github.libretube.parcelable.DownloadData
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

/**
 * Intentionally simple download chooser.
 *
 * A normal user only needs to decide whether the item should be kept as audio or video. The app
 * chooses a compact, compatible stream automatically so downloads are useful offline without a
 * technical format/quality dialog.
 */
class DownloadDialog : DialogFragment() {
    private lateinit var videoId: String

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        videoId = arguments?.getString(IntentData.videoId)!!
    }

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        val binding = DialogDownloadBinding.inflate(layoutInflater)

        binding.downloadAudioButton.isEnabled = false
        binding.downloadVideoButton.isEnabled = false
        binding.downloadAudioButton.isGone = true
        binding.downloadVideoButton.isGone = true
        fetchAvailableSources(binding)

        return MaterialAlertDialogBuilder(requireContext())
            .setTitle(R.string.download)
            .setView(binding.root)
            .setNegativeButton(R.string.cancel, null)
            .create()
    }

    private fun fetchAvailableSources(binding: DialogDownloadBinding) {
        lifecycleScope.launch {
            val streams = try {
                withContext(Dispatchers.IO) {
                    MediaServiceRepository.instance.getStreams(videoId)
                }
            } catch (e: Exception) {
                Log.e(TAG(), e.stackTraceToString())
                context?.toastFromMainDispatcher(e.localizedMessage.orEmpty())
                dismissAllowingStateLoss()
                return@launch
            }

            if (!isAdded) return@launch
            bindSimpleChoices(binding, streams)
        }
    }

    private fun bindSimpleChoices(binding: DialogDownloadBinding, streams: Streams) {
        binding.videoTitle.text = streams.title
        binding.downloadLoading.isGone = true
        binding.downloadHint.isGone = false

        val audioStream = selectCompactAudio(streams.audioStreams)
        val videoStream = selectCompactVideo(streams.videoStreams)
        val audioForVideo = if (videoStream?.videoOnly != false) audioStream else null

        binding.downloadAudioButton.apply {
            isGone = audioStream == null
            isEnabled = audioStream != null
            text = getString(R.string.download_audio_simple)
            setOnClickListener {
                audioStream ?: return@setOnClickListener
                startDownload(video = null, audio = audioStream)
            }
        }

        binding.downloadVideoButton.apply {
            isGone = videoStream == null
            isEnabled = videoStream != null
            text = videoStream?.quality?.takeIf { it.isNotBlank() }?.let { quality ->
                getString(R.string.download_video_simple_quality, quality)
            } ?: getString(R.string.download_video_simple)
            setOnClickListener {
                videoStream ?: return@setOnClickListener
                startDownload(video = videoStream, audio = audioForVideo)
            }
        }

        if (audioStream == null && videoStream == null) {
            context?.let { ctx ->
                Toast.makeText(ctx, R.string.downloadfailed, Toast.LENGTH_SHORT).show()
            }
            dismissAllowingStateLoss()
        }
    }

    /**
     * Prefer the primary/original audio track and then the smallest positive bitrate. This keeps
     * audio downloads compact while avoiding secondary/commentary tracks when the backend exposes
     * multiple languages/roles.
     */
    private fun selectCompactAudio(streams: List<PipedStream>): PipedStream? {
        val valid = streams.filter {
            !it.url.isNullOrEmpty() && !it.format.isNullOrEmpty() && !it.quality.isNullOrEmpty()
        }
        if (valid.isEmpty()) return null

        val bestRole = valid.minOfOrNull {
            PlayerHelper.getFullAudioRoleFlags(0, it.audioTrackType.orEmpty())
        }
        val mainTracks = valid.filter {
            PlayerHelper.getFullAudioRoleFlags(0, it.audioTrackType.orEmpty()) == bestRole
        }

        return mainTracks.minWithOrNull(
            compareBy<PipedStream> { it.bitrate?.takeIf { value -> value > 0 } ?: Int.MAX_VALUE }
                .thenBy { it.contentLength.takeIf { size -> size > 0 } ?: Long.MAX_VALUE }
        ) ?: valid.firstOrNull()
    }

    /**
     * Pick the lowest available non-HLS resolution. At that resolution prefer a muxed stream when
     * available (it already contains audio), otherwise download the compact audio track beside the
     * video-only file and the offline player merges both locally.
     */
    private fun selectCompactVideo(streams: List<PipedStream>): PipedStream? {
        val valid = streams.filter {
            !it.url.isNullOrEmpty() &&
                !it.format.isNullOrEmpty() &&
                !it.quality.isNullOrEmpty() &&
                !it.format.orEmpty().contains("HLS", ignoreCase = true)
        }
        if (valid.isEmpty()) return null

        val minimumResolution = valid.mapNotNull { it.height ?: it.quality.getWhileDigit() }
            .filter { it > 0 }
            .minOrNull()

        val lowestResolutionStreams = minimumResolution?.let { minHeight ->
            valid.filter { (it.height ?: it.quality.getWhileDigit()) == minHeight }
        }.orEmpty().ifEmpty { valid }

        return lowestResolutionStreams.minWithOrNull(
            compareBy<PipedStream> { if (it.videoOnly == false) 0 else 1 }
                .thenBy { it.contentLength.takeIf { size -> size > 0 } ?: Long.MAX_VALUE }
                .thenBy { it.bitrate?.takeIf { value -> value > 0 } ?: Int.MAX_VALUE }
        )
    }

    private fun startDownload(video: PipedStream?, audio: PipedStream?) {
        if (video == null && audio == null) return

        val downloadData = DownloadData(
            videoId = videoId,
            videoFormat = video?.format,
            videoQuality = video?.quality,
            audioFormat = audio?.format,
            audioQuality = audio?.quality,
            audioLanguage = audio?.audioTrackLocale,
            subtitleCode = null
        )
        DownloadHelper.startDownloadService(requireContext(), downloadData)
        dismiss()
    }

    override fun onDismiss(dialog: DialogInterface) {
        super.onDismiss(dialog)
        setFragmentResult(DOWNLOAD_DIALOG_DISMISSED_KEY, bundleOf())
    }

    companion object {
        const val DOWNLOAD_DIALOG_DISMISSED_KEY = "download_dialog_dismissed_key"
    }
}
