package dev.privacyllc.period.feature.lock

import androidx.activity.ComponentActivity
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import dev.privacyllc.period.R
import dev.privacyllc.period.designsystem.PeriodTheme
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config

/**
 * Back, on a screen whose steps the navigation graph knows nothing about.
 *
 * App lock keeps its step in remembered state rather than in a destination, so
 * the system gesture would leave the screen entirely from "choose a PIN" while
 * the arrow beside it stepped back one — two controls a hand's width apart doing
 * different things. Both are asserted here, at both depths.
 *
 * The last assertion is the subtle one: at the Overview the handler must be
 * *disabled*, so the gesture falls through to the NavHost and pops the
 * destination. A handler that stayed enabled and did nothing would be a
 * different trap in the same place.
 */
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [34])
class LockSettingsBackTest {

    @get:Rule val rule = createAndroidComposeRule<ComponentActivity>()

    private val back get() = rule.activity.getString(R.string.action_back)
    private var backs = 0

    private fun setUpAtOverview() {
        rule.setContent {
            PeriodTheme {
                LockSettingsContent(state = LockSettings(hasPin = false), onNavigateBack = { backs++ })
            }
        }
    }

    private fun systemBack() {
        rule.runOnUiThread { rule.activity.onBackPressedDispatcher.onBackPressed() }
        rule.waitForIdle()
    }

    @Test fun `the gesture is left alone at the first step, so it can leave the screen`() {
        setUpAtOverview()

        // Nothing of ours is registered, so back means what it means everywhere
        // else: pop the destination.
        assertFalse(rule.activity.onBackPressedDispatcher.hasEnabledCallbacks())
    }

    @Test fun `the arrow leaves the screen from the first step`() {
        setUpAtOverview()

        rule.onNodeWithContentDescription(back).performClick()

        assertEquals(1, backs)
    }

    @Test fun `inside a step, both the gesture and the arrow step back rather than leaving`() {
        setUpAtOverview()

        rule.onNodeWithText("Set a PIN").performClick()
        rule.onNodeWithText("Before you set a PIN").assertIsDisplayed()
        assertTrue(rule.activity.onBackPressedDispatcher.hasEnabledCallbacks())

        systemBack()

        rule.onNodeWithText("Set a PIN").assertIsDisplayed()
        assertEquals("the gesture left App lock instead of stepping back", 0, backs)
        assertFalse(rule.activity.onBackPressedDispatcher.hasEnabledCallbacks())

        // And the arrow, from the same step, does the same thing.
        rule.onNodeWithText("Set a PIN").performClick()
        rule.onNodeWithContentDescription(back).performClick()
        rule.onNodeWithText("Set a PIN").assertIsDisplayed()
        assertEquals(0, backs)
    }
}
