# frozen_string_literal: true

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

require 'securerandom'
require 'synor/zk'

class ZkExample
  def initialize(zk)
    @zk = zk
  end

  def run_circuit_example
    puts '=== Circuit Compilation ==='

    # Simple Circom circuit: prove knowledge of factors
    circom_code = <<~CIRCOM
      pragma circom 2.1.0;

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

          c <== a * b;
      }

      component main = Multiplier();
    CIRCOM

    # Compile the circuit
    puts 'Compiling circuit...'
    circuit = @zk.circuits.compile(
      code: circom_code,
      language: :circom
    )

    puts 'Circuit compiled:'
    puts "  Circuit ID: #{circuit.circuit_id}"
    puts "  Constraints: #{circuit.constraints}"
    puts "  Public inputs: #{circuit.public_inputs}"
    puts "  Private inputs: #{circuit.private_inputs}"
    puts "  Outputs: #{circuit.outputs}"

    # Get circuit info
    info = @zk.circuits.get(circuit.circuit_id)
    puts "\nCircuit info:"
    puts "  Name: #{info.name}"
    puts "  Version: #{info.version}"
    puts "  Wires: #{info.wire_count}"
    puts "  Labels: #{info.label_count}"

    # List available circuits
    circuits = @zk.circuits.list
    puts "\nAvailable circuits: #{circuits.length}"
    circuits.first(3).each do |c|
      puts "  #{c.circuit_id}: #{c.name} (#{c.constraints} constraints)"
    end
    puts
  end

  def run_groth16_example
    puts '=== Groth16 Proving System ==='

    circuit_id = 'multiplier-v1'

    # Generate proving/verification keys (trusted setup)
    puts 'Generating keys...'
    keys = @zk.groth16.setup(circuit_id)
    puts 'Keys generated:'
    puts "  Proving key size: #{keys.proving_key.length} bytes"
    puts "  Verification key size: #{keys.verification_key.length} bytes"

    # Prepare witness (private inputs)
    witness = { 'a' => '3', 'b' => '7' }

    # Generate proof
    puts "\nGenerating proof..."
    proof = @zk.groth16.prove(
      circuit_id: circuit_id,
      witness: witness,
      proving_key: keys.proving_key
    )

    puts 'Proof generated:'
    puts "  Proof size: #{proof.proof_bytes.length} bytes"
    puts "  Public signals: #{proof.public_signals.length}"
    puts "  Proving time: #{proof.proving_time_ms}ms"

    # Verify proof
    puts "\nVerifying proof..."
    verified = @zk.groth16.verify(
      proof: proof.proof_bytes,
      public_signals: proof.public_signals,
      verification_key: keys.verification_key
    )

    puts "Verification result: #{verified}"

    # Export proof for on-chain verification
    solidity_calldata = @zk.groth16.export_calldata(proof)
    puts "\nSolidity calldata: #{solidity_calldata[0, 100]}..."
    puts
  end

  def run_plonk_example
    puts '=== PLONK Proving System ==='

    circuit_id = 'multiplier-v1'

    # PLONK uses universal trusted setup
    puts 'Getting universal setup...'
    srs = @zk.plonk.get_universal_setup(14) # 2^14 constraints
    puts "SRS loaded: #{srs.length} bytes"

    # Generate circuit-specific keys
    puts "\nGenerating circuit keys..."
    keys = @zk.plonk.setup(circuit_id, srs)
    puts 'Keys generated'

    # Generate proof
    witness = { 'a' => '5', 'b' => '9' }
    puts "\nGenerating PLONK proof..."
    proof = @zk.plonk.prove(
      circuit_id: circuit_id,
      witness: witness,
      proving_key: keys.proving_key
    )

    puts 'Proof generated:'
    puts "  Proof size: #{proof.proof_bytes.length} bytes"
    puts "  Proving time: #{proof.proving_time_ms}ms"

    # Verify proof
    verified = @zk.plonk.verify(
      proof: proof.proof_bytes,
      public_signals: proof.public_signals,
      verification_key: keys.verification_key
    )
    puts "Verification result: #{verified}"

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

  def run_stark_example
    puts '=== STARK Proving System ==='

    circuit_id = 'multiplier-v1'

    # STARKs don't need trusted setup
    puts 'Configuring STARK parameters...'
    config = {
      field_size: 256,
      hash_function: :poseidon,
      blowup_factor: 8,
      num_queries: 30,
      folding_factor: 8
    }

    # Generate proof
    witness = { 'a' => '11', 'b' => '13' }
    puts "\nGenerating STARK proof..."
    proof = @zk.stark.prove(
      circuit_id: circuit_id,
      witness: witness,
      config: config
    )

    puts 'Proof generated:'
    puts "  Proof size: #{proof.proof_bytes.length} bytes"
    puts "  Proving time: #{proof.proving_time_ms}ms"
    puts "  FRI layers: #{proof.fri_layers}"

    # Verify proof
    puts "\nVerifying STARK proof..."
    verified = @zk.stark.verify(
      proof: proof.proof_bytes,
      public_signals: proof.public_signals,
      config: config
    )

    puts "Verification result: #{verified}"

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

  def run_recursive_example
    puts '=== Recursive Proof Composition ==='

    # Create inner proofs
    puts 'Generating inner proofs...'
    inner_proofs = []

    (1..3).each do |i|
      witness = { 'a' => i.to_s, 'b' => (i + 1).to_s }

      proof = @zk.recursive.prove_inner(
        circuit_id: 'multiplier-v1',
        witness: witness,
        level: 0
      )
      inner_proofs << proof
      puts "  Inner proof #{i} generated"
    end

    # Aggregate proofs recursively
    puts "\nAggregating proofs..."
    aggregated_proof = @zk.recursive.aggregate(
      proofs: inner_proofs,
      aggregation_circuit: 'recursive-aggregator-v1'
    )

    puts 'Aggregated proof:'
    puts "  Proof size: #{aggregated_proof.proof_bytes.length} bytes"
    puts "  Proofs aggregated: #{aggregated_proof.proofs_aggregated}"
    puts "  Recursion depth: #{aggregated_proof.recursion_depth}"

    # Verify aggregated proof (verifies all inner proofs at once)
    puts "\nVerifying aggregated proof..."
    verified = @zk.recursive.verify_aggregated(aggregated_proof)
    puts "Verification result: #{verified}"

    # Use cases
    puts "\nRecursive proof use cases:"
    puts '  - Rollup batch verification'
    puts '  - Incremental computation proofs'
    puts '  - Cross-chain state proofs'
    puts
  end

  def run_ceremony_example
    puts '=== Trusted Setup Ceremony ==='

    # List active ceremonies
    ceremonies = @zk.ceremony.list(:active)
    puts "Active ceremonies: #{ceremonies.length}"

    ceremonies.first(3).each do |ceremony|
      puts "\n  #{ceremony.ceremony_id}:"
      puts "    Circuit: #{ceremony.circuit_id}"
      puts "    Participants: #{ceremony.participant_count}"
      puts "    Current round: #{ceremony.current_round}"
      puts "    Status: #{ceremony.status}"
    end

    # Create a new ceremony
    puts "\nCreating new ceremony..."
    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
    )

    puts 'Ceremony created:'
    puts "  Ceremony ID: #{new_ceremony.ceremony_id}"
    puts "  Join URL: #{new_ceremony.join_url}"

    # Participate in a ceremony
    puts "\nParticipating in ceremony..."

    # Generate entropy
    entropy = SecureRandom.random_bytes(32)

    contribution = @zk.ceremony.contribute(
      ceremony_id: new_ceremony.ceremony_id,
      entropy: entropy
    )

    puts 'Contribution made:'
    puts "  Contribution ID: #{contribution.contribution_id}"
    puts "  Position: #{contribution.position}"
    puts "  Hash: #{contribution.hash}"

    # Verify a contribution
    puts "\nVerifying contribution..."
    valid = @zk.ceremony.verify_contribution(contribution.contribution_id)
    puts "Contribution valid: #{valid}"

    # Get ceremony transcript (for auditability)
    transcript = @zk.ceremony.get_transcript(new_ceremony.ceremony_id)
    puts "\nCeremony transcript:"
    puts "  Total contributions: #{transcript.contributions.length}"
    puts "  Start time: #{transcript.start_time}"
    puts "  Final hash: #{transcript.final_hash.empty? ? 'pending' : transcript.final_hash}"
    puts
  end
end

# Main execution
if __FILE__ == $PROGRAM_NAME
  # Initialize client
  api_key = ENV.fetch('SYNOR_API_KEY', 'your-api-key')
  config = Synor::Zk::Config.new(
    api_key: api_key,
    endpoint: 'https://zk.synor.io/v1',
    timeout: 120, # ZK operations can be slow
    retries: 3,
    debug: false,
    default_proving_system: :groth16,
    prove_timeout: 300,
    verify_timeout: 30
  )

  zk = Synor::Zk::Client.new(config)
  example = ZkExample.new(zk)

  begin
    # Check service health
    healthy = zk.health_check
    puts "Service healthy: #{healthy}\n\n"

    # Run examples
    example.run_circuit_example
    example.run_groth16_example
    example.run_plonk_example
    example.run_stark_example
    example.run_recursive_example
    example.run_ceremony_example
  rescue StandardError => e
    warn "Error: #{e.message}"
    exit 1
  end
end
