package com.ponzischeme89.memby.ui.player

import android.app.Activity
import android.graphics.BitmapFactory
import android.view.LayoutInflater
import android.view.View
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.LinearLayout
import com.github.takahirom.roborazzi.captureRoboImage
import com.ponzischeme89.memby.R
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.Robolectric
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import org.robolectric.annotation.GraphicsMode

/**
 * Renders the subtitle drop-up to PNGs under `build/screenshots/subtitles-menu/`.
 *
 * ```powershell
 * .\gradlew.bat :app:testDebugUnitTest --tests "*SubtitleMenuScreenshotTest"
 * ```
 *
 * Captured over a playback still, so a near-black panel can be judged against a bright
 * frame. The transport row is drawn underneath even though the real menu hides the
 * controller: it is the thing the panel's bottom margin is measured off, and the gap above
 * it is only checkable if both are on screen. [bindSubtitleMenu] is the same call
 * `PlayerActivity` makes, so these are the real rows rather than a mock-up of them.
 */
@RunWith(RobolectricTestRunner::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
@Config(sdk = [34], qualifiers = "w960dp-h540dp-television-xhdpi")
class SubtitleMenuScreenshotTest {

    /** The ordinary case: a couple of sidecars, one of them on. */
    @Test
    fun `subtitles on with a short track list`() {
        capture(
            name = "subtitles-menu-track-selected",
            tracks = listOf(
                entry("Off"),
                entry("English", selected = true),
                entry("English · SDH"),
                entry("Italian · Forced"),
            ),
            focused = 1,
        )
    }

    /** Subtitles off, which is the first row and the one focus lands on. */
    @Test
    fun `subtitles off`() {
        capture(
            name = "subtitles-menu-off",
            tracks = listOf(entry("Off", selected = true), entry("English"), entry("Français")),
            focused = 0,
        )
    }

    /** Focus in the size chips, the only other thing the menu can be doing. */
    @Test
    fun `text size focused`() {
        capture(
            name = "subtitles-menu-text-size",
            tracks = listOf(entry("Off"), entry("English", selected = true)),
            focusedSize = 2,
        )
    }

    /**
     * A library title with every track Emby holds, plus a burned-in one: the case that
     * decides whether the list scrolls inside the panel instead of growing up the screen.
     */
    @Test
    fun `long track list scrolls inside the panel`() {
        capture(
            name = "subtitles-menu-long-list",
            tracks = listOf(
                entry("Off"),
                entry("English", selected = true),
                entry("English · SDH"),
                entry("Français"),
                entry("Deutsch · Forced"),
                entry("Italiano"),
                entry("Español · Default"),
                entry("Português (Brasil)"),
                entry("Nederlands · SDH · Burned in"),
            ),
            focused = 1,
        )
    }

    /**
     * The download section as a viewer first meets it: one row offering a search, under a
     * track list that has nothing in the language they wanted. This is the case the whole
     * feature exists for, so it is the one worth being able to look at.
     */
    @Test
    fun `download offered with nothing suitable in the list`() {
        capture(
            name = "subtitles-menu-download-offered",
            tracks = listOf(entry("Off", selected = true), entry("Deutsch")),
            downloads = SubtitleDownloadState(
                available = true,
                entries = listOf(entry("Search for subtitles…", prominent = true)),
            ),
        )
    }

    /** A search in flight: the row is drawn but cannot be pressed a second time. */
    @Test
    fun `download searching`() {
        capture(
            name = "subtitles-menu-download-searching",
            tracks = listOf(entry("Off", selected = true)),
            downloads = SubtitleDownloadState(
                available = true,
                status = "Looking for subtitles. This can take a moment.",
                expanded = true,
                entries = listOf(
                    SubtitleMenuEntry("Search for subtitles…", false, enabled = false, prominent = true),
                ),
            ),
        )
    }

    /**
     * Results. Both scrollers are now competing for the panel's height, which is the layout
     * question this capture answers.
     */
    @Test
    fun `download results`() {
        capture(
            name = "subtitles-menu-download-results",
            tracks = listOf(entry("Off", selected = true), entry("Deutsch")),
            downloads = SubtitleDownloadState(
                available = true,
                expanded = true,
                entries = listOf(
                    entry("Search again", prominent = true),
                    entry("English · 98% match"),
                    entry("English · Hearing impaired · 96% match"),
                    entry("English · Forced · 91% match"),
                    entry("Italian · 84% match"),
                ),
            ),
            focusedDownload = 1,
        )
    }

    /** Nothing found, which is an ordinary answer and has to read as one. */
    @Test
    fun `download found nothing`() {
        capture(
            name = "subtitles-menu-download-empty",
            tracks = listOf(entry("Off", selected = true)),
            downloads = SubtitleDownloadState(
                available = true,
                status = "No subtitles were found for this release.",
                expanded = true,
                entries = listOf(entry("Search for subtitles…", prominent = true)),
            ),
        )
    }

    /**
     * A title with no subtitles at all, which is the case the whole feature exists for and
     * the one a capture is worth most on: the track list holds nothing but Off, and whether
     * that reads as "this film has none" or as "the menu is broken" is entirely down to the
     * notice under the heading and where the focus ring landed.
     */
    @Test
    fun `title has no subtitles`() {
        capture(
            name = "subtitles-menu-none-downloadable",
            tracks = listOf(entry("Off", selected = true)),
            tracksState = SubtitleTracksState(empty = true, downloadable = true),
            downloads = SubtitleDownloadState(
                available = true,
                entries = listOf(entry("Find subtitles for this title…", prominent = true)),
            ),
            focusedDownload = 0,
        )
    }

    /** The same title on a backend with no provider: there is nothing to offer, and the
     *  panel says so once rather than leaving somebody looking for an option. */
    @Test
    fun `title has no subtitles and none can be fetched`() {
        capture(
            name = "subtitles-menu-none-unavailable",
            tracks = listOf(entry("Off", selected = true)),
            tracksState = SubtitleTracksState(empty = true, downloadable = false),
        )
    }

    private fun entry(
        label: String,
        selected: Boolean = false,
        prominent: Boolean = false,
    ) = SubtitleMenuEntry(label, selected, prominent = prominent)

    private fun capture(
        name: String,
        tracks: List<SubtitleMenuEntry>,
        focused: Int? = null,
        focusedSize: Int? = null,
        focusedDownload: Int? = null,
        downloads: SubtitleDownloadState = SubtitleDownloadState(),
        tracksState: SubtitleTracksState = SubtitleTracksState(),
    ) {
        val activity = Robolectric.buildActivity(Activity::class.java).setup().get()
        val root = FrameLayout(activity)
        root.addView(
            ImageView(activity).apply {
                scaleType = ImageView.ScaleType.CENTER_CROP
                setImageBitmap(previewArtwork())
            },
            FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.MATCH_PARENT,
            ),
        )
        // The controls the menu drops up from, so the gap above them is visible.
        val controls = FrameLayout(activity)
        root.addView(controls, FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)
        LayoutInflater.from(activity).inflate(R.layout.memby_player_controls, controls, true)
        controls.findViewById<View>(R.id.player_pause_overlay).visibility = View.GONE

        val overlay = LayoutInflater.from(activity)
            .inflate(R.layout.player_subtitle_overlay, root, false)
        root.addView(overlay)
        overlay.visibility = View.VISIBLE
        bindSubtitleMenu(
            overlay = overlay,
            tracks = tracks,
            sizes = listOf(entry("Small"), entry("Medium", selected = true), entry("Large")),
            downloads = downloads,
            tracksState = tracksState,
        )
        activity.setContentView(root)

        val container = when {
            focusedSize != null -> R.id.player_subtitle_sizes
            focusedDownload != null -> R.id.player_subtitle_downloads
            else -> R.id.player_subtitle_tracks
        }
        val index = focusedSize ?: focusedDownload ?: focused
        if (index != null) {
            // Robolectric starts in touch mode, where a focusable row refuses focus and the
            // capture would show every option unfocused. This leaves touch mode the way a
            // D-pad press does.
            val row = requireNotNull(overlay.findViewById<LinearLayout>(container).getChildAt(index))
            row.requestFocusFromTouch()
            check(row.isFocused) { "the row to capture under focus never took it" }
        }

        root.captureRoboImage("build/screenshots/subtitles-menu/$name.png")
    }

    private fun previewArtwork() =
        javaClass.classLoader
            ?.getResourceAsStream("home_hero_preview_art.png")
            ?.use(BitmapFactory::decodeStream)
}
