/**
 * Synor ZK SDK Examples for JavaScript/TypeScript
 *
 * Demonstrates Zero-Knowledge proof operations:
 * - Circuit compilation
 * - Proof generation and verification
 * - Groth16, PLONK, and STARK proving systems
 * - Recursive proofs
 * - On-chain verification
 */

import {
  SynorZk,
  ZkConfig,
  ProvingSystem,
  CircuitFormat,
  ProofStatus,
} from '../src/zk';

async function main() {
  // Initialize client
  const config: ZkConfig = {
    apiKey: process.env.SYNOR_API_KEY || 'your-api-key',
    endpoint: 'https://zk.synor.io/v1',
    timeout: 120000, // ZK ops can be slow
    retries: 3,
    debug: false,
    defaultProvingSystem: ProvingSystem.Groth16,
  };

  const zk = new SynorZk(config);

  try {
    // Check service health
    const healthy = await zk.healthCheck();
    console.log(`Service healthy: ${healthy}\n`);

    // Example 1: Circuit compilation
    await circuitExample(zk);

    // Example 2: Proof generation
    await proofExample(zk);

    // Example 3: Different proving systems
    await provingSystemsExample(zk);

    // Example 4: Recursive proofs
    await recursiveProofExample(zk);

    // Example 5: On-chain verification
    await onChainVerificationExample(zk);

    // Example 6: Trusted setup
    await setupExample(zk);
  } finally {
    zk.close();
  }
}

/**
 * Circuit compilation
 */
async function circuitExample(zk: SynorZk): Promise<void> {
  console.log('=== Circuit Compilation ===');

  // Circom circuit example: prove knowledge of preimage
  const circomCircuit = `
    pragma circom 2.1.0;

    template HashPreimage() {
        signal input preimage;
        signal input hash;

        // Simplified hash computation (in reality, use proper hash)
        signal preimageSquared;
        preimageSquared <== preimage * preimage;

        // Constrain that hash matches
        hash === preimageSquared;
    }

    component main {public [hash]} = HashPreimage();
  `;

  // Compile circuit
  console.log('Compiling Circom circuit...');
  const compiled = await zk.circuits.compile({
    code: circomCircuit,
    format: CircuitFormat.Circom,
    name: 'hash_preimage',
    provingSystem: ProvingSystem.Groth16,
  });

  console.log(`Circuit compiled:`);
  console.log(`  Circuit ID: ${compiled.circuitId}`);
  console.log(`  Constraints: ${compiled.constraintCount}`);
  console.log(`  Public inputs: ${compiled.publicInputCount}`);
  console.log(`  Private inputs: ${compiled.privateInputCount}`);
  console.log(`  Proving key size: ${compiled.provingKeySize} bytes`);
  console.log(`  Verification key size: ${compiled.verificationKeySize} bytes`);

  // List circuits
  const circuits = await zk.circuits.list();
  console.log(`\nYour circuits: ${circuits.length}`);
  for (const circuit of circuits) {
    console.log(`  ${circuit.name} (${circuit.circuitId})`);
  }

  // Get circuit details
  const details = await zk.circuits.get(compiled.circuitId);
  console.log(`\nCircuit details:`);
  console.log(`  Format: ${details.format}`);
  console.log(`  Proving system: ${details.provingSystem}`);
  console.log(`  Created: ${new Date(details.createdAt).toISOString()}`);

  // Download proving key
  const provingKey = await zk.circuits.getProvingKey(compiled.circuitId);
  console.log(`\nProving key downloaded: ${provingKey.length} bytes`);

  // Download verification key
  const verificationKey = await zk.circuits.getVerificationKey(compiled.circuitId);
  console.log(`Verification key downloaded: ${verificationKey.length} bytes`);

  console.log('');
}

/**
 * Proof generation and verification
 */
async function proofExample(zk: SynorZk): Promise<void> {
  console.log('=== Proof Generation ===');

  // First, compile a simple circuit
  const circuit = `
    pragma circom 2.1.0;

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

        c <== a * b;
    }

    component main {public [c]} = Multiplier();
  `;

  const compiled = await zk.circuits.compile({
    code: circuit,
    format: CircuitFormat.Circom,
    name: 'multiplier',
    provingSystem: ProvingSystem.Groth16,
  });

  // Generate proof
  console.log('Generating proof...');
  const startTime = Date.now();

  const proof = await zk.proofs.generate({
    circuitId: compiled.circuitId,
    inputs: {
      a: '3',
      b: '7',
    },
  });

  const proofTime = Date.now() - startTime;
  console.log(`Proof generated in ${proofTime}ms`);
  console.log(`  Proof ID: ${proof.proofId}`);
  console.log(`  Proof size: ${proof.proof.length} bytes`);
  console.log(`  Public signals: ${proof.publicSignals}`);

  // Verify proof
  console.log('\nVerifying proof...');
  const verifyStart = Date.now();

  const isValid = await zk.proofs.verify({
    circuitId: compiled.circuitId,
    proof: proof.proof,
    publicSignals: proof.publicSignals,
  });

  const verifyTime = Date.now() - verifyStart;
  console.log(`Verification completed in ${verifyTime}ms`);
  console.log(`  Valid: ${isValid}`);

  // Verify with wrong public signals (should fail)
  console.log('\nVerifying with wrong signals...');
  const invalidResult = await zk.proofs.verify({
    circuitId: compiled.circuitId,
    proof: proof.proof,
    publicSignals: ['42'], // Wrong answer
  });
  console.log(`  Valid: ${invalidResult} (expected false)`);

  // Get proof status
  const status = await zk.proofs.getStatus(proof.proofId);
  console.log(`\nProof status:`);
  console.log(`  State: ${status.state}`);
  console.log(`  Verified: ${status.verified}`);
  console.log(`  Created: ${new Date(status.createdAt).toISOString()}`);

  // List proofs
  const proofs = await zk.proofs.list({ circuitId: compiled.circuitId });
  console.log(`\nProofs for circuit: ${proofs.length}`);

  console.log('');
}

/**
 * Different proving systems comparison
 */
async function provingSystemsExample(zk: SynorZk): Promise<void> {
  console.log('=== Proving Systems Comparison ===');

  // Simple circuit for comparison
  const circuit = `
    pragma circom 2.1.0;

    template Comparison() {
        signal input x;
        signal input y;
        signal output sum;

        sum <== x + y;
    }

    component main {public [sum]} = Comparison();
  `;

  const systems = [
    ProvingSystem.Groth16,
    ProvingSystem.Plonk,
    ProvingSystem.Stark,
  ];

  console.log('Comparing proving systems:\n');

  for (const system of systems) {
    console.log(`${system}:`);

    // Compile for this system
    const compiled = await zk.circuits.compile({
      code: circuit,
      format: CircuitFormat.Circom,
      name: `comparison_${system.toLowerCase()}`,
      provingSystem: system,
    });

    // Generate proof
    const proofStart = Date.now();
    const proof = await zk.proofs.generate({
      circuitId: compiled.circuitId,
      inputs: { x: '10', y: '20' },
    });
    const proofTime = Date.now() - proofStart;

    // Verify proof
    const verifyStart = Date.now();
    await zk.proofs.verify({
      circuitId: compiled.circuitId,
      proof: proof.proof,
      publicSignals: proof.publicSignals,
    });
    const verifyTime = Date.now() - verifyStart;

    console.log(`  Setup: ${compiled.setupTime}ms`);
    console.log(`  Proof time: ${proofTime}ms`);
    console.log(`  Verify time: ${verifyTime}ms`);
    console.log(`  Proof size: ${proof.proof.length} bytes`);
    console.log(`  Verification key: ${compiled.verificationKeySize} bytes`);
    console.log('');
  }

  console.log('Summary:');
  console.log('  Groth16: Smallest proofs, fast verification, trusted setup required');
  console.log('  PLONK: Universal setup, flexible, moderate proof size');
  console.log('  STARK: No trusted setup, largest proofs, quantum resistant');

  console.log('');
}

/**
 * Recursive proof aggregation
 */
async function recursiveProofExample(zk: SynorZk): Promise<void> {
  console.log('=== Recursive Proofs ===');

  // Inner circuit
  const innerCircuit = `
    pragma circom 2.1.0;

    template Inner() {
        signal input x;
        signal output y;
        y <== x * x;
    }

    component main {public [y]} = Inner();
  `;

  // Compile inner circuit
  const inner = await zk.circuits.compile({
    code: innerCircuit,
    format: CircuitFormat.Circom,
    name: 'inner_circuit',
    provingSystem: ProvingSystem.Groth16,
  });

  // Generate multiple proofs to aggregate
  console.log('Generating proofs to aggregate...');
  const proofsToAggregate = [];
  for (let i = 1; i <= 4; i++) {
    const proof = await zk.proofs.generate({
      circuitId: inner.circuitId,
      inputs: { x: i.toString() },
    });
    proofsToAggregate.push({
      proof: proof.proof,
      publicSignals: proof.publicSignals,
    });
    console.log(`  Proof ${i}: y = ${proof.publicSignals[0]}`);
  }

  // Aggregate proofs recursively
  console.log('\nAggregating proofs...');
  const aggregated = await zk.proofs.aggregate({
    circuitId: inner.circuitId,
    proofs: proofsToAggregate,
    aggregationType: 'recursive',
  });

  console.log(`Aggregated proof:`);
  console.log(`  Proof ID: ${aggregated.proofId}`);
  console.log(`  Aggregated count: ${aggregated.aggregatedCount}`);
  console.log(`  Proof size: ${aggregated.proof.length} bytes`);
  console.log(`  Size reduction: ${(1 - aggregated.proof.length / (proofsToAggregate.length * proofsToAggregate[0].proof.length)) * 100}%`);

  // Verify aggregated proof
  const isValid = await zk.proofs.verifyAggregated({
    circuitId: inner.circuitId,
    proof: aggregated.proof,
    publicSignalsList: proofsToAggregate.map(p => p.publicSignals),
  });
  console.log(`\nAggregated proof valid: ${isValid}`);

  // Batch verification (verify multiple proofs in one operation)
  console.log('\nBatch verification...');
  const batchResult = await zk.proofs.batchVerify({
    circuitId: inner.circuitId,
    proofs: proofsToAggregate,
  });
  console.log(`  All valid: ${batchResult.allValid}`);
  console.log(`  Results: ${batchResult.results.join(', ')}`);

  console.log('');
}

/**
 * On-chain verification
 */
async function onChainVerificationExample(zk: SynorZk): Promise<void> {
  console.log('=== On-Chain Verification ===');

  // Compile circuit
  const circuit = `
    pragma circom 2.1.0;

    template VoteCommitment() {
        signal input vote;        // Private: actual vote
        signal input nullifier;   // Private: unique identifier
        signal input commitment;  // Public: commitment to verify

        // Simplified commitment (in practice, use Poseidon hash)
        signal computed;
        computed <== vote * nullifier;
        commitment === computed;
    }

    component main {public [commitment]} = VoteCommitment();
  `;

  const compiled = await zk.circuits.compile({
    code: circuit,
    format: CircuitFormat.Circom,
    name: 'vote_commitment',
    provingSystem: ProvingSystem.Groth16,
  });

  // Generate Solidity verifier
  console.log('Generating Solidity verifier...');
  const solidityVerifier = await zk.contracts.generateVerifier({
    circuitId: compiled.circuitId,
    language: 'solidity',
    optimized: true,
  });
  console.log(`Solidity verifier generated: ${solidityVerifier.code.length} bytes`);
  console.log(`  Contract name: ${solidityVerifier.contractName}`);
  console.log(`  Gas estimate: ${solidityVerifier.gasEstimate}`);

  // Generate proof
  const proof = await zk.proofs.generate({
    circuitId: compiled.circuitId,
    inputs: {
      vote: '1',        // Vote YES (1) or NO (0)
      nullifier: '12345',
      commitment: '12345', // 1 * 12345
    },
  });

  // Format proof for on-chain verification
  console.log('\nFormatting proof for on-chain...');
  const onChainProof = await zk.contracts.formatProof({
    circuitId: compiled.circuitId,
    proof: proof.proof,
    publicSignals: proof.publicSignals,
    format: 'calldata',
  });
  console.log(`  Calldata: ${onChainProof.calldata.slice(0, 100)}...`);
  console.log(`  Estimated gas: ${onChainProof.gasEstimate}`);

  // Deploy verifier contract (simulation)
  console.log('\nDeploying verifier contract...');
  const deployment = await zk.contracts.deployVerifier({
    circuitId: compiled.circuitId,
    network: 'synor-testnet',
  });
  console.log(`  Contract address: ${deployment.address}`);
  console.log(`  TX hash: ${deployment.txHash}`);
  console.log(`  Gas used: ${deployment.gasUsed}`);

  // Verify on-chain
  console.log('\nVerifying on-chain...');
  const onChainResult = await zk.contracts.verifyOnChain({
    contractAddress: deployment.address,
    proof: proof.proof,
    publicSignals: proof.publicSignals,
    network: 'synor-testnet',
  });
  console.log(`  TX hash: ${onChainResult.txHash}`);
  console.log(`  Verified: ${onChainResult.verified}`);
  console.log(`  Gas used: ${onChainResult.gasUsed}`);

  // Generate verifier for other targets
  console.log('\nGenerating verifiers for other targets:');
  const targets = ['cairo', 'noir', 'ink'];
  for (const target of targets) {
    const verifier = await zk.contracts.generateVerifier({
      circuitId: compiled.circuitId,
      language: target,
    });
    console.log(`  ${target}: ${verifier.code.length} bytes`);
  }

  console.log('');
}

/**
 * Trusted setup ceremonies
 */
async function setupExample(zk: SynorZk): Promise<void> {
  console.log('=== Trusted Setup ===');

  // Get available ceremonies
  const ceremonies = await zk.setup.listCeremonies();
  console.log(`Active ceremonies: ${ceremonies.length}`);
  for (const ceremony of ceremonies) {
    console.log(`  ${ceremony.name}:`);
    console.log(`    Status: ${ceremony.status}`);
    console.log(`    Participants: ${ceremony.participantCount}`);
    console.log(`    Current round: ${ceremony.currentRound}`);
  }

  // Create a new circuit setup
  const circuit = `
    pragma circom 2.1.0;

    template NewCircuit() {
        signal input a;
        signal output b;
        b <== a + 1;
    }

    component main {public [b]} = NewCircuit();
  `;

  console.log('\nInitializing setup for new circuit...');
  const setup = await zk.setup.initialize({
    circuit,
    format: CircuitFormat.Circom,
    name: 'new_circuit_setup',
    provingSystem: ProvingSystem.Groth16,
    ceremonyType: 'powers_of_tau', // or 'phase2'
  });
  console.log(`Setup initialized:`);
  console.log(`  Ceremony ID: ${setup.ceremonyId}`);
  console.log(`  Powers of Tau required: ${setup.powersRequired}`);
  console.log(`  Current phase: ${setup.phase}`);

  // Contribute to ceremony (in practice, generates random entropy)
  console.log('\nContributing to ceremony...');
  const contribution = await zk.setup.contribute({
    ceremonyId: setup.ceremonyId,
    entropy: Buffer.from('random-entropy-from-user').toString('hex'),
  });
  console.log(`Contribution submitted:`);
  console.log(`  Participant: ${contribution.participantId}`);
  console.log(`  Contribution hash: ${contribution.hash}`);
  console.log(`  Verification status: ${contribution.verified}`);

  // Get ceremony status
  const status = await zk.setup.getStatus(setup.ceremonyId);
  console.log(`\nCeremony status:`);
  console.log(`  Phase: ${status.phase}`);
  console.log(`  Total contributions: ${status.totalContributions}`);
  console.log(`  Verified contributions: ${status.verifiedContributions}`);
  console.log(`  Ready for finalization: ${status.readyForFinalization}`);

  // Finalize setup (when enough contributions)
  if (status.readyForFinalization) {
    console.log('\nFinalizing setup...');
    const finalized = await zk.setup.finalize(setup.ceremonyId);
    console.log(`Setup finalized:`);
    console.log(`  Proving key hash: ${finalized.provingKeyHash}`);
    console.log(`  Verification key hash: ${finalized.verificationKeyHash}`);
    console.log(`  Contribution transcript: ${finalized.transcriptCid}`);
  }

  // Download ceremony transcript
  const transcript = await zk.setup.getTranscript(setup.ceremonyId);
  console.log(`\nCeremony transcript: ${transcript.length} bytes`);

  console.log('');
}

// Run examples
main().catch(console.error);
