package wang.yaojia.webterm.tlsandroid

import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyProperties
import android.security.keystore.StrongBoxUnavailableException
import android.util.Log
import java.security.KeyPair
import java.security.KeyPairGenerator
import java.security.KeyStore
import java.security.PrivateKey
import java.security.Signature
import java.security.cert.X509Certificate
import java.security.interfaces.ECPublicKey
import java.security.spec.ECGenParameterSpec
import wang.yaojia.webterm.api.enroll.CsrSigner
import wang.yaojia.webterm.api.enroll.EcPointEncoding

/**
 * B4 · A P-256 signing key that lives ENTIRELY inside AndroidKeyStore and is NON-EXPORTABLE by
 * construction (AndroidKeyStore has no key-material getter). It is the Android analogue of the iOS
 * `SecureEnclaveKey`: `sign` runs inside secure hardware (StrongBox → TEE) and drives the same
 * `Signature("SHA256withECDSA")` path the JVM-unit-test software key uses, so [CsrSigner] callers
 * (`CertificateSigningRequest`) are exercised identically.
 *
 * The wrapped [privateKey] is the opaque AndroidKeyStore handle — presented to the re-reading
 * `X509KeyManager` for the TLS `CertificateVerify` and never exported. [publicKey] is only used to
 * emit the CSR's `SubjectPublicKeyInfo`.
 */
public class HardwareBackedKey internal constructor(
    public val alias: String,
    private val privateKey: PrivateKey,
    private val publicKey: ECPublicKey,
) : CsrSigner {

    override fun publicKeyX963(): ByteArray = EcPointEncoding.x963(publicKey)

    override fun sign(message: ByteArray): ByteArray =
        Signature.getInstance(SIGNATURE_ALGORITHM).apply {
            initSign(privateKey)
            update(message)
        }.sign()

    /** The opaque, non-exportable AndroidKeyStore private-key handle presented on the mTLS path. */
    public val keyHandle: PrivateKey get() = privateKey

    public companion object {
        private const val SIGNATURE_ALGORITHM = "SHA256withECDSA"
    }
}

/**
 * Creates / loads / deletes the device's non-exportable P-256 key in AndroidKeyStore.
 *
 * Generation prefers **StrongBox** (dedicated secure element) and falls back to the **TEE** when the
 * device has no StrongBox — the security posture (non-exportable, hardware-backed, silent-signing)
 * is identical either way; StrongBox is a hardening bonus, not a requirement. The key is
 * `PURPOSE_SIGN` only with a broad digest set so TLS 1.2/1.3 signature negotiation for the client
 * `CertificateVerify` works, and NO user-authentication is required so silent enroll/renew never
 * blocks on a biometric prompt.
 */
public object HardwareKeyStore {
    private const val TAG = "HardwareKeyStore"
    private const val ANDROID_KEYSTORE = "AndroidKeyStore"
    private const val CURVE = "secp256r1"

    /**
     * Generate a fresh non-exportable P-256 key at [alias], overwriting any prior entry there.
     * StrongBox-backed when available, else TEE-backed. Throws the underlying keystore exception if
     * BOTH paths fail (never returns a half-generated key).
     */
    public fun generate(alias: String): HardwareBackedKey {
        val keyPair = try {
            generateKeyPair(alias, strongBox = true)
        } catch (_: StrongBoxUnavailableException) {
            Log.i(TAG, "StrongBox unavailable; generating a TEE-backed device key (non-exportable)")
            delete(alias) // clear any partial StrongBox entry before the TEE retry
            generateKeyPair(alias, strongBox = false)
        }
        return HardwareBackedKey(alias, keyPair.private, keyPair.public as ECPublicKey)
    }

    /**
     * Load a previously-generated key by [alias] (renew path, after relaunch). The public key is
     * recovered from the self-signed placeholder certificate AndroidKeyStore stored at generation.
     * Returns null if no key entry exists (the normal pre-enroll state).
     */
    public fun load(alias: String): HardwareBackedKey? {
        val keyStore = androidKeyStore()
        val privateKey = keyStore.getKey(alias, null) as? PrivateKey ?: return null
        val publicKey = (keyStore.getCertificate(alias) as? X509Certificate)?.publicKey as? ECPublicKey
            ?: return null
        return HardwareBackedKey(alias, privateKey, publicKey)
    }

    /** Delete the key entry at [alias]. Idempotent (a missing alias is a no-op). */
    public fun delete(alias: String) {
        val keyStore = androidKeyStore()
        if (keyStore.containsAlias(alias)) keyStore.deleteEntry(alias)
    }

    /** Cheap existence check (does NOT read key material). */
    public fun exists(alias: String): Boolean = androidKeyStore().containsAlias(alias)

    private fun generateKeyPair(alias: String, strongBox: Boolean): KeyPair {
        val spec = KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_SIGN)
            .setAlgorithmParameterSpec(ECGenParameterSpec(CURVE))
            .setDigests(
                KeyProperties.DIGEST_NONE,
                KeyProperties.DIGEST_SHA256,
                KeyProperties.DIGEST_SHA384,
                KeyProperties.DIGEST_SHA512,
            )
            .setIsStrongBoxBacked(strongBox)
            .build()
        val generator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC, ANDROID_KEYSTORE)
        generator.initialize(spec)
        return generator.generateKeyPair()
    }

    private fun androidKeyStore(): KeyStore =
        KeyStore.getInstance(ANDROID_KEYSTORE).apply { load(null) }
}
