package com.tt.honeyDue.ui.screens

import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue

/**
 * P6 Stream T — unit tests for [BiometricLockState].
 *
 * Pure-Kotlin state-machine tests (no Android/iOS dependencies).
 */
class BiometricLockScreenStateTest {

    @Test
    fun initialState_onAppearAvailable_triggersPromptAndMovesToPrompting() {
        var unlockCalls = 0
        val state = BiometricLockState(onUnlock = { unlockCalls++ })

        val shouldPrompt = state.onAppear(BiometricLockState.Availability.AVAILABLE)

        assertTrue(shouldPrompt, "onAppear should return true when biometrics are available")
        assertEquals(BiometricLockState.Phase.PROMPTING, state.phase)
        assertEquals(0, unlockCalls, "unlock must not fire before biometric success")
    }

    @Test
    fun onAppear_noHardware_bypassesLockAndInvokesUnlock() {
        var unlockCalls = 0
        val state = BiometricLockState(onUnlock = { unlockCalls++ })

        val shouldPrompt = state.onAppear(BiometricLockState.Availability.NO_HARDWARE)

        assertFalse(shouldPrompt, "NO_HARDWARE should skip the prompt")
        assertEquals(BiometricLockState.Phase.BYPASSED, state.phase)
        assertEquals(1, unlockCalls, "NO_HARDWARE should release the lock exactly once")
    }

    @Test
    fun threeConsecutiveFailures_surfaceFallbackPinUi() {
        val state = BiometricLockState(onUnlock = {})
        state.onAppear(BiometricLockState.Availability.AVAILABLE)

        assertFalse(state.onBiometricFailure(), "1st failure should not surface fallback")
        assertFalse(state.onBiometricFailure(), "2nd failure should not surface fallback")
        val thirdCrossedThreshold = state.onBiometricFailure()

        assertTrue(thirdCrossedThreshold, "3rd failure should cross 3-strike threshold")
        assertEquals(BiometricLockState.Phase.FALLBACK_PIN, state.phase)
        assertEquals(3, state.failureCount)
    }

    @Test
    fun onBiometricSuccess_invokesUnlockCallbackAndMarksUnlocked() {
        var unlockCalls = 0
        val state = BiometricLockState(onUnlock = { unlockCalls++ })
        state.onAppear(BiometricLockState.Availability.AVAILABLE)

        state.onBiometricSuccess()

        assertEquals(1, unlockCalls, "onUnlock must be invoked exactly once")
        assertEquals(BiometricLockState.Phase.UNLOCKED, state.phase)
        assertEquals(0, state.failureCount)
    }

    @Test
    fun onBiometricSuccess_idempotent_doesNotDoubleInvokeUnlock() {
        var unlockCalls = 0
        val state = BiometricLockState(onUnlock = { unlockCalls++ })
        state.onAppear(BiometricLockState.Availability.AVAILABLE)

        state.onBiometricSuccess()
        state.onBiometricSuccess()

        assertEquals(1, unlockCalls, "unlock callback must not double-fire")
    }

    @Test
    fun onRetry_afterThreeFailures_staysOnFallbackAndReturnsFalse() {
        val state = BiometricLockState(onUnlock = {})
        state.onAppear(BiometricLockState.Availability.AVAILABLE)
        repeat(3) { state.onBiometricFailure() }

        val retriedSuccessfully = state.onRetry()

        assertFalse(retriedSuccessfully, "retry after lockout must not resume prompt")
        assertEquals(BiometricLockState.Phase.FALLBACK_PIN, state.phase)
    }

    @Test
    fun onPinEntered_correctPin_unlocks() {
        var unlockCalls = 0
        val state = BiometricLockState(onUnlock = { unlockCalls++ })
        state.onAppear(BiometricLockState.Availability.AVAILABLE)
        repeat(3) { state.onBiometricFailure() }

        val accepted = state.onPinEntered("1234", expectedPin = "1234")

        assertTrue(accepted)
        assertEquals(1, unlockCalls)
        assertEquals(BiometricLockState.Phase.UNLOCKED, state.phase)
    }

    @Test
    fun onPinEntered_wrongPin_keepsFallbackVisibleAndDoesNotUnlock() {
        var unlockCalls = 0
        val state = BiometricLockState(onUnlock = { unlockCalls++ })
        state.onAppear(BiometricLockState.Availability.AVAILABLE)
        repeat(3) { state.onBiometricFailure() }

        val accepted = state.onPinEntered("0000", expectedPin = "1234")

        assertFalse(accepted)
        assertEquals(0, unlockCalls)
        assertEquals(BiometricLockState.Phase.FALLBACK_PIN, state.phase)
    }
}
