package io.synor.examples

import io.synor.zk.*
import io.synor.zk.types.*
import kotlinx.coroutines.runBlocking

/**
 * Synor ZK SDK Examples for Kotlin
 *
 * Demonstrates zero-knowledge proof operations:
 * - Circuit compilation (Circom)
 * - Proof generation and verification
 * - Multiple proving systems (Groth16, PLONK, STARK)
 * - Recursive proof composition
 * - Trusted setup ceremonies
 */
fun main() = runBlocking {
    // Initialize client
    val config = ZkConfig(
        apiKey = System.getenv("SYNOR_API_KEY") ?: "your-api-key",
        endpoint = "https://zk.synor.io/v1",
        timeout = 120_000, // ZK operations can be slow
        retries = 3,
        debug = false,
        defaultProvingSystem = ProvingSystem.GROTH16,
        proveTimeout = 300_000,
        verifyTimeout = 30_000
    )

    val zk = SynorZk(config)

    try {
        // Check service health
        val healthy = zk.healthCheck()
        println("Service healthy: $healthy\n")

        // Run examples
        circuitExample(zk)
        groth16Example(zk)
        plonkExample(zk)
        starkExample(zk)
        recursiveExample(zk)
        ceremonyExample(zk)
    } finally {
        zk.close()
    }
}

suspend fun circuitExample(zk: SynorZk) {
    println("=== Circuit Compilation ===")

    // Simple Circom circuit: prove knowledge of factors
    val circomCode = """
        pragma circom 2.1.0;

        template Multiplier() {
            signal input a;
            signal input b;
            signal output c;

            c <== a * b;
        }

        component main = Multiplier();
    """.trimIndent()

    // Compile the circuit
    println("Compiling circuit...")
    val circuit = zk.circuits.compile(
        CircuitSource(
            code = circomCode,
            language = CircuitLanguage.CIRCOM
        )
    )

    println("Circuit compiled:")
    println("  Circuit ID: ${circuit.circuitId}")
    println("  Constraints: ${circuit.constraints}")
    println("  Public inputs: ${circuit.publicInputs}")
    println("  Private inputs: ${circuit.privateInputs}")
    println("  Outputs: ${circuit.outputs}")

    // Get circuit info
    val info = zk.circuits.get(circuit.circuitId)
    println("\nCircuit info:")
    println("  Name: ${info.name}")
    println("  Version: ${info.version}")
    println("  Wires: ${info.wireCount}")
    println("  Labels: ${info.labelCount}")

    // List available circuits
    val circuits = zk.circuits.list()
    println("\nAvailable circuits: ${circuits.size}")
    circuits.take(3).forEach { c ->
        println("  ${c.circuitId}: ${c.name} (${c.constraints} constraints)")
    }

    println()
}

suspend fun groth16Example(zk: SynorZk) {
    println("=== Groth16 Proving System ===")

    // Use a pre-compiled circuit
    val circuitId = "multiplier-v1"

    // Generate proving/verification keys (trusted setup)
    println("Generating keys...")
    val keys = zk.groth16.setup(circuitId)
    println("Keys generated:")
    println("  Proving key size: ${keys.provingKey.size} bytes")
    println("  Verification key size: ${keys.verificationKey.size} bytes")

    // Prepare witness (private inputs)
    val witness = mapOf(
        "a" to "3",
        "b" to "7"
    )

    // Generate proof
    println("\nGenerating proof...")
    val proof = zk.groth16.prove(
        ProveRequest(
            circuitId = circuitId,
            witness = witness,
            provingKey = keys.provingKey
        )
    )

    println("Proof generated:")
    println("  Proof size: ${proof.proofBytes.size} bytes")
    println("  Public signals: ${proof.publicSignals}")
    println("  Proving time: ${proof.provingTimeMs}ms")

    // Verify proof
    println("\nVerifying proof...")
    val verified = zk.groth16.verify(
        VerifyRequest(
            proof = proof.proofBytes,
            publicSignals = proof.publicSignals,
            verificationKey = keys.verificationKey
        )
    )

    println("Verification result: $verified")

    // Export proof for on-chain verification
    val solidityCalldata = zk.groth16.exportCalldata(proof)
    println("\nSolidity calldata: ${solidityCalldata.take(100)}...")

    println()
}

suspend fun plonkExample(zk: SynorZk) {
    println("=== PLONK Proving System ===")

    val circuitId = "multiplier-v1"

    // PLONK uses universal trusted setup
    println("Getting universal setup...")
    val srs = zk.plonk.getUniversalSetup(powersOfTau = 14) // 2^14 constraints
    println("SRS loaded: ${srs.size} bytes")

    // Generate circuit-specific keys
    println("\nGenerating circuit keys...")
    val keys = zk.plonk.setup(circuitId, srs)
    println("Keys generated")

    // Generate proof
    val witness = mapOf("a" to "5", "b" to "9")
    println("\nGenerating PLONK proof...")
    val proof = zk.plonk.prove(
        PlonkProveRequest(
            circuitId = circuitId,
            witness = witness,
            provingKey = keys.provingKey
        )
    )

    println("Proof generated:")
    println("  Proof size: ${proof.proofBytes.size} bytes")
    println("  Proving time: ${proof.provingTimeMs}ms")

    // Verify proof
    val verified = zk.plonk.verify(
        PlonkVerifyRequest(
            proof = proof.proofBytes,
            publicSignals = proof.publicSignals,
            verificationKey = keys.verificationKey
        )
    )
    println("Verification result: $verified")

    // Compare with Groth16
    println("\nPLONK advantages:")
    println("  - Universal trusted setup")
    println("  - Larger proofs (~2.5 KB vs ~200 bytes)")
    println("  - Faster proving for some circuits")

    println()
}

suspend fun starkExample(zk: SynorZk) {
    println("=== STARK Proving System ===")

    val circuitId = "multiplier-v1"

    // STARKs don't need trusted setup
    println("Configuring STARK parameters...")
    val config = StarkConfig(
        fieldSize = 256,
        hashFunction = HashFunction.POSEIDON,
        blowupFactor = 8,
        numQueries = 30,
        foldingFactor = 8
    )

    // Generate proof
    val witness = mapOf("a" to "11", "b" to "13")
    println("\nGenerating STARK proof...")
    val proof = zk.stark.prove(
        StarkProveRequest(
            circuitId = circuitId,
            witness = witness,
            config = config
        )
    )

    println("Proof generated:")
    println("  Proof size: ${proof.proofBytes.size} bytes")
    println("  Proving time: ${proof.provingTimeMs}ms")
    println("  FRI layers: ${proof.friLayers}")

    // Verify proof
    println("\nVerifying STARK proof...")
    val verified = zk.stark.verify(
        StarkVerifyRequest(
            proof = proof.proofBytes,
            publicSignals = proof.publicSignals,
            config = config
        )
    )

    println("Verification result: $verified")

    // Compare with SNARKs
    println("\nSTARK advantages:")
    println("  - No trusted setup needed")
    println("  - Post-quantum secure")
    println("  - Larger proofs (~100 KB)")
    println("  - Faster proving for complex computations")

    println()
}

suspend fun recursiveExample(zk: SynorZk) {
    println("=== Recursive Proof Composition ===")

    // Create inner proofs
    println("Generating inner proofs...")
    val innerProofs = mutableListOf<RecursiveProof>()

    for (i in 1..3) {
        val witness = mapOf("a" to i.toString(), "b" to (i + 1).toString())
        val proof = zk.recursive.proveInner(
            RecursiveProveRequest(
                circuitId = "multiplier-v1",
                witness = witness,
                level = 0
            )
        )
        innerProofs.add(proof)
        println("  Inner proof $i generated")
    }

    // Aggregate proofs recursively
    println("\nAggregating proofs...")
    val aggregatedProof = zk.recursive.aggregate(
        AggregateRequest(
            proofs = innerProofs,
            aggregationCircuit = "recursive-aggregator-v1"
        )
    )

    println("Aggregated proof:")
    println("  Proof size: ${aggregatedProof.proofBytes.size} bytes")
    println("  Proofs aggregated: ${aggregatedProof.proofsAggregated}")
    println("  Recursion depth: ${aggregatedProof.recursionDepth}")

    // Verify aggregated proof (verifies all inner proofs at once)
    println("\nVerifying aggregated proof...")
    val verified = zk.recursive.verifyAggregated(aggregatedProof)
    println("Verification result: $verified")

    // Use cases
    println("\nRecursive proof use cases:")
    println("  - Rollup batch verification")
    println("  - Incremental computation proofs")
    println("  - Cross-chain state proofs")

    println()
}

suspend fun ceremonyExample(zk: SynorZk) {
    println("=== Trusted Setup Ceremony ===")

    // List active ceremonies
    val ceremonies = zk.ceremony.list(status = CeremonyStatus.ACTIVE)
    println("Active ceremonies: ${ceremonies.size}")

    ceremonies.take(3).forEach { ceremony ->
        println("\n  ${ceremony.ceremonyId}:")
        println("    Circuit: ${ceremony.circuitId}")
        println("    Participants: ${ceremony.participantCount}")
        println("    Current round: ${ceremony.currentRound}")
        println("    Status: ${ceremony.status}")
    }

    // Create a new ceremony
    println("\nCreating new ceremony...")
    val newCeremony = zk.ceremony.create(
        CeremonyConfig(
            circuitId = "new-circuit-v1",
            minParticipants = 10,
            maxParticipants = 100,
            roundDuration = 3600, // 1 hour per round
            verifyContributions = true
        )
    )

    println("Ceremony created:")
    println("  Ceremony ID: ${newCeremony.ceremonyId}")
    println("  Join URL: ${newCeremony.joinUrl}")

    // Participate in a ceremony
    println("\nParticipating in ceremony...")
    val contribution = zk.ceremony.contribute(
        ContributionRequest(
            ceremonyId = newCeremony.ceremonyId,
            entropy = generateEntropy()
        )
    )

    println("Contribution made:")
    println("  Contribution ID: ${contribution.contributionId}")
    println("  Position: ${contribution.position}")
    println("  Hash: ${contribution.hash}")

    // Verify a contribution
    println("\nVerifying contribution...")
    val valid = zk.ceremony.verifyContribution(contribution.contributionId)
    println("Contribution valid: $valid")

    // Get ceremony transcript (for auditability)
    val transcript = zk.ceremony.getTranscript(newCeremony.ceremonyId)
    println("\nCeremony transcript:")
    println("  Total contributions: ${transcript.contributions.size}")
    println("  Start time: ${transcript.startTime}")
    println("  Final hash: ${transcript.finalHash ?: "pending"}")

    println()
}

// Helper function to generate random entropy
fun generateEntropy(): ByteArray {
    val entropy = ByteArray(32)
    java.security.SecureRandom().nextBytes(entropy)
    return entropy
}
