/**
 * 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 <iostream>
#include <iomanip>
#include <cstdlib>
#include <random>
#include <synor/zk.hpp>

using namespace synor::zk;

void circuit_example(SynorZk& zk) {
    std::cout << "=== Circuit Compilation ===" << std::endl;

    // Simple Circom circuit: prove knowledge of factors
    std::string circom_code = R"(
        pragma circom 2.1.0;

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

            c <== a * b;
        }

        component main = Multiplier();
    )";

    // Compile the circuit
    std::cout << "Compiling circuit..." << std::endl;
    auto circuit = zk.circuits().compile({
        .code = circom_code,
        .language = CircuitLanguage::Circom
    });

    std::cout << "Circuit compiled:" << std::endl;
    std::cout << "  Circuit ID: " << circuit.circuit_id() << std::endl;
    std::cout << "  Constraints: " << circuit.constraints() << std::endl;
    std::cout << "  Public inputs: " << circuit.public_inputs() << std::endl;
    std::cout << "  Private inputs: " << circuit.private_inputs() << std::endl;
    std::cout << "  Outputs: " << circuit.outputs() << std::endl;

    // Get circuit info
    auto info = zk.circuits().get(circuit.circuit_id());
    std::cout << "\nCircuit info:" << std::endl;
    std::cout << "  Name: " << info.name() << std::endl;
    std::cout << "  Version: " << info.version() << std::endl;
    std::cout << "  Wires: " << info.wire_count() << std::endl;
    std::cout << "  Labels: " << info.label_count() << std::endl;

    // List available circuits
    auto circuits = zk.circuits().list();
    std::cout << "\nAvailable circuits: " << circuits.size() << std::endl;
    for (size_t i = 0; i < std::min(circuits.size(), size_t(3)); ++i) {
        const auto& c = circuits[i];
        std::cout << "  " << c.circuit_id() << ": " << c.name()
                  << " (" << c.constraints() << " constraints)" << std::endl;
    }

    std::cout << std::endl;
}

void groth16_example(SynorZk& zk) {
    std::cout << "=== Groth16 Proving System ===" << std::endl;

    std::string circuit_id = "multiplier-v1";

    // Generate proving/verification keys (trusted setup)
    std::cout << "Generating keys..." << std::endl;
    auto keys = zk.groth16().setup(circuit_id);
    std::cout << "Keys generated:" << std::endl;
    std::cout << "  Proving key size: " << keys.proving_key().size() << " bytes" << std::endl;
    std::cout << "  Verification key size: " << keys.verification_key().size() << " bytes" << std::endl;

    // Prepare witness (private inputs)
    std::map<std::string, std::string> witness = {
        {"a", "3"},
        {"b", "7"}
    };

    // Generate proof
    std::cout << "\nGenerating proof..." << std::endl;
    auto proof = zk.groth16().prove({
        .circuit_id = circuit_id,
        .witness = witness,
        .proving_key = keys.proving_key()
    });

    std::cout << "Proof generated:" << std::endl;
    std::cout << "  Proof size: " << proof.proof_bytes().size() << " bytes" << std::endl;
    std::cout << "  Public signals: " << proof.public_signals().size() << std::endl;
    std::cout << "  Proving time: " << proof.proving_time_ms() << "ms" << std::endl;

    // Verify proof
    std::cout << "\nVerifying proof..." << std::endl;
    bool verified = zk.groth16().verify({
        .proof = proof.proof_bytes(),
        .public_signals = proof.public_signals(),
        .verification_key = keys.verification_key()
    });

    std::cout << "Verification result: " << (verified ? "true" : "false") << std::endl;

    // Export proof for on-chain verification
    auto solidity_calldata = zk.groth16().export_calldata(proof);
    std::cout << "\nSolidity calldata: " << solidity_calldata.substr(0, 100) << "..." << std::endl;

    std::cout << std::endl;
}

void plonk_example(SynorZk& zk) {
    std::cout << "=== PLONK Proving System ===" << std::endl;

    std::string circuit_id = "multiplier-v1";

    // PLONK uses universal trusted setup
    std::cout << "Getting universal setup..." << std::endl;
    auto srs = zk.plonk().get_universal_setup(14);  // 2^14 constraints
    std::cout << "SRS loaded: " << srs.size() << " bytes" << std::endl;

    // Generate circuit-specific keys
    std::cout << "\nGenerating circuit keys..." << std::endl;
    auto keys = zk.plonk().setup(circuit_id, srs);
    std::cout << "Keys generated" << std::endl;

    // Generate proof
    std::map<std::string, std::string> witness = {{"a", "5"}, {"b", "9"}};
    std::cout << "\nGenerating PLONK proof..." << std::endl;
    auto proof = zk.plonk().prove({
        .circuit_id = circuit_id,
        .witness = witness,
        .proving_key = keys.proving_key()
    });

    std::cout << "Proof generated:" << std::endl;
    std::cout << "  Proof size: " << proof.proof_bytes().size() << " bytes" << std::endl;
    std::cout << "  Proving time: " << proof.proving_time_ms() << "ms" << std::endl;

    // Verify proof
    bool verified = zk.plonk().verify({
        .proof = proof.proof_bytes(),
        .public_signals = proof.public_signals(),
        .verification_key = keys.verification_key()
    });
    std::cout << "Verification result: " << (verified ? "true" : "false") << std::endl;

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

    std::cout << std::endl;
}

void stark_example(SynorZk& zk) {
    std::cout << "=== STARK Proving System ===" << std::endl;

    std::string circuit_id = "multiplier-v1";

    // STARKs don't need trusted setup
    std::cout << "Configuring STARK parameters..." << std::endl;
    StarkConfig config{
        .field_size = 256,
        .hash_function = HashFunction::Poseidon,
        .blowup_factor = 8,
        .num_queries = 30,
        .folding_factor = 8
    };

    // Generate proof
    std::map<std::string, std::string> witness = {{"a", "11"}, {"b", "13"}};
    std::cout << "\nGenerating STARK proof..." << std::endl;
    auto proof = zk.stark().prove({
        .circuit_id = circuit_id,
        .witness = witness,
        .config = config
    });

    std::cout << "Proof generated:" << std::endl;
    std::cout << "  Proof size: " << proof.proof_bytes().size() << " bytes" << std::endl;
    std::cout << "  Proving time: " << proof.proving_time_ms() << "ms" << std::endl;
    std::cout << "  FRI layers: " << proof.fri_layers() << std::endl;

    // Verify proof
    std::cout << "\nVerifying STARK proof..." << std::endl;
    bool verified = zk.stark().verify({
        .proof = proof.proof_bytes(),
        .public_signals = proof.public_signals(),
        .config = config
    });

    std::cout << "Verification result: " << (verified ? "true" : "false") << std::endl;

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

    std::cout << std::endl;
}

void recursive_example(SynorZk& zk) {
    std::cout << "=== Recursive Proof Composition ===" << std::endl;

    // Create inner proofs
    std::cout << "Generating inner proofs..." << std::endl;
    std::vector<RecursiveProof> inner_proofs;

    for (int i = 1; i <= 3; ++i) {
        std::map<std::string, std::string> witness = {
            {"a", std::to_string(i)},
            {"b", std::to_string(i + 1)}
        };

        auto proof = zk.recursive().prove_inner({
            .circuit_id = "multiplier-v1",
            .witness = witness,
            .level = 0
        });
        inner_proofs.push_back(proof);
        std::cout << "  Inner proof " << i << " generated" << std::endl;
    }

    // Aggregate proofs recursively
    std::cout << "\nAggregating proofs..." << std::endl;
    auto aggregated_proof = zk.recursive().aggregate({
        .proofs = inner_proofs,
        .aggregation_circuit = "recursive-aggregator-v1"
    });

    std::cout << "Aggregated proof:" << std::endl;
    std::cout << "  Proof size: " << aggregated_proof.proof_bytes().size() << " bytes" << std::endl;
    std::cout << "  Proofs aggregated: " << aggregated_proof.proofs_aggregated() << std::endl;
    std::cout << "  Recursion depth: " << aggregated_proof.recursion_depth() << std::endl;

    // Verify aggregated proof (verifies all inner proofs at once)
    std::cout << "\nVerifying aggregated proof..." << std::endl;
    bool verified = zk.recursive().verify_aggregated(aggregated_proof);
    std::cout << "Verification result: " << (verified ? "true" : "false") << std::endl;

    // Use cases
    std::cout << "\nRecursive proof use cases:" << std::endl;
    std::cout << "  - Rollup batch verification" << std::endl;
    std::cout << "  - Incremental computation proofs" << std::endl;
    std::cout << "  - Cross-chain state proofs" << std::endl;

    std::cout << std::endl;
}

void ceremony_example(SynorZk& zk) {
    std::cout << "=== Trusted Setup Ceremony ===" << std::endl;

    // List active ceremonies
    auto ceremonies = zk.ceremony().list(CeremonyStatus::Active);
    std::cout << "Active ceremonies: " << ceremonies.size() << std::endl;

    for (size_t i = 0; i < std::min(ceremonies.size(), size_t(3)); ++i) {
        const auto& ceremony = ceremonies[i];
        std::cout << "\n  " << ceremony.ceremony_id() << ":" << std::endl;
        std::cout << "    Circuit: " << ceremony.circuit_id() << std::endl;
        std::cout << "    Participants: " << ceremony.participant_count() << std::endl;
        std::cout << "    Current round: " << ceremony.current_round() << std::endl;
        std::cout << "    Status: " << ceremony.status() << std::endl;
    }

    // Create a new ceremony
    std::cout << "\nCreating new ceremony..." << std::endl;
    auto new_ceremony = zk.ceremony().create({
        .circuit_id = "new-circuit-v1",
        .min_participants = 10,
        .max_participants = 100,
        .round_duration = 3600,  // 1 hour per round
        .verify_contributions = true
    });

    std::cout << "Ceremony created:" << std::endl;
    std::cout << "  Ceremony ID: " << new_ceremony.ceremony_id() << std::endl;
    std::cout << "  Join URL: " << new_ceremony.join_url() << std::endl;

    // Participate in a ceremony
    std::cout << "\nParticipating in ceremony..." << std::endl;

    // Generate entropy
    std::vector<uint8_t> entropy(32);
    std::random_device rd;
    std::generate(entropy.begin(), entropy.end(), std::ref(rd));

    auto contribution = zk.ceremony().contribute({
        .ceremony_id = new_ceremony.ceremony_id(),
        .entropy = entropy
    });

    std::cout << "Contribution made:" << std::endl;
    std::cout << "  Contribution ID: " << contribution.contribution_id() << std::endl;
    std::cout << "  Position: " << contribution.position() << std::endl;
    std::cout << "  Hash: " << contribution.hash() << std::endl;

    // Verify a contribution
    std::cout << "\nVerifying contribution..." << std::endl;
    bool valid = zk.ceremony().verify_contribution(contribution.contribution_id());
    std::cout << "Contribution valid: " << (valid ? "true" : "false") << std::endl;

    // Get ceremony transcript (for auditability)
    auto transcript = zk.ceremony().get_transcript(new_ceremony.ceremony_id());
    std::cout << "\nCeremony transcript:" << std::endl;
    std::cout << "  Total contributions: " << transcript.contributions().size() << std::endl;
    std::cout << "  Start time: " << transcript.start_time() << std::endl;
    std::cout << "  Final hash: " << (transcript.final_hash().empty() ? "pending" : transcript.final_hash()) << std::endl;

    std::cout << std::endl;
}

int main(int argc, char** argv) {
    // Initialize client
    const char* api_key = std::getenv("SYNOR_API_KEY");
    ZkConfig config{
        .api_key = api_key ? api_key : "your-api-key",
        .endpoint = "https://zk.synor.io/v1",
        .timeout = std::chrono::seconds(120),  // ZK operations can be slow
        .retries = 3,
        .debug = false,
        .default_proving_system = ProvingSystem::Groth16,
        .prove_timeout = std::chrono::seconds(300),
        .verify_timeout = std::chrono::seconds(30)
    };

    SynorZk zk(config);

    try {
        // Check service health
        bool healthy = zk.health_check();
        std::cout << "Service healthy: " << (healthy ? "true" : "false") << std::endl << std::endl;

        // Run examples
        circuit_example(zk);
        groth16_example(zk);
        plonk_example(zk);
        stark_example(zk);
        recursive_example(zk);
        ceremony_example(zk);
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
        return 1;
    }

    return 0;
}
