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

import 'dart:io';
import 'package:synor_zk/synor_zk.dart';

Future<void> main() async {
  // Initialize client
  final config = ZkConfig(
    apiKey: Platform.environment['SYNOR_API_KEY'] ?? 'your-api-key',
    endpoint: 'https://zk.synor.io/v1',
    timeout: const Duration(seconds: 120), // ZK ops can be slow
    retries: 3,
    debug: false,
    defaultProvingSystem: ProvingSystem.groth16,
  );

  final zk = SynorZk(config);

  try {
    // Check service health
    final healthy = await zk.healthCheck();
    print('Service healthy: $healthy\n');

    // Run examples
    await circuitExample(zk);
    await proofExample(zk);
    await provingSystemsExample(zk);
    await recursiveProofExample(zk);
    await onChainVerificationExample(zk);
    await setupExample(zk);
  } finally {
    await zk.close();
  }
}

Future<void> circuitExample(SynorZk zk) async {
  print('=== 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
  print('Compiling Circom circuit...');
  final compiled = await zk.circuits.compile(CompileRequest(
    code: circomCircuit,
    format: CircuitFormat.circom,
    name: 'hash_preimage',
    provingSystem: ProvingSystem.groth16,
  ));

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

  // List circuits
  final circuits = await zk.circuits.list();
  print('\nYour circuits: ${circuits.length}');
  for (final circuit in circuits) {
    print('  ${circuit.name} (${circuit.circuitId})');
  }

  // Get circuit details
  final details = await zk.circuits.get(compiled.circuitId);
  print('\nCircuit details:');
  print('  Format: ${details.format}');
  print('  Proving system: ${details.provingSystem}');
  print('  Created: ${details.createdAt}');

  // Download proving key
  final provingKey = await zk.circuits.getProvingKey(compiled.circuitId);
  print('\nProving key downloaded: ${provingKey.length} bytes');

  // Download verification key
  final verificationKey = await zk.circuits.getVerificationKey(compiled.circuitId);
  print('Verification key downloaded: ${verificationKey.length} bytes');

  print('');
}

Future<void> proofExample(SynorZk zk) async {
  print('=== 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();
    ''';

  final compiled = await zk.circuits.compile(CompileRequest(
    code: circuit,
    format: CircuitFormat.circom,
    name: 'multiplier',
    provingSystem: ProvingSystem.groth16,
  ));

  // Generate proof
  print('Generating proof...');
  final startTime = DateTime.now();

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

  final proofTime = DateTime.now().difference(startTime);
  print('Proof generated in ${proofTime.inMilliseconds}ms');
  print('  Proof ID: ${proof.proofId}');
  print('  Proof size: ${proof.proof.length} bytes');
  print('  Public signals: ${proof.publicSignals}');

  // Verify proof
  print('\nVerifying proof...');
  final verifyStart = DateTime.now();

  final isValid = await zk.proofs.verify(VerifyRequest(
    circuitId: compiled.circuitId,
    proof: proof.proof,
    publicSignals: proof.publicSignals,
  ));

  final verifyTime = DateTime.now().difference(verifyStart);
  print('Verification completed in ${verifyTime.inMilliseconds}ms');
  print('  Valid: $isValid');

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

  // Get proof status
  final status = await zk.proofs.getStatus(proof.proofId);
  print('\nProof status:');
  print('  State: ${status.state}');
  print('  Verified: ${status.verified}');
  print('  Created: ${status.createdAt}');

  // List proofs
  final proofs = await zk.proofs.list(compiled.circuitId);
  print('\nProofs for circuit: ${proofs.length}');

  print('');
}

Future<void> provingSystemsExample(SynorZk zk) async {
  print('=== 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();
    ''';

  final systems = [
    (ProvingSystem.groth16, 'GROTH16'),
    (ProvingSystem.plonk, 'PLONK'),
    (ProvingSystem.stark, 'STARK'),
  ];

  print('Comparing proving systems:\n');

  for (final (system, name) in systems) {
    print('$name:');

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

    // Generate proof
    final proofStart = DateTime.now();
    final proof = await zk.proofs.generate(GenerateProofRequest(
      circuitId: compiled.circuitId,
      inputs: {'x': '10', 'y': '20'},
    ));
    final proofTime = DateTime.now().difference(proofStart);

    // Verify proof
    final verifyStart = DateTime.now();
    await zk.proofs.verify(VerifyRequest(
      circuitId: compiled.circuitId,
      proof: proof.proof,
      publicSignals: proof.publicSignals,
    ));
    final verifyTime = DateTime.now().difference(verifyStart);

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

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

  print('');
}

Future<void> recursiveProofExample(SynorZk zk) async {
  print('=== 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
  final inner = await zk.circuits.compile(CompileRequest(
    code: innerCircuit,
    format: CircuitFormat.circom,
    name: 'inner_circuit',
    provingSystem: ProvingSystem.groth16,
  ));

  // Generate multiple proofs to aggregate
  print('Generating proofs to aggregate...');
  final proofsToAggregate = <ProofData>[];
  for (var i = 1; i <= 4; i++) {
    final proof = await zk.proofs.generate(GenerateProofRequest(
      circuitId: inner.circuitId,
      inputs: {'x': '$i'},
    ));
    proofsToAggregate.add(ProofData(
      proof: proof.proof,
      publicSignals: proof.publicSignals,
    ));
    print('  Proof $i: y = ${proof.publicSignals[0]}');
  }

  // Aggregate proofs recursively
  print('\nAggregating proofs...');
  final aggregated = await zk.proofs.aggregate(AggregateRequest(
    circuitId: inner.circuitId,
    proofs: proofsToAggregate,
    aggregationType: 'recursive',
  ));

  final originalSize = proofsToAggregate.fold<int>(
    0,
    (sum, p) => sum + p.proof.length,
  );

  print('Aggregated proof:');
  print('  Proof ID: ${aggregated.proofId}');
  print('  Aggregated count: ${aggregated.aggregatedCount}');
  print('  Proof size: ${aggregated.proof.length} bytes');
  print('  Size reduction: ${((1 - aggregated.proof.length / originalSize) * 100).toStringAsFixed(1)}%');

  // Verify aggregated proof
  final isValid = await zk.proofs.verifyAggregated(VerifyAggregatedRequest(
    circuitId: inner.circuitId,
    proof: aggregated.proof,
    publicSignalsList: proofsToAggregate.map((p) => p.publicSignals).toList(),
  ));
  print('\nAggregated proof valid: $isValid');

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

  print('');
}

Future<void> onChainVerificationExample(SynorZk zk) async {
  print('=== 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();
    ''';

  final compiled = await zk.circuits.compile(CompileRequest(
    code: circuit,
    format: CircuitFormat.circom,
    name: 'vote_commitment',
    provingSystem: ProvingSystem.groth16,
  ));

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

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

  // Format proof for on-chain verification
  print('\nFormatting proof for on-chain...');
  final onChainProof = await zk.contracts.formatProof(FormatProofRequest(
    circuitId: compiled.circuitId,
    proof: proof.proof,
    publicSignals: proof.publicSignals,
    format: 'calldata',
  ));
  final calldataPreview = onChainProof.calldata.length > 100
      ? onChainProof.calldata.substring(0, 100)
      : onChainProof.calldata;
  print('  Calldata: $calldataPreview...');
  print('  Estimated gas: ${onChainProof.gasEstimate}');

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

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

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

  print('');
}

Future<void> setupExample(SynorZk zk) async {
  print('=== Trusted Setup ===');

  // Get available ceremonies
  final ceremonies = await zk.setup.listCeremonies();
  print('Active ceremonies: ${ceremonies.length}');
  for (final ceremony in ceremonies) {
    print('  ${ceremony.name}:');
    print('    Status: ${ceremony.status}');
    print('    Participants: ${ceremony.participantCount}');
    print('    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();
    ''';

  print('\nInitializing setup for new circuit...');
  final setup = await zk.setup.initialize(SetupInitRequest(
    circuit: circuit,
    format: CircuitFormat.circom,
    name: 'new_circuit_setup',
    provingSystem: ProvingSystem.groth16,
    ceremonyType: 'powers_of_tau', // or 'phase2'
  ));
  print('Setup initialized:');
  print('  Ceremony ID: ${setup.ceremonyId}');
  print('  Powers of Tau required: ${setup.powersRequired}');
  print('  Current phase: ${setup.phase}');

  // Contribute to ceremony (in practice, generates random entropy)
  print('\nContributing to ceremony...');
  final contribution = await zk.setup.contribute(ContributeRequest(
    ceremonyId: setup.ceremonyId,
    entropy: 'random-entropy-from-user'.codeUnits.map((b) => b.toRadixString(16).padLeft(2, '0')).join(),
  ));
  print('Contribution submitted:');
  print('  Participant: ${contribution.participantId}');
  print('  Contribution hash: ${contribution.hash}');
  print('  Verification status: ${contribution.verified}');

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

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

  // Download ceremony transcript
  final transcript = await zk.setup.getTranscript(setup.ceremonyId);
  print('\nCeremony transcript: ${transcript.length} bytes');

  print('');
}
