package wang.yaojia.webterm.tlsandroid

import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.DataInputStream
import java.io.DataOutputStream
import java.io.IOException
import java.security.GeneralSecurityException
import java.security.cert.CertificateException
import java.security.cert.CertificateFactory
import java.security.cert.X509Certificate

/**
 * The at-rest description of the installed device identity — and the single source-of-truth "live
 * pointer" for the whole repository. It carries the [alias] + [keyAlgorithm] the pure
 * `ClientKeyManagerLogic` needs for alias selection, the DER cert [certificateChain] (leaf at index 0)
 * presented in the TLS handshake, AND the [keyStoreAlias] naming which physical AndroidKeyStore slot
 * holds the live private key. Persisting a new blob (one atomic [TinkCertStore.save]) is the COMMIT
 * that flips which identity is live; startup and the re-reading KeyManager resolve the live identity
 * SOLELY through this pointer (see [AndroidIdentityRepository]).
 *
 * The PRIVATE KEY is deliberately NOT here — it lives non-exportable in AndroidKeyStore (the one key
 * home, plan §2/§8). This blob is exactly what [TinkCertStore] AEAD-encrypts at rest; no `.p12` bytes
 * and no passphrase are ever persisted.
 */
public data class StoredIdentityMetadata(
    val alias: String,
    val keyAlgorithm: String,
    val keyStoreAlias: String,
    val certificateChain: List<X509Certificate>,
) {
    init {
        require(keyStoreAlias.isNotBlank()) { "keyStoreAlias (live-key slot) must not be blank" }
        require(certificateChain.isNotEmpty()) { "certificateChain must contain at least the leaf" }
    }

    /** The leaf (device) certificate — chain[0]. */
    public val leafCertificate: X509Certificate get() = certificateChain.first()

    /** Issuer certificates accompanying the leaf (chain minus leaf); may be empty. */
    public val issuerCertificates: List<X509Certificate> get() = certificateChain.drop(1)
}

/** The stored identity blob was present but could not be decoded (tamper / version skew / corruption). */
public class CorruptStoredIdentityException(message: String, cause: Throwable? = null) :
    GeneralSecurityException(message, cause)

/**
 * Length-prefixed binary codec for [StoredIdentityMetadata] (KISS: no serialization framework — the
 * cert chain is `X509Certificate`, not a plain data type). Certificates are stored as their DER
 * encoding and re-parsed with the JVM `CertificateFactory`.
 *
 * Layout: `UTF(alias) · UTF(keyAlgorithm) · UTF(keyStoreAlias) · Int(chainCount) · [Int(derLen) · derBytes]*`.
 */
public object IdentityMetadataCodec {
    private const val X509 = "X.509"
    private const val MAX_CHAIN = 32
    private const val MAX_DER_BYTES = 1 shl 20 // 1 MiB per cert — a defensive upper bound.

    public fun encode(metadata: StoredIdentityMetadata): ByteArray {
        val out = ByteArrayOutputStream()
        DataOutputStream(out).use { data ->
            data.writeUTF(metadata.alias)
            data.writeUTF(metadata.keyAlgorithm)
            data.writeUTF(metadata.keyStoreAlias)
            data.writeInt(metadata.certificateChain.size)
            metadata.certificateChain.forEach { cert ->
                val der = cert.encoded
                data.writeInt(der.size)
                data.write(der)
            }
        }
        return out.toByteArray()
    }

    /** Decode [bytes]; any structural or certificate-parse failure → [CorruptStoredIdentityException]. */
    public fun decode(bytes: ByteArray): StoredIdentityMetadata =
        try {
            DataInputStream(ByteArrayInputStream(bytes)).use { data ->
                val alias = data.readUTF()
                val keyAlgorithm = data.readUTF()
                val keyStoreAlias = data.readUTF()
                val count = data.readInt()
                if (count !in 1..MAX_CHAIN) {
                    throw CorruptStoredIdentityException("Implausible chain length: $count")
                }
                val factory = CertificateFactory.getInstance(X509)
                val chain = (0 until count).map { readCertificate(data, factory) }
                StoredIdentityMetadata(alias, keyAlgorithm, keyStoreAlias, chain)
            }
        } catch (e: CorruptStoredIdentityException) {
            throw e
        } catch (e: IOException) {
            throw CorruptStoredIdentityException("Stored identity blob was truncated/malformed", e)
        } catch (e: CertificateException) {
            throw CorruptStoredIdentityException("Stored identity certificate could not be parsed", e)
        }

    private fun readCertificate(data: DataInputStream, factory: CertificateFactory): X509Certificate {
        val len = data.readInt()
        if (len !in 1..MAX_DER_BYTES) {
            throw CorruptStoredIdentityException("Implausible certificate length: $len")
        }
        val der = ByteArray(len)
        data.readFully(der)
        return factory.generateCertificate(ByteArrayInputStream(der)) as X509Certificate
    }
}
