package io.synor.crypto

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import java.util.Base64

// ============================================================================
// Enumerations
// ============================================================================

@Serializable
enum class Network(val value: String) {
    @SerialName("mainnet") MAINNET("mainnet"),
    @SerialName("testnet") TESTNET("testnet"),
    @SerialName("devnet") DEVNET("devnet");

    companion object {
        fun fromValue(value: String): Network =
            entries.find { it.value == value } ?: MAINNET
    }
}

@Serializable
enum class FalconVariant(
    val value: String,
    val signatureSize: Int,
    val publicKeySize: Int,
    val securityLevel: Int
) {
    @SerialName("falcon512") FALCON512("falcon512", 690, 897, 128),
    @SerialName("falcon1024") FALCON1024("falcon1024", 1330, 1793, 256);

    companion object {
        fun fromValue(value: String): FalconVariant =
            entries.find { it.value == value } ?: FALCON512
    }
}

@Serializable
enum class SphincsVariant(
    val value: String,
    val signatureSize: Int,
    val securityLevel: Int
) {
    @SerialName("shake128s") SHAKE128S("shake128s", 7856, 128),
    @SerialName("shake192s") SHAKE192S("shake192s", 16224, 192),
    @SerialName("shake256s") SHAKE256S("shake256s", 29792, 256);

    companion object {
        fun fromValue(value: String): SphincsVariant =
            entries.find { it.value == value } ?: SHAKE128S
    }
}

@Serializable
enum class PqAlgorithm(val value: String) {
    @SerialName("dilithium3") DILITHIUM3("dilithium3"),
    @SerialName("falcon512") FALCON512("falcon512"),
    @SerialName("falcon1024") FALCON1024("falcon1024"),
    @SerialName("sphincs128s") SPHINCS128S("sphincs128s"),
    @SerialName("sphincs192s") SPHINCS192S("sphincs192s"),
    @SerialName("sphincs256s") SPHINCS256S("sphincs256s")
}

@Serializable
enum class AlgorithmFamily(val value: String) {
    @SerialName("classical") CLASSICAL("classical"),
    @SerialName("lattice") LATTICE("lattice"),
    @SerialName("hash_based") HASH_BASED("hash_based"),
    @SerialName("hybrid") HYBRID("hybrid")
}

// ============================================================================
// Configuration Types
// ============================================================================

data class CryptoConfig(
    val apiKey: String,
    var endpoint: String = "https://crypto.synor.io/v1",
    var timeout: Long = 30000,
    var retries: Int = 3,
    var debug: Boolean = false,
    var defaultNetwork: Network = Network.MAINNET
)

data class DerivationConfig(
    var salt: ByteArray? = null,
    var info: ByteArray? = null,
    var outputLength: Int = 32
) {
    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (other !is DerivationConfig) return false
        return salt.contentEquals(other.salt) && info.contentEquals(other.info) && outputLength == other.outputLength
    }

    override fun hashCode(): Int {
        var result = salt?.contentHashCode() ?: 0
        result = 31 * result + (info?.contentHashCode() ?: 0)
        result = 31 * result + outputLength
        return result
    }
}

data class PasswordDerivationConfig(
    val salt: ByteArray,
    var iterations: Int = 100000,
    var outputLength: Int = 32
) {
    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (other !is PasswordDerivationConfig) return false
        return salt.contentEquals(other.salt) && iterations == other.iterations && outputLength == other.outputLength
    }

    override fun hashCode(): Int {
        var result = salt.contentHashCode()
        result = 31 * result + iterations
        result = 31 * result + outputLength
        return result
    }
}

data class DerivationPath(
    val account: Int,
    val change: Int,
    val index: Int
) {
    companion object {
        const val COIN_TYPE = 0x5359

        fun external(account: Int, index: Int) = DerivationPath(account, 0, index)
        fun internal(account: Int, index: Int) = DerivationPath(account, 1, index)
    }

    override fun toString(): String = "m/44'/$COIN_TYPE'/$account'/$change/$index"

    fun toMap(): Map<String, Int> = mapOf("account" to account, "change" to change, "index" to index)
}

// ============================================================================
// Key Types
// ============================================================================

@Serializable
data class HybridPublicKey(
    val ed25519: String,
    val dilithium: String
) {
    fun getEd25519Bytes(): ByteArray = Base64.getDecoder().decode(ed25519)
    fun getDilithiumBytes(): ByteArray = Base64.getDecoder().decode(dilithium)
    fun size(): Int = getEd25519Bytes().size + getDilithiumBytes().size
    fun toMap(): Map<String, String> = mapOf("ed25519" to ed25519, "dilithium" to dilithium)
}

@Serializable
data class SecretKey(
    @SerialName("ed25519_seed") val ed25519Seed: String,
    @SerialName("master_seed") val masterSeed: String
) {
    fun getEd25519SeedBytes(): ByteArray = Base64.getDecoder().decode(ed25519Seed)
    fun getMasterSeedBytes(): ByteArray = Base64.getDecoder().decode(masterSeed)
    fun toMap(): Map<String, String> = mapOf("ed25519_seed" to ed25519Seed, "master_seed" to masterSeed)
}

@Serializable
data class FalconPublicKey(
    val variant: String,
    val bytes: String
) {
    fun getVariantEnum(): FalconVariant = FalconVariant.fromValue(variant)
    fun getKeyBytes(): ByteArray = Base64.getDecoder().decode(bytes)
}

@Serializable
data class FalconSecretKey(
    val variant: String,
    val bytes: String
) {
    fun getVariantEnum(): FalconVariant = FalconVariant.fromValue(variant)
    fun getKeyBytes(): ByteArray = Base64.getDecoder().decode(bytes)
}

@Serializable
data class SphincsPublicKey(
    val variant: String,
    val bytes: String
) {
    fun getVariantEnum(): SphincsVariant = SphincsVariant.fromValue(variant)
    fun getKeyBytes(): ByteArray = Base64.getDecoder().decode(bytes)
}

@Serializable
data class SphincsSecretKey(
    val variant: String,
    val bytes: String
) {
    fun getVariantEnum(): SphincsVariant = SphincsVariant.fromValue(variant)
    fun getKeyBytes(): ByteArray = Base64.getDecoder().decode(bytes)
}

// ============================================================================
// Signature Types
// ============================================================================

@Serializable
data class HybridSignature(
    val ed25519: String,
    val dilithium: String
) {
    fun getEd25519Bytes(): ByteArray = Base64.getDecoder().decode(ed25519)
    fun getDilithiumBytes(): ByteArray = Base64.getDecoder().decode(dilithium)
    fun size(): Int = getEd25519Bytes().size + getDilithiumBytes().size

    fun toBytes(): ByteArray {
        val e = getEd25519Bytes()
        val d = getDilithiumBytes()
        return e + d
    }

    fun toMap(): Map<String, String> = mapOf("ed25519" to ed25519, "dilithium" to dilithium)
}

@Serializable
data class FalconSignature(
    val variant: String,
    val signature: String
) {
    fun getVariantEnum(): FalconVariant = FalconVariant.fromValue(variant)
    fun getBytes(): ByteArray = Base64.getDecoder().decode(signature)
    fun size(): Int = getBytes().size
}

@Serializable
data class SphincsSignature(
    val variant: String,
    val signature: String
) {
    fun getVariantEnum(): SphincsVariant = SphincsVariant.fromValue(variant)
    fun getBytes(): ByteArray = Base64.getDecoder().decode(signature)
    fun size(): Int = getBytes().size
}

// ============================================================================
// Keypair Types
// ============================================================================

@Serializable
data class HybridKeypair(
    @SerialName("public_key") val publicKey: HybridPublicKey,
    @SerialName("secret_key") val secretKey: SecretKey,
    val addresses: Map<String, String> = emptyMap()
) {
    fun getAddress(network: Network): String = addresses[network.value] ?: ""
}

@Serializable
data class FalconKeypair(
    val variant: String,
    @SerialName("public_key") val publicKey: FalconPublicKey,
    @SerialName("secret_key") val secretKey: FalconSecretKey
) {
    fun getVariantEnum(): FalconVariant = FalconVariant.fromValue(variant)
}

@Serializable
data class SphincsKeypair(
    val variant: String,
    @SerialName("public_key") val publicKey: SphincsPublicKey,
    @SerialName("secret_key") val secretKey: SphincsSecretKey
) {
    fun getVariantEnum(): SphincsVariant = SphincsVariant.fromValue(variant)
}

// ============================================================================
// Mnemonic Types
// ============================================================================

@Serializable
data class Mnemonic(
    val phrase: String,
    val words: List<String>? = null,
    @SerialName("word_count") val wordCount: Int = 0,
    val entropy: String? = null
) {
    fun getWords(): List<String> = words ?: phrase.split(" ")
    fun getWordCount(): Int = if (wordCount > 0) wordCount else getWords().size
    fun getEntropy(): ByteArray = entropy?.let { Base64.getDecoder().decode(it) } ?: ByteArray(0)
}

@Serializable
data class MnemonicValidation(
    val valid: Boolean,
    val error: String? = null
)

// ============================================================================
// Address Types
// ============================================================================

@Serializable
data class Address(
    val address: String,
    val network: String,
    @SerialName("pubkey_hash") val pubkeyHash: String? = null
) {
    fun getNetwork(): Network = Network.fromValue(network)
    fun getPubkeyHashBytes(): ByteArray = pubkeyHash?.let { Base64.getDecoder().decode(it) } ?: ByteArray(0)
}

// ============================================================================
// Hash Types
// ============================================================================

@Serializable
data class Hash256(
    val hash: String
) {
    fun getHex(): String = hash
    fun getBytes(): ByteArray {
        val result = ByteArray(hash.length / 2)
        for (i in result.indices) {
            result[i] = hash.substring(i * 2, i * 2 + 2).toInt(16).toByte()
        }
        return result
    }
}

// ============================================================================
// Error Types
// ============================================================================

class CryptoException(
    message: String,
    val code: String? = null
) : RuntimeException(message)

// ============================================================================
// Constants
// ============================================================================

object CryptoConstants {
    const val ED25519_PUBLIC_KEY_SIZE = 32
    const val ED25519_SECRET_KEY_SIZE = 32
    const val ED25519_SIGNATURE_SIZE = 64
    const val DILITHIUM3_PUBLIC_KEY_SIZE = 1952
    const val DILITHIUM3_SIGNATURE_SIZE = 3293
    const val HYBRID_SIGNATURE_SIZE = 64 + 3293
    const val COIN_TYPE = 0x5359
    const val MIN_PBKDF2_ITERATIONS = 10000
    const val MIN_SALT_LENGTH = 8
    const val DEFAULT_ENDPOINT = "https://crypto.synor.io/v1"
}
