package no.zyon.shade.types

/**
 * Core Shade protocol types. Mirror @shade/core/types.ts.
 *
 * IMPORTANT: byte-for-byte compatibility with the TypeScript version
 * is a hard requirement — the wire format, serialization, and KDF
 * inputs must be identical.
 */

/** Long-term identity: Ed25519 for signing + X25519 for DH */
data class IdentityKeyPair(
    val signingPublicKey: ByteArray,
    val signingPrivateKey: ByteArray,
    val dhPublicKey: ByteArray,
    val dhPrivateKey: ByteArray,
) {
    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (other !is IdentityKeyPair) return false
        return signingPublicKey.contentEquals(other.signingPublicKey) &&
            signingPrivateKey.contentEquals(other.signingPrivateKey) &&
            dhPublicKey.contentEquals(other.dhPublicKey) &&
            dhPrivateKey.contentEquals(other.dhPrivateKey)
    }

    override fun hashCode(): Int {
        var result = signingPublicKey.contentHashCode()
        result = 31 * result + signingPrivateKey.contentHashCode()
        result = 31 * result + dhPublicKey.contentHashCode()
        result = 31 * result + dhPrivateKey.contentHashCode()
        return result
    }
}

/** Generic asymmetric keypair */
data class KeyPair(
    val publicKey: ByteArray,
    val privateKey: ByteArray,
) {
    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (other !is KeyPair) return false
        return publicKey.contentEquals(other.publicKey) &&
            privateKey.contentEquals(other.privateKey)
    }

    override fun hashCode(): Int {
        var result = publicKey.contentHashCode()
        result = 31 * result + privateKey.contentHashCode()
        return result
    }
}

/** Medium-term signed prekey, rotated periodically */
data class SignedPreKey(
    val keyId: Int,
    val keyPair: KeyPair,
    val signature: ByteArray,
    val timestamp: Long,
)

/** Single-use one-time prekey */
data class OneTimePreKey(
    val keyId: Int,
    val keyPair: KeyPair,
)

/** Prekey bundle fetched from the server to initiate a session */
data class PreKeyBundle(
    val registrationId: Int,
    val identitySigningKey: ByteArray,
    val identityDHKey: ByteArray,
    val signedPreKey: BundleSignedPreKey,
    val oneTimePreKey: BundleOneTimePreKey? = null,
) {
    data class BundleSignedPreKey(
        val keyId: Int,
        val publicKey: ByteArray,
        val signature: ByteArray,
    )

    data class BundleOneTimePreKey(
        val keyId: Int,
        val publicKey: ByteArray,
    )
}

/** Chain state (root key ratchet or chain key ratchet) */
data class ChainState(
    var chainKey: ByteArray,
    var counter: Int,
)

/** Full Double Ratchet session state */
data class SessionState(
    var remoteIdentityKey: ByteArray,
    var rootKey: ByteArray,
    var sendChain: ChainState,
    var receiveChain: ChainState?,
    var dhSend: KeyPair,
    var dhReceive: ByteArray?,
    var previousSendCounter: Int,
    val skippedKeys: MutableMap<String, ByteArray>,
)

/** A ratchet-encrypted message */
data class RatchetMessage(
    val dhPublicKey: ByteArray,
    val previousCounter: Int,
    val counter: Int,
    val ciphertext: ByteArray,
    val nonce: ByteArray,
)

/** First message to a new peer (embeds X3DH + RatchetMessage) */
data class PreKeyMessage(
    val registrationId: Int,
    val preKeyId: Int?,
    val signedPreKeyId: Int,
    val ephemeralKey: ByteArray,
    val identityDHKey: ByteArray,
    val message: RatchetMessage,
)

/** Envelope wrapping a wire message */
data class ShadeEnvelope(
    val type: EnvelopeType,
    val content: Any, // PreKeyMessage or RatchetMessage
    val timestamp: Long,
    val senderAddress: String,
) {
    enum class EnvelopeType { PREKEY, RATCHET }
}

/** Max skip constants — must match @shade/core */
object Constants {
    const val MAX_SKIP = 1000
    const val MAX_CACHED_SKIPPED_KEYS = 2000
}
