package com.ponzischeme89.memby.ui.settings

import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.input.key.Key
import androidx.compose.ui.test.assertIsFocused
import androidx.compose.ui.test.assertIsNotFocused
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onAllNodesWithTag
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onRoot
import androidx.compose.ui.test.performKeyInput
import androidx.compose.ui.test.pressKey
import androidx.compose.ui.test.requestFocus
import com.ponzischeme89.memby.ui.PreviewSurface
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config

@RunWith(RobolectricTestRunner::class)
@Config(sdk = [34], qualifiers = "w960dp-h540dp-television-xhdpi")
@OptIn(androidx.compose.ui.test.ExperimentalTestApi::class)
class SettingsRailFocusTest {

    @get:Rule
    val compose = createComposeRule()

    @Test
    fun `up from about reaches storage`() {
        compose.setContent { Fixture() }
        compose.waitForIdle()

        compose.onNodeWithTag("settings-rail-appearance").requestFocus()
        compose.waitForIdle()
        SettingsPage.entries.drop(1).forEach { page ->
            compose.onRoot().performKeyInput { pressKey(Key.DirectionDown) }
            compose.waitForIdle()
            compose.onNodeWithTag("settings-rail-${page.name.lowercase()}").assertIsFocused()
        }
        compose.onNodeWithTag("settings-rail-about").assertIsFocused()
        // The rail settles onto the page a beat after the focus lands on it; the press
        // worth testing is the one made after the About page is actually drawn.
        compose.waitUntil {
            compose.onAllNodesWithTag("settings-page-about").fetchSemanticsNodes().isNotEmpty()
        }

        compose.onRoot().performKeyInput { pressKey(Key.DirectionUp) }
        compose.waitForIdle()
        compose.onNodeWithTag("settings-rail-storage").assertIsFocused()
    }

    @Test
    fun `up from the top of the about pane leaves the pane`() {
        compose.setContent { Fixture() }
        compose.waitForIdle()

        compose.onNodeWithTag("settings-rail-about").requestFocus()
        compose.waitForIdle()
        compose.onRoot().performKeyInput { pressKey(Key.DirectionRight) }
        compose.waitForIdle()
        val top = MembyReleaseHistory.first().version
        compose.onNodeWithTag("settings-release-$top").assertIsFocused()

        compose.onRoot().performKeyInput { pressKey(Key.DirectionUp) }
        compose.waitForIdle()
        compose.onNodeWithTag("settings-release-$top").assertIsNotFocused()
        compose.onNodeWithTag("settings-rail-about").assertIsFocused()
    }

    @Test
    fun `up from the top of the playback pane leaves the pane`() {
        compose.setContent { Fixture() }
        compose.waitForIdle()

        compose.onNodeWithTag("settings-rail-playback").requestFocus()
        compose.waitForIdle()
        compose.onRoot().performKeyInput { pressKey(Key.DirectionRight) }
        compose.waitForIdle()
        compose.onNodeWithTag("settings-rail-playback").assertIsNotFocused()

        compose.onRoot().performKeyInput { pressKey(Key.DirectionUp) }
        compose.waitForIdle()
        compose.onNodeWithTag("settings-rail-playback").assertIsFocused()
    }

    /** The pane's own vertical navigation is untouched by the escape above it. */
    @Test
    fun `down and up move between rows inside a pane`() {
        compose.setContent { Fixture() }
        compose.waitForIdle()

        compose.onNodeWithTag("settings-rail-about").requestFocus()
        compose.waitForIdle()
        compose.onRoot().performKeyInput { pressKey(Key.DirectionRight) }
        compose.waitForIdle()
        val releases = MembyReleaseHistory
        compose.onRoot().performKeyInput { pressKey(Key.DirectionDown) }
        compose.waitForIdle()
        compose.onNodeWithTag("settings-release-${releases[1].version}").assertIsFocused()

        compose.onRoot().performKeyInput { pressKey(Key.DirectionUp) }
        compose.waitForIdle()
        compose.onNodeWithTag("settings-release-${releases[0].version}").assertIsFocused()
    }

    @Composable
    private fun Fixture() {
        var selectedPage by remember { mutableStateOf(SettingsPage.APPEARANCE) }
        val firstFocus = remember { FocusRequester() }
        PreviewSurface {
            SettingsPanelContent(
                state = SettingsPanelState(
                    selectedPage = selectedPage,
                    installedVersion = "0.1.60",
                    releaseHistory = MembyReleaseHistory,
                ),
                actions = SettingsPanelActions(onPageSelected = { selectedPage = it }),
                overlay = false,
                firstFocusRequester = firstFocus,
            )
        }
    }
}
