package io.synor.privacy

import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.engine.cio.*
import io.ktor.client.plugins.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.client.request.*
import io.ktor.http.*
import io.ktor.serialization.kotlinx.json.*
import kotlinx.coroutines.delay
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.*
import java.io.Closeable
import java.util.concurrent.atomic.AtomicBoolean

/**
 * Synor Privacy SDK client for Kotlin.
 * Confidential transactions, ring signatures, stealth addresses, and cryptographic commitments.
 */
class PrivacyClient(private val config: PrivacyConfig) : Closeable {
    private val closed = AtomicBoolean(false)
    private val json = Json { ignoreUnknownKeys = true }

    private val client = HttpClient(CIO) {
        install(ContentNegotiation) {
            json(json)
        }
        install(HttpTimeout) {
            requestTimeoutMillis = config.timeoutMs
            connectTimeoutMillis = config.timeoutMs
        }
        defaultRequest {
            header("Authorization", "Bearer ${config.apiKey}")
            header("X-SDK-Version", "kotlin/$VERSION")
            contentType(ContentType.Application.Json)
        }
    }

    // ==================== Confidential Transactions ====================

    suspend fun createConfidentialTx(
        inputs: List<ConfidentialTxInput>,
        outputs: List<ConfidentialTxOutput>
    ): ConfidentialTransaction {
        val body = buildJsonObject {
            put("inputs", json.encodeToJsonElement(inputs))
            put("outputs", json.encodeToJsonElement(outputs))
        }
        return post("/privacy/confidential/create", body)
    }

    suspend fun verifyConfidentialTx(tx: ConfidentialTransaction): Boolean {
        val body = buildJsonObject {
            put("transaction", json.encodeToJsonElement(tx))
        }
        val response: JsonObject = post("/privacy/confidential/verify", body)
        return response["valid"]?.jsonPrimitive?.boolean ?: false
    }

    suspend fun createCommitment(value: String, blindingFactor: String): Commitment {
        val body = buildJsonObject {
            put("value", value)
            put("blinding_factor", blindingFactor)
        }
        return post("/privacy/commitment/create", body)
    }

    suspend fun verifyCommitment(commitment: String, value: String, blindingFactor: String): Boolean {
        val body = buildJsonObject {
            put("commitment", commitment)
            put("value", value)
            put("blinding_factor", blindingFactor)
        }
        val response: JsonObject = post("/privacy/commitment/verify", body)
        return response["valid"]?.jsonPrimitive?.boolean ?: false
    }

    suspend fun createRangeProof(
        value: String,
        blindingFactor: String,
        minValue: Long,
        maxValue: Long
    ): RangeProof {
        val body = buildJsonObject {
            put("value", value)
            put("blinding_factor", blindingFactor)
            put("min_value", minValue)
            put("max_value", maxValue)
        }
        return post("/privacy/range-proof/create", body)
    }

    suspend fun verifyRangeProof(proof: RangeProof): Boolean {
        val body = buildJsonObject {
            put("proof", json.encodeToJsonElement(proof))
        }
        val response: JsonObject = post("/privacy/range-proof/verify", body)
        return response["valid"]?.jsonPrimitive?.boolean ?: false
    }

    // ==================== Ring Signatures ====================

    suspend fun createRingSignature(
        message: String,
        ring: List<String>,
        signerIndex: Int,
        privateKey: String
    ): RingSignature {
        val body = buildJsonObject {
            put("message", message)
            put("ring", json.encodeToJsonElement(ring))
            put("signer_index", signerIndex)
            put("private_key", privateKey)
        }
        return post("/privacy/ring/sign", body)
    }

    suspend fun verifyRingSignature(signature: RingSignature, message: String): Boolean {
        val body = buildJsonObject {
            put("signature", json.encodeToJsonElement(signature))
            put("message", message)
        }
        val response: JsonObject = post("/privacy/ring/verify", body)
        return response["valid"]?.jsonPrimitive?.boolean ?: false
    }

    suspend fun generateDecoys(count: Int, excludeKey: String? = null): List<String> {
        var path = "/privacy/ring/decoys?count=$count"
        if (excludeKey != null) path += "&exclude=$excludeKey"
        return get(path)
    }

    suspend fun checkKeyImage(keyImage: String): Boolean {
        val response: JsonObject = get("/privacy/ring/key-image/$keyImage")
        return response["spent"]?.jsonPrimitive?.boolean ?: false
    }

    // ==================== Stealth Addresses ====================

    suspend fun generateStealthKeyPair(): StealthKeyPair {
        return post("/privacy/stealth/generate", buildJsonObject {})
    }

    suspend fun deriveStealthAddress(
        spendPublicKey: String,
        viewPublicKey: String
    ): StealthAddress {
        val body = buildJsonObject {
            put("spend_public_key", spendPublicKey)
            put("view_public_key", viewPublicKey)
        }
        return post("/privacy/stealth/derive", body)
    }

    suspend fun recoverStealthPrivateKey(
        stealthAddress: String,
        viewPrivateKey: String,
        spendPrivateKey: String
    ): String {
        val body = buildJsonObject {
            put("stealth_address", stealthAddress)
            put("view_private_key", viewPrivateKey)
            put("spend_private_key", spendPrivateKey)
        }
        val response: JsonObject = post("/privacy/stealth/recover", body)
        return response["private_key"]?.jsonPrimitive?.content ?: throw PrivacyException("Missing private_key")
    }

    suspend fun scanOutputs(
        viewPrivateKey: String,
        spendPublicKey: String,
        fromBlock: Long,
        toBlock: Long? = null
    ): List<StealthOutput> {
        val body = buildJsonObject {
            put("view_private_key", viewPrivateKey)
            put("spend_public_key", spendPublicKey)
            put("from_block", fromBlock)
            toBlock?.let { put("to_block", it) }
        }
        return post("/privacy/stealth/scan", body)
    }

    // ==================== Blinding ====================

    suspend fun generateBlindingFactor(): String {
        val response: JsonObject = post("/privacy/blinding/generate", buildJsonObject {})
        return response["blinding_factor"]?.jsonPrimitive?.content ?: throw PrivacyException("Missing blinding_factor")
    }

    suspend fun blindValue(value: String, blindingFactor: String): String {
        val body = buildJsonObject {
            put("value", value)
            put("blinding_factor", blindingFactor)
        }
        val response: JsonObject = post("/privacy/blinding/blind", body)
        return response["blinded_value"]?.jsonPrimitive?.content ?: throw PrivacyException("Missing blinded_value")
    }

    suspend fun unblindValue(blindedValue: String, blindingFactor: String): String {
        val body = buildJsonObject {
            put("blinded_value", blindedValue)
            put("blinding_factor", blindingFactor)
        }
        val response: JsonObject = post("/privacy/blinding/unblind", body)
        return response["value"]?.jsonPrimitive?.content ?: throw PrivacyException("Missing value")
    }

    // ==================== Lifecycle ====================

    suspend fun healthCheck(): Boolean {
        if (closed.get()) return false
        return try {
            val response: JsonObject = get("/health")
            response["status"]?.jsonPrimitive?.content == "healthy"
        } catch (e: Exception) {
            false
        }
    }

    override fun close() {
        closed.set(true)
        client.close()
    }

    fun isClosed(): Boolean = closed.get()

    // ==================== HTTP Helpers ====================

    private suspend inline fun <reified T> get(path: String): T {
        checkClosed()
        return executeWithRetry { client.get("${config.endpoint}$path").body() }
    }

    private suspend inline fun <reified T> post(path: String, body: JsonObject): T {
        checkClosed()
        return executeWithRetry {
            client.post("${config.endpoint}$path") {
                setBody(body)
            }.body()
        }
    }

    private suspend inline fun <T> executeWithRetry(crossinline block: suspend () -> T): T {
        var lastException: Exception? = null
        repeat(config.retries) { attempt ->
            try {
                return block()
            } catch (e: Exception) {
                lastException = e
                if (attempt < config.retries - 1) {
                    delay((1L shl attempt) * 1000)
                }
            }
        }
        throw lastException ?: PrivacyException("Request failed")
    }

    private fun checkClosed() {
        if (closed.get()) throw PrivacyException("Client has been closed")
    }

    companion object {
        const val VERSION = "0.1.0"
    }
}

/**
 * Privacy SDK configuration.
 */
data class PrivacyConfig(
    val apiKey: String,
    val endpoint: String = "https://privacy.synor.io",
    val timeoutMs: Long = 30000,
    val retries: Int = 3
)

/**
 * Privacy SDK exception.
 */
class PrivacyException(
    message: String,
    val statusCode: Int? = null,
    val errorCode: String? = null
) : RuntimeException(message)
