/**
 * Synor ZK SDK Examples for C
 *
 * Demonstrates zero-knowledge proof operations:
 * - Circuit compilation (Circom)
 * - Proof generation and verification
 * - Multiple proving systems (Groth16, PLONK, STARK)
 * - Recursive proof composition
 * - Trusted setup ceremonies
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <synor/zk.h>

void circuit_example(synor_zk_t* zk) {
    printf("=== Circuit Compilation ===\n");

    synor_error_t err;

    // Simple Circom circuit: prove knowledge of factors
    const char* circom_code =
        "pragma circom 2.1.0;\n"
        "\n"
        "template Multiplier() {\n"
        "    signal input a;\n"
        "    signal input b;\n"
        "    signal output c;\n"
        "\n"
        "    c <== a * b;\n"
        "}\n"
        "\n"
        "component main = Multiplier();\n";

    // Compile the circuit
    printf("Compiling circuit...\n");
    synor_circuit_source_t source = {
        .code = circom_code,
        .language = SYNOR_CIRCUIT_LANGUAGE_CIRCOM
    };

    synor_circuit_t* circuit = NULL;
    err = synor_circuits_compile(zk, &source, &circuit);
    if (err != SYNOR_OK) {
        fprintf(stderr, "Failed to compile circuit\n");
        return;
    }

    printf("Circuit compiled:\n");
    printf("  Circuit ID: %s\n", synor_circuit_get_id(circuit));
    printf("  Constraints: %zu\n", synor_circuit_get_constraints(circuit));
    printf("  Public inputs: %zu\n", synor_circuit_get_public_inputs(circuit));
    printf("  Private inputs: %zu\n", synor_circuit_get_private_inputs(circuit));
    printf("  Outputs: %zu\n", synor_circuit_get_outputs(circuit));

    // Get circuit info
    synor_circuit_info_t* info = NULL;
    err = synor_circuits_get(zk, synor_circuit_get_id(circuit), &info);
    if (err == SYNOR_OK) {
        printf("\nCircuit info:\n");
        printf("  Name: %s\n", synor_circuit_info_get_name(info));
        printf("  Version: %s\n", synor_circuit_info_get_version(info));
        printf("  Wires: %zu\n", synor_circuit_info_get_wire_count(info));
        printf("  Labels: %zu\n", synor_circuit_info_get_label_count(info));
        synor_circuit_info_free(info);
    }

    // List available circuits
    synor_circuit_list_t* circuits = NULL;
    err = synor_circuits_list(zk, &circuits);
    if (err == SYNOR_OK) {
        printf("\nAvailable circuits: %zu\n", synor_circuit_list_count(circuits));
        for (size_t i = 0; i < 3 && i < synor_circuit_list_count(circuits); i++) {
            synor_circuit_t* c = synor_circuit_list_get(circuits, i);
            printf("  %s: %s (%zu constraints)\n",
                   synor_circuit_get_id(c),
                   synor_circuit_get_name(c),
                   synor_circuit_get_constraints(c));
        }
        synor_circuit_list_free(circuits);
    }

    synor_circuit_free(circuit);
    printf("\n");
}

void groth16_example(synor_zk_t* zk) {
    printf("=== Groth16 Proving System ===\n");

    synor_error_t err;
    const char* circuit_id = "multiplier-v1";

    // Generate proving/verification keys (trusted setup)
    printf("Generating keys...\n");
    synor_groth16_keys_t* keys = NULL;
    err = synor_groth16_setup(zk, circuit_id, &keys);
    if (err != SYNOR_OK) {
        fprintf(stderr, "Failed to generate keys\n");
        return;
    }

    printf("Keys generated:\n");
    printf("  Proving key size: %zu bytes\n", synor_groth16_keys_get_pk_size(keys));
    printf("  Verification key size: %zu bytes\n", synor_groth16_keys_get_vk_size(keys));

    // Prepare witness (private inputs)
    synor_witness_t* witness = synor_witness_create();
    synor_witness_set(witness, "a", "3");
    synor_witness_set(witness, "b", "7");

    // Generate proof
    printf("\nGenerating proof...\n");
    synor_prove_request_t prove_req = {
        .circuit_id = circuit_id,
        .witness = witness,
        .proving_key = synor_groth16_keys_get_pk(keys),
        .proving_key_len = synor_groth16_keys_get_pk_size(keys)
    };

    synor_proof_t* proof = NULL;
    err = synor_groth16_prove(zk, &prove_req, &proof);
    if (err != SYNOR_OK) {
        fprintf(stderr, "Failed to generate proof\n");
        synor_witness_free(witness);
        synor_groth16_keys_free(keys);
        return;
    }

    printf("Proof generated:\n");
    printf("  Proof size: %zu bytes\n", synor_proof_get_size(proof));
    printf("  Public signals: %zu\n", synor_proof_get_public_signal_count(proof));
    printf("  Proving time: %lums\n", synor_proof_get_proving_time_ms(proof));

    // Verify proof
    printf("\nVerifying proof...\n");
    synor_verify_request_t verify_req = {
        .proof = synor_proof_get_bytes(proof),
        .proof_len = synor_proof_get_size(proof),
        .public_signals = synor_proof_get_public_signals(proof),
        .public_signal_count = synor_proof_get_public_signal_count(proof),
        .verification_key = synor_groth16_keys_get_vk(keys),
        .verification_key_len = synor_groth16_keys_get_vk_size(keys)
    };

    bool verified;
    err = synor_groth16_verify(zk, &verify_req, &verified);
    printf("Verification result: %s\n", verified ? "true" : "false");

    // Export proof for on-chain verification
    char* solidity_calldata = NULL;
    err = synor_groth16_export_calldata(zk, proof, &solidity_calldata);
    if (err == SYNOR_OK && solidity_calldata) {
        printf("\nSolidity calldata: %.100s...\n", solidity_calldata);
        free(solidity_calldata);
    }

    synor_proof_free(proof);
    synor_witness_free(witness);
    synor_groth16_keys_free(keys);
    printf("\n");
}

void plonk_example(synor_zk_t* zk) {
    printf("=== PLONK Proving System ===\n");

    synor_error_t err;
    const char* circuit_id = "multiplier-v1";

    // PLONK uses universal trusted setup
    printf("Getting universal setup...\n");
    synor_srs_t* srs = NULL;
    err = synor_plonk_get_universal_setup(zk, 14, &srs);  // 2^14 constraints
    if (err != SYNOR_OK) {
        fprintf(stderr, "Failed to get SRS\n");
        return;
    }
    printf("SRS loaded: %zu bytes\n", synor_srs_get_size(srs));

    // Generate circuit-specific keys
    printf("\nGenerating circuit keys...\n");
    synor_plonk_keys_t* keys = NULL;
    err = synor_plonk_setup(zk, circuit_id, srs, &keys);
    if (err != SYNOR_OK) {
        fprintf(stderr, "Failed to generate keys\n");
        synor_srs_free(srs);
        return;
    }
    printf("Keys generated\n");

    // Generate proof
    synor_witness_t* witness = synor_witness_create();
    synor_witness_set(witness, "a", "5");
    synor_witness_set(witness, "b", "9");

    printf("\nGenerating PLONK proof...\n");
    synor_plonk_prove_request_t prove_req = {
        .circuit_id = circuit_id,
        .witness = witness,
        .proving_key = synor_plonk_keys_get_pk(keys),
        .proving_key_len = synor_plonk_keys_get_pk_size(keys)
    };

    synor_proof_t* proof = NULL;
    err = synor_plonk_prove(zk, &prove_req, &proof);
    if (err == SYNOR_OK) {
        printf("Proof generated:\n");
        printf("  Proof size: %zu bytes\n", synor_proof_get_size(proof));
        printf("  Proving time: %lums\n", synor_proof_get_proving_time_ms(proof));

        // Verify proof
        synor_plonk_verify_request_t verify_req = {
            .proof = synor_proof_get_bytes(proof),
            .proof_len = synor_proof_get_size(proof),
            .public_signals = synor_proof_get_public_signals(proof),
            .public_signal_count = synor_proof_get_public_signal_count(proof),
            .verification_key = synor_plonk_keys_get_vk(keys),
            .verification_key_len = synor_plonk_keys_get_vk_size(keys)
        };

        bool verified;
        synor_plonk_verify(zk, &verify_req, &verified);
        printf("Verification result: %s\n", verified ? "true" : "false");

        synor_proof_free(proof);
    }

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

    synor_witness_free(witness);
    synor_plonk_keys_free(keys);
    synor_srs_free(srs);
    printf("\n");
}

void stark_example(synor_zk_t* zk) {
    printf("=== STARK Proving System ===\n");

    synor_error_t err;
    const char* circuit_id = "multiplier-v1";

    // STARKs don't need trusted setup
    printf("Configuring STARK parameters...\n");
    synor_stark_config_t config = {
        .field_size = 256,
        .hash_function = SYNOR_HASH_FUNCTION_POSEIDON,
        .blowup_factor = 8,
        .num_queries = 30,
        .folding_factor = 8
    };

    // Generate proof
    synor_witness_t* witness = synor_witness_create();
    synor_witness_set(witness, "a", "11");
    synor_witness_set(witness, "b", "13");

    printf("\nGenerating STARK proof...\n");
    synor_stark_prove_request_t prove_req = {
        .circuit_id = circuit_id,
        .witness = witness,
        .config = &config
    };

    synor_stark_proof_t* proof = NULL;
    err = synor_stark_prove(zk, &prove_req, &proof);
    if (err == SYNOR_OK) {
        printf("Proof generated:\n");
        printf("  Proof size: %zu bytes\n", synor_stark_proof_get_size(proof));
        printf("  Proving time: %lums\n", synor_stark_proof_get_proving_time_ms(proof));
        printf("  FRI layers: %zu\n", synor_stark_proof_get_fri_layers(proof));

        // Verify proof
        printf("\nVerifying STARK proof...\n");
        synor_stark_verify_request_t verify_req = {
            .proof = synor_stark_proof_get_bytes(proof),
            .proof_len = synor_stark_proof_get_size(proof),
            .public_signals = synor_stark_proof_get_public_signals(proof),
            .public_signal_count = synor_stark_proof_get_public_signal_count(proof),
            .config = &config
        };

        bool verified;
        synor_stark_verify(zk, &verify_req, &verified);
        printf("Verification result: %s\n", verified ? "true" : "false");

        synor_stark_proof_free(proof);
    }

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

    synor_witness_free(witness);
    printf("\n");
}

void recursive_example(synor_zk_t* zk) {
    printf("=== Recursive Proof Composition ===\n");

    synor_error_t err;

    // Create inner proofs
    printf("Generating inner proofs...\n");
    synor_recursive_proof_list_t* inner_proofs = synor_recursive_proof_list_create();

    for (int i = 1; i <= 3; i++) {
        synor_witness_t* witness = synor_witness_create();
        char a_str[16], b_str[16];
        snprintf(a_str, sizeof(a_str), "%d", i);
        snprintf(b_str, sizeof(b_str), "%d", i + 1);
        synor_witness_set(witness, "a", a_str);
        synor_witness_set(witness, "b", b_str);

        synor_recursive_prove_request_t prove_req = {
            .circuit_id = "multiplier-v1",
            .witness = witness,
            .level = 0
        };

        synor_recursive_proof_t* proof = NULL;
        err = synor_recursive_prove_inner(zk, &prove_req, &proof);
        if (err == SYNOR_OK) {
            synor_recursive_proof_list_add(inner_proofs, proof);
            printf("  Inner proof %d generated\n", i);
        }

        synor_witness_free(witness);
    }

    // Aggregate proofs recursively
    printf("\nAggregating proofs...\n");
    synor_aggregate_request_t aggregate_req = {
        .proofs = inner_proofs,
        .aggregation_circuit = "recursive-aggregator-v1"
    };

    synor_aggregated_proof_t* aggregated = NULL;
    err = synor_recursive_aggregate(zk, &aggregate_req, &aggregated);
    if (err == SYNOR_OK) {
        printf("Aggregated proof:\n");
        printf("  Proof size: %zu bytes\n", synor_aggregated_proof_get_size(aggregated));
        printf("  Proofs aggregated: %zu\n", synor_aggregated_proof_get_count(aggregated));
        printf("  Recursion depth: %zu\n", synor_aggregated_proof_get_depth(aggregated));

        // Verify aggregated proof
        printf("\nVerifying aggregated proof...\n");
        bool verified;
        err = synor_recursive_verify_aggregated(zk, aggregated, &verified);
        printf("Verification result: %s\n", verified ? "true" : "false");

        synor_aggregated_proof_free(aggregated);
    }

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

    synor_recursive_proof_list_free(inner_proofs);
    printf("\n");
}

void ceremony_example(synor_zk_t* zk) {
    printf("=== Trusted Setup Ceremony ===\n");

    synor_error_t err;

    // List active ceremonies
    synor_ceremony_list_t* ceremonies = NULL;
    err = synor_ceremony_list(zk, SYNOR_CEREMONY_STATUS_ACTIVE, &ceremonies);
    if (err == SYNOR_OK) {
        printf("Active ceremonies: %zu\n", synor_ceremony_list_count(ceremonies));
        for (size_t i = 0; i < 3 && i < synor_ceremony_list_count(ceremonies); i++) {
            synor_ceremony_t* ceremony = synor_ceremony_list_get(ceremonies, i);
            printf("\n  %s:\n", synor_ceremony_get_id(ceremony));
            printf("    Circuit: %s\n", synor_ceremony_get_circuit_id(ceremony));
            printf("    Participants: %zu\n", synor_ceremony_get_participant_count(ceremony));
            printf("    Current round: %zu\n", synor_ceremony_get_current_round(ceremony));
            printf("    Status: %s\n", synor_ceremony_get_status(ceremony));
        }
        synor_ceremony_list_free(ceremonies);
    }

    // Create a new ceremony
    printf("\nCreating new ceremony...\n");
    synor_ceremony_config_t config = {
        .circuit_id = "new-circuit-v1",
        .min_participants = 10,
        .max_participants = 100,
        .round_duration = 3600,  // 1 hour per round
        .verify_contributions = true
    };

    synor_ceremony_t* new_ceremony = NULL;
    err = synor_ceremony_create(zk, &config, &new_ceremony);
    if (err == SYNOR_OK) {
        printf("Ceremony created:\n");
        printf("  Ceremony ID: %s\n", synor_ceremony_get_id(new_ceremony));
        printf("  Join URL: %s\n", synor_ceremony_get_join_url(new_ceremony));

        // Participate in ceremony
        printf("\nParticipating in ceremony...\n");
        uint8_t entropy[32];
        // In real code, use secure random: arc4random_buf(entropy, sizeof(entropy));
        memset(entropy, 0x42, sizeof(entropy));  // Example only

        synor_contribution_request_t contrib_req = {
            .ceremony_id = synor_ceremony_get_id(new_ceremony),
            .entropy = entropy,
            .entropy_len = sizeof(entropy)
        };

        synor_contribution_t* contribution = NULL;
        err = synor_ceremony_contribute(zk, &contrib_req, &contribution);
        if (err == SYNOR_OK) {
            printf("Contribution made:\n");
            printf("  Contribution ID: %s\n", synor_contribution_get_id(contribution));
            printf("  Position: %zu\n", synor_contribution_get_position(contribution));
            printf("  Hash: %s\n", synor_contribution_get_hash(contribution));

            // Verify contribution
            printf("\nVerifying contribution...\n");
            bool valid;
            synor_ceremony_verify_contribution(zk, synor_contribution_get_id(contribution), &valid);
            printf("Contribution valid: %s\n", valid ? "true" : "false");

            synor_contribution_free(contribution);
        }

        // Get ceremony transcript
        synor_transcript_t* transcript = NULL;
        err = synor_ceremony_get_transcript(zk, synor_ceremony_get_id(new_ceremony), &transcript);
        if (err == SYNOR_OK) {
            printf("\nCeremony transcript:\n");
            printf("  Total contributions: %zu\n", synor_transcript_get_contribution_count(transcript));
            printf("  Start time: %s\n", synor_transcript_get_start_time(transcript));
            const char* final_hash = synor_transcript_get_final_hash(transcript);
            printf("  Final hash: %s\n", final_hash ? final_hash : "pending");
            synor_transcript_free(transcript);
        }

        synor_ceremony_free(new_ceremony);
    }

    printf("\n");
}

int main(int argc, char** argv) {
    synor_error_t err;

    // Initialize client
    synor_zk_config_t config = {
        .api_key = getenv("SYNOR_API_KEY") ? getenv("SYNOR_API_KEY") : "your-api-key",
        .endpoint = "https://zk.synor.io/v1",
        .timeout_ms = 120000,  // ZK operations can be slow
        .retries = 3,
        .debug = false,
        .default_proving_system = SYNOR_PROVING_SYSTEM_GROTH16,
        .prove_timeout_ms = 300000,
        .verify_timeout_ms = 30000
    };

    synor_zk_t* zk = NULL;
    err = synor_zk_init(&config, &zk);
    if (err != SYNOR_OK) {
        fprintf(stderr, "Failed to initialize ZK client: %s\n", synor_error_string(err));
        return 1;
    }

    // Check service health
    bool healthy;
    err = synor_zk_health_check(zk, &healthy);
    printf("Service healthy: %s\n\n", healthy ? "true" : "false");

    // Run examples
    circuit_example(zk);
    groth16_example(zk);
    plonk_example(zk);
    stark_example(zk);
    recursive_example(zk);
    ceremony_example(zk);

    // Cleanup
    synor_zk_free(zk);

    return 0;
}
