package dev.privacyllc.period.core.security

import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec

/**
 * A [MacProvider] backed by an ordinary in-memory key.
 *
 * Stands in for the Android Keystore, which has no Robolectric shadow and
 * therefore cannot exist on the host JVM at all. Everything the real provider
 * contributes to correctness — that the MAC is deterministic, that a different
 * key gives a different answer, that a deleted key stops verifying — is
 * reproduced here; what it cannot reproduce is the *non-exportability* of the
 * key, which is a hardware property and is covered by the instrumented test.
 */
internal class FakeMacProvider(
    private var key: ByteArray? = null,
    private val seed: Byte = 0x7F,
) : MacProvider {

    var ensureCalls = 0
        private set
    var deleteCalls = 0
        private set

    override fun hasKey(): Boolean = key != null

    override fun ensureKey() {
        ensureCalls++
        if (key == null) key = ByteArray(32) { (it + seed).toByte() }
    }

    override fun mac(data: ByteArray): ByteArray {
        val k = key ?: throw IllegalStateException("the verifier key is absent")
        return Mac.getInstance("HmacSHA256")
            .apply { init(SecretKeySpec(k, "HmacSHA256")) }
            .doFinal(data)
    }

    override fun deleteKey() {
        deleteCalls++
        key = null
    }
}

/** Time that only moves when a test moves it. */
internal class FakeClocks(
    var wall: Long = 1_700_000_000_000L,
    var elapsed: Long = 10_000L,
) : Clocks {
    override fun wallMillis(): Long = wall
    override fun elapsedMillis(): Long = elapsed

    /** Both clocks forward together, as real time does. */
    fun advance(millis: Long) {
        wall += millis
        elapsed += millis
    }

    /** What a reboot looks like: elapsed restarts near zero, wall carries on. */
    fun reboot(wallGain: Long = 60_000L) {
        wall += wallGain
        elapsed = 500L
    }
}
