package com.ponzischeme89.memby.ui.calendar

import androidx.compose.runtime.Composable
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onRoot
import androidx.test.core.app.ApplicationProvider
import com.github.takahirom.roborazzi.captureRoboImage
import com.ponzischeme89.memby.ServiceLocator
import com.ponzischeme89.memby.data.model.BaseItem
import com.ponzischeme89.memby.data.model.GatewayCalendar
import com.ponzischeme89.memby.data.model.GatewayCalendarDay
import org.junit.Before
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

/**
 * The TV calendar as it actually sits on a television, to `build/screenshots/tv-calendar/`.
 *
 * ```powershell
 * .\gradlew.bat :app:testDebugUnitTest --tests "*CalendarScreenshotTest"
 * ```
 *
 * A unit test can check that thirty-one days lay out as six weeks; it cannot check whether
 * a cell two centimetres across can carry a day number, two show titles and a "+2 more"
 * without any of them becoming unreadable, which is the whole question this layout has. So
 * the busy month is the one worth looking at: a household that follows twenty shows is what
 * this page is for, and an empty February proves nothing about it.
 *
 * Poster artwork is injected as absent rather than stubbed. There is no network here, and a
 * row that reads correctly with no picture in it is the state a title Sonarr has no cover
 * for is drawn in anyway.
 */
@RunWith(RobolectricTestRunner::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
@Config(sdk = [34], qualifiers = "w960dp-h540dp-television-xhdpi")
class CalendarScreenshotTest {

    @get:Rule
    val compose = createComposeRule()

    @Before
    fun locator() {
        ServiceLocator.init(ApplicationProvider.getApplicationContext())
    }

    /** A month with something on most weeks, opened on today — the ordinary case. */
    @Test
    fun `a busy month`() {
        capture("calendar_busy-month") { CalendarPage(busyMonth()) }
    }

    /** The day panel filled past what one cell could ever show. */
    @Test
    fun `a crowded day`() {
        capture("calendar_crowded-day") {
            CalendarPage(busyMonth(), selectedDate = "2026-08-12")
        }
    }

    /** Nothing scheduled: the grid still reads as a month rather than as a failure. */
    @Test
    fun `a quiet month`() {
        capture("calendar_quiet-month") {
            CalendarPage(
                CalendarUiState(
                    calendar = august(days = emptyList()),
                    selectedDate = "2026-08-11",
                    isLoading = false,
                ),
            )
        }
    }

    /** No gateway, or no Sonarr behind it. The page says so instead of drawing a month. */
    @Test
    fun `no calendar available`() {
        capture("calendar_unavailable") {
            CalendarPage(CalendarUiState(calendar = GatewayCalendar(), isLoading = false))
        }
    }

    @Composable
    private fun CalendarPage(state: CalendarUiState, selectedDate: String = state.selectedDate) {
        CalendarContent(
            state = state.copy(selectedDate = selectedDate),
            navigationFocusRequester = FocusRequester(),
            contentFocusRequester = FocusRequester(),
            onShowMonth = {},
            onSelectDate = {},
            onRetry = {},
            onItemFocused = {},
            onItemSelected = {},
            onExit = {},
            posterUrlFor = { null },
        )
    }

    private fun capture(name: String, content: @Composable () -> Unit) {
        compose.setContent(content)
        compose.onRoot().captureRoboImage("build/screenshots/tv-calendar/$name.png")
    }

    private fun busyMonth() = CalendarUiState(
        calendar = august(
            days = listOf(
                day(3, episode("Northbound", "S02E04", "The Crossing", "Monday: 8:30 PM")),
                day(
                    12,
                    episode("Northbound", "S02E05", "Slack Water", "Wednesday: 8:30 PM", "Season finale"),
                    episode("Harbour Lights", "S01E02", "Low Tide", "Wednesday: 9:00 PM"),
                    episode("The Long Paddock", "S04E11", "Shearing", "Wednesday: 9:30 PM"),
                    episode("Deep Water", "S03E01", "First Light", "Wednesday: 10:00 PM", "Season premiere"),
                    episode("Kererū", "S01E06", "The Nesting", "Wednesday: 10:30 PM"),
                ),
                day(
                    18,
                    episode("Harbour Lights", "S01E03", "Spring Tide", "Tuesday: 9:00 PM"),
                    episode("Deep Water", "S03E02", "The Drop", "Tuesday: 10:00 PM"),
                ),
                day(26, episode("The Long Paddock", "S04E12", "Muster", "Wednesday: 9:30 PM")),
            ),
        ),
        selectedDate = "2026-08-11",
        isLoading = false,
    )

    // August 2026: 31 days, beginning on a Saturday.
    private fun august(days: List<GatewayCalendarDay>) = GatewayCalendar(
        available = true,
        month = "2026-08",
        label = "August 2026",
        previous = "2026-07",
        next = "2026-09",
        today = "2026-08-11",
        firstWeekday = 6,
        dayCount = 31,
        days = days,
    )

    private fun day(number: Int, vararg items: BaseItem) = GatewayCalendarDay(
        date = "2026-08-" + if (number < 10) "0$number" else "$number",
        day = number,
        items = items.toList(),
    )

    private fun episode(
        series: String,
        code: String,
        title: String,
        airs: String,
        event: String? = null,
    ) = BaseItem(
        id = "sonarr:$series:$code",
        name = series,
        type = "MembySonarrEpisode",
        membySource = "sonarr",
        membyEpisodeCode = code,
        membyEpisodeTitle = title,
        membyAirLabel = airs,
        membyEpisodeEvent = event,
        membyAvailabilityText = "Upcoming",
        membyPlayable = false,
    )
}
