package com.ponzischeme89.memby.ui

import android.graphics.BitmapFactory
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onRoot
import com.github.takahirom.roborazzi.captureRoboImage
import com.ponzischeme89.memby.data.ServiceAlert
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import org.robolectric.annotation.GraphicsMode

/**
 * Renders the aired banner to PNGs under `build/screenshots/service-alert-banner/`, so its layout can be
 * looked at without deploying to a TV.
 *
 * ```powershell
 * .\gradlew.bat :app:testDebugUnitTest --tests "*ServiceAlertBannerScreenshotTest"
 * ```
 *
 * This is the **only** part of `app/src/test` that touches Android — rendering a
 * composable genuinely cannot be done in plain JUnit, so Robolectric is confined to files
 * named `*ScreenshotTest.kt`. Logic tests stay pure; keep them that way.
 *
 * Qualifiers describe a 1080p TV: 960x540dp at xhdpi. The bar is drawn over a realistic
 * cinematic playback still, flush to the top edge exactly as `PlayerActivity` places it.
 *
 * [AlertBanner] is rendered directly rather than [ServiceAlertBanner]: the wrapper's whole
 * job is the drop-in from above, and a still frame of an animation says nothing. Posters
 * are null because there is no network here — which makes these the check on the fallback
 * tile too.
 */
@RunWith(RobolectricTestRunner::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
@Config(sdk = [34], qualifiers = "w960dp-h540dp-television-xhdpi")
class ServiceAlertBannerScreenshotTest {

    @get:Rule
    val compose = createComposeRule()

    @Test
    fun `awaiting download`() {
        capture(
            "alert-banner-awaiting",
            ServiceAlert(
                id = "sonarr:7:42:aired",
                title = "Northbound",
                message = "S02E04 — The Crossing aired at 9:00 PM and will be in Emby soon.",
            ),
        )
    }

    @Test
    fun `downloading now`() {
        capture(
            "alert-banner-downloading",
            ServiceAlert(
                id = "sonarr:8:43:aired",
                title = "The Long Dark",
                message = "S01E09 — Winterlight aired at 8:30 PM and is downloading now.",
            ),
        )
    }

    /**
     * A Radarr import, which is the case with a server-supplied eyebrow: the longest
     * label the bar is expected to carry has to leave the countdown ring room.
     */
    @Test
    fun `new movie added`() {
        capture(
            "alert-banner-movie-added",
            ServiceAlert(
                id = "radarr:412:file:9001",
                title = "Mr. Smith Goes to Washington (1939)",
                message = "Mr. Smith Goes to Washington will be available in Emby shortly.",
                label = "NEW MOVIE ADDED",
            ),
        )
    }

    /** The service itself talking: a finished refresh, with nothing to illustrate it. */
    @Test
    fun `library updated`() {
        capture(
            "alert-banner-library-updated",
            ServiceAlert(
                id = "library:1785012345",
                title = "24 titles added or updated",
                message = "Memby has finished refreshing — it is on the home screen now.",
                label = "LIBRARY UPDATED",
            ),
        )
    }

    /**
     * The alert most likely to be read over a film, and the one whose wording has to work
     * with the picture already stalled behind it.
     */
    @Test
    fun `server not responding`() {
        capture(
            "alert-banner-server-down",
            ServiceAlert(
                id = "emby:down:1785012345",
                title = "Emby has stopped communicating",
                message = "Playback may stop until it is back. Memby will say when it returns.",
                label = "SERVER NOT RESPONDING",
            ),
        )
    }

    /** Both lines ellipsise at one line; this is the case that proves it. */
    @Test
    fun `long title and message`() {
        capture(
            "alert-banner-long-text",
            ServiceAlert(
                id = "sonarr:9:88:aired",
                title = "A Very Long Programme Title That Will Not Fit In One Line",
                message = "S11E03 — The One Where Absolutely Everything Happens At Once " +
                    "And Then Some More Happens After That aired at 10:30 PM and is " +
                    "downloading now.",
            ),
        )
    }

    /** Shortest plausible content: the bar keeps its height and the text stays put. */
    @Test
    fun `short title and message`() {
        capture(
            "alert-banner-short-text",
            ServiceAlert(
                id = "sonarr:3:12:aired",
                title = "Dune",
                message = "S01E01 aired and will be in Emby soon.",
            ),
        )
    }

    /**
     * The countdown ring is full at t=0, which tells you nothing about whether it
     * empties. Holding the clock and stepping it forward captures it mid-sweep.
     */
    @Test
    fun `countdown part way through`() {
        compose.mainClock.autoAdvance = false
        compose.setContent {
            AlertBannerOnHomeBackground(
                ServiceAlert(
                    id = "sonarr:7:42:aired",
                    title = "Northbound",
                    message = "S02E04 — The Crossing aired at 9:00 PM and will be in Emby soon.",
                ),
            )
        }
        compose.mainClock.advanceTimeBy(6_500L)
        compose.onRoot().captureRoboImage(
            "build/screenshots/service-alert-banner/alert-banner-countdown.png",
        )
    }

    private fun capture(name: String, alert: ServiceAlert) {
        compose.setContent { AlertBannerOnHomeBackground(alert) }
        compose.onRoot().captureRoboImage("build/screenshots/service-alert-banner/$name.png")
    }

    @Composable
    private fun AlertBannerOnHomeBackground(alert: ServiceAlert) {
        val playbackStill = requireNotNull(
            javaClass.getResourceAsStream("/playback_alert_preview_still.png"),
        ).use(BitmapFactory::decodeStream).asImageBitmap()
        Box(Modifier.fillMaxSize(), contentAlignment = Alignment.TopCenter) {
            Image(
                bitmap = playbackStill,
                contentDescription = null,
                contentScale = ContentScale.Crop,
                modifier = Modifier.fillMaxSize(),
            )
            AlertBanner(alert)
        }
    }
}
