/**
 * 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
 */

using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Threading.Tasks;
using Synor.Zk;

namespace Synor.Examples;

public class ZkExample
{
    private readonly SynorZk _zk;

    public ZkExample(SynorZk zk)
    {
        _zk = zk;
    }

    public async Task RunCircuitExample()
    {
        Console.WriteLine("=== Circuit Compilation ===");

        // Simple Circom circuit: prove knowledge of factors
        var circomCode = @"
            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
        Console.WriteLine("Compiling circuit...");
        var circuit = await _zk.Circuits.CompileAsync(new CompileRequest
        {
            Code = circomCode,
            Language = CircuitLanguage.Circom
        });

        Console.WriteLine("Circuit compiled:");
        Console.WriteLine($"  Circuit ID: {circuit.CircuitId}");
        Console.WriteLine($"  Constraints: {circuit.Constraints}");
        Console.WriteLine($"  Public inputs: {circuit.PublicInputs}");
        Console.WriteLine($"  Private inputs: {circuit.PrivateInputs}");
        Console.WriteLine($"  Outputs: {circuit.Outputs}");

        // Get circuit info
        var info = await _zk.Circuits.GetAsync(circuit.CircuitId);
        Console.WriteLine("\nCircuit info:");
        Console.WriteLine($"  Name: {info.Name}");
        Console.WriteLine($"  Version: {info.Version}");
        Console.WriteLine($"  Wires: {info.WireCount}");
        Console.WriteLine($"  Labels: {info.LabelCount}");

        // List available circuits
        var circuits = await _zk.Circuits.ListAsync();
        Console.WriteLine($"\nAvailable circuits: {circuits.Count}");
        foreach (var c in circuits.GetRange(0, Math.Min(circuits.Count, 3)))
        {
            Console.WriteLine($"  {c.CircuitId}: {c.Name} ({c.Constraints} constraints)");
        }
        Console.WriteLine();
    }

    public async Task RunGroth16Example()
    {
        Console.WriteLine("=== Groth16 Proving System ===");

        var circuitId = "multiplier-v1";

        // Generate proving/verification keys (trusted setup)
        Console.WriteLine("Generating keys...");
        var keys = await _zk.Groth16.SetupAsync(circuitId);
        Console.WriteLine("Keys generated:");
        Console.WriteLine($"  Proving key size: {keys.ProvingKey.Length} bytes");
        Console.WriteLine($"  Verification key size: {keys.VerificationKey.Length} bytes");

        // Prepare witness (private inputs)
        var witness = new Dictionary<string, string>
        {
            ["a"] = "3",
            ["b"] = "7"
        };

        // Generate proof
        Console.WriteLine("\nGenerating proof...");
        var proof = await _zk.Groth16.ProveAsync(new ProveRequest
        {
            CircuitId = circuitId,
            Witness = witness,
            ProvingKey = keys.ProvingKey
        });

        Console.WriteLine("Proof generated:");
        Console.WriteLine($"  Proof size: {proof.ProofBytes.Length} bytes");
        Console.WriteLine($"  Public signals: {proof.PublicSignals.Count}");
        Console.WriteLine($"  Proving time: {proof.ProvingTimeMs}ms");

        // Verify proof
        Console.WriteLine("\nVerifying proof...");
        var verified = await _zk.Groth16.VerifyAsync(new VerifyRequest
        {
            Proof = proof.ProofBytes,
            PublicSignals = proof.PublicSignals,
            VerificationKey = keys.VerificationKey
        });

        Console.WriteLine($"Verification result: {verified}");

        // Export proof for on-chain verification
        var solidityCalldata = await _zk.Groth16.ExportCalldataAsync(proof);
        Console.WriteLine($"\nSolidity calldata: {solidityCalldata[..Math.Min(solidityCalldata.Length, 100)]}...");
        Console.WriteLine();
    }

    public async Task RunPlonkExample()
    {
        Console.WriteLine("=== PLONK Proving System ===");

        var circuitId = "multiplier-v1";

        // PLONK uses universal trusted setup
        Console.WriteLine("Getting universal setup...");
        var srs = await _zk.Plonk.GetUniversalSetupAsync(14); // 2^14 constraints
        Console.WriteLine($"SRS loaded: {srs.Length} bytes");

        // Generate circuit-specific keys
        Console.WriteLine("\nGenerating circuit keys...");
        var keys = await _zk.Plonk.SetupAsync(circuitId, srs);
        Console.WriteLine("Keys generated");

        // Generate proof
        var witness = new Dictionary<string, string>
        {
            ["a"] = "5",
            ["b"] = "9"
        };

        Console.WriteLine("\nGenerating PLONK proof...");
        var proof = await _zk.Plonk.ProveAsync(new ProveRequest
        {
            CircuitId = circuitId,
            Witness = witness,
            ProvingKey = keys.ProvingKey
        });

        Console.WriteLine("Proof generated:");
        Console.WriteLine($"  Proof size: {proof.ProofBytes.Length} bytes");
        Console.WriteLine($"  Proving time: {proof.ProvingTimeMs}ms");

        // Verify proof
        var verified = await _zk.Plonk.VerifyAsync(new VerifyRequest
        {
            Proof = proof.ProofBytes,
            PublicSignals = proof.PublicSignals,
            VerificationKey = keys.VerificationKey
        });
        Console.WriteLine($"Verification result: {verified}");

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

    public async Task RunStarkExample()
    {
        Console.WriteLine("=== STARK Proving System ===");

        var circuitId = "multiplier-v1";

        // STARKs don't need trusted setup
        Console.WriteLine("Configuring STARK parameters...");
        var config = new StarkConfig
        {
            FieldSize = 256,
            HashFunction = HashFunction.Poseidon,
            BlowupFactor = 8,
            NumQueries = 30,
            FoldingFactor = 8
        };

        // Generate proof
        var witness = new Dictionary<string, string>
        {
            ["a"] = "11",
            ["b"] = "13"
        };

        Console.WriteLine("\nGenerating STARK proof...");
        var proof = await _zk.Stark.ProveAsync(new StarkProveRequest
        {
            CircuitId = circuitId,
            Witness = witness,
            Config = config
        });

        Console.WriteLine("Proof generated:");
        Console.WriteLine($"  Proof size: {proof.ProofBytes.Length} bytes");
        Console.WriteLine($"  Proving time: {proof.ProvingTimeMs}ms");
        Console.WriteLine($"  FRI layers: {proof.FriLayers}");

        // Verify proof
        Console.WriteLine("\nVerifying STARK proof...");
        var verified = await _zk.Stark.VerifyAsync(new StarkVerifyRequest
        {
            Proof = proof.ProofBytes,
            PublicSignals = proof.PublicSignals,
            Config = config
        });

        Console.WriteLine($"Verification result: {verified}");

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

    public async Task RunRecursiveExample()
    {
        Console.WriteLine("=== Recursive Proof Composition ===");

        // Create inner proofs
        Console.WriteLine("Generating inner proofs...");
        var innerProofs = new List<RecursiveProof>();

        for (var i = 1; i <= 3; i++)
        {
            var witness = new Dictionary<string, string>
            {
                ["a"] = i.ToString(),
                ["b"] = (i + 1).ToString()
            };

            var proof = await _zk.Recursive.ProveInnerAsync(new RecursiveProveRequest
            {
                CircuitId = "multiplier-v1",
                Witness = witness,
                Level = 0
            });
            innerProofs.Add(proof);
            Console.WriteLine($"  Inner proof {i} generated");
        }

        // Aggregate proofs recursively
        Console.WriteLine("\nAggregating proofs...");
        var aggregatedProof = await _zk.Recursive.AggregateAsync(new AggregateRequest
        {
            Proofs = innerProofs,
            AggregationCircuit = "recursive-aggregator-v1"
        });

        Console.WriteLine("Aggregated proof:");
        Console.WriteLine($"  Proof size: {aggregatedProof.ProofBytes.Length} bytes");
        Console.WriteLine($"  Proofs aggregated: {aggregatedProof.ProofsAggregated}");
        Console.WriteLine($"  Recursion depth: {aggregatedProof.RecursionDepth}");

        // Verify aggregated proof (verifies all inner proofs at once)
        Console.WriteLine("\nVerifying aggregated proof...");
        var verified = await _zk.Recursive.VerifyAggregatedAsync(aggregatedProof);
        Console.WriteLine($"Verification result: {verified}");

        // Use cases
        Console.WriteLine("\nRecursive proof use cases:");
        Console.WriteLine("  - Rollup batch verification");
        Console.WriteLine("  - Incremental computation proofs");
        Console.WriteLine("  - Cross-chain state proofs");
        Console.WriteLine();
    }

    public async Task RunCeremonyExample()
    {
        Console.WriteLine("=== Trusted Setup Ceremony ===");

        // List active ceremonies
        var ceremonies = await _zk.Ceremony.ListAsync(CeremonyStatus.Active);
        Console.WriteLine($"Active ceremonies: {ceremonies.Count}");

        foreach (var ceremony in ceremonies.GetRange(0, Math.Min(ceremonies.Count, 3)))
        {
            Console.WriteLine($"\n  {ceremony.CeremonyId}:");
            Console.WriteLine($"    Circuit: {ceremony.CircuitId}");
            Console.WriteLine($"    Participants: {ceremony.ParticipantCount}");
            Console.WriteLine($"    Current round: {ceremony.CurrentRound}");
            Console.WriteLine($"    Status: {ceremony.Status}");
        }

        // Create a new ceremony
        Console.WriteLine("\nCreating new ceremony...");
        var newCeremony = await _zk.Ceremony.CreateAsync(new CreateCeremonyRequest
        {
            CircuitId = "new-circuit-v1",
            MinParticipants = 10,
            MaxParticipants = 100,
            RoundDuration = 3600, // 1 hour per round
            VerifyContributions = true
        });

        Console.WriteLine("Ceremony created:");
        Console.WriteLine($"  Ceremony ID: {newCeremony.CeremonyId}");
        Console.WriteLine($"  Join URL: {newCeremony.JoinUrl}");

        // Participate in a ceremony
        Console.WriteLine("\nParticipating in ceremony...");

        // Generate entropy
        var entropy = new byte[32];
        using (var rng = RandomNumberGenerator.Create())
        {
            rng.GetBytes(entropy);
        }

        var contribution = await _zk.Ceremony.ContributeAsync(new ContributeRequest
        {
            CeremonyId = newCeremony.CeremonyId,
            Entropy = entropy
        });

        Console.WriteLine("Contribution made:");
        Console.WriteLine($"  Contribution ID: {contribution.ContributionId}");
        Console.WriteLine($"  Position: {contribution.Position}");
        Console.WriteLine($"  Hash: {contribution.Hash}");

        // Verify a contribution
        Console.WriteLine("\nVerifying contribution...");
        var valid = await _zk.Ceremony.VerifyContributionAsync(contribution.ContributionId);
        Console.WriteLine($"Contribution valid: {valid}");

        // Get ceremony transcript (for auditability)
        var transcript = await _zk.Ceremony.GetTranscriptAsync(newCeremony.CeremonyId);
        Console.WriteLine("\nCeremony transcript:");
        Console.WriteLine($"  Total contributions: {transcript.Contributions.Count}");
        Console.WriteLine($"  Start time: {transcript.StartTime}");
        Console.WriteLine($"  Final hash: {(string.IsNullOrEmpty(transcript.FinalHash) ? "pending" : transcript.FinalHash)}");
        Console.WriteLine();
    }

    public static async Task Main(string[] args)
    {
        // Initialize client
        var apiKey = Environment.GetEnvironmentVariable("SYNOR_API_KEY") ?? "your-api-key";
        var config = new ZkConfig
        {
            ApiKey = apiKey,
            Endpoint = "https://zk.synor.io/v1",
            Timeout = TimeSpan.FromSeconds(120), // ZK operations can be slow
            Retries = 3,
            Debug = false,
            DefaultProvingSystem = ProvingSystem.Groth16,
            ProveTimeout = TimeSpan.FromSeconds(300),
            VerifyTimeout = TimeSpan.FromSeconds(30)
        };

        var zk = new SynorZk(config);
        var example = new ZkExample(zk);

        try
        {
            // Check service health
            var healthy = await zk.HealthCheckAsync();
            Console.WriteLine($"Service healthy: {healthy}\n");

            // Run examples
            await example.RunCircuitExample();
            await example.RunGroth16Example();
            await example.RunPlonkExample();
            await example.RunStarkExample();
            await example.RunRecursiveExample();
            await example.RunCeremonyExample();
        }
        catch (Exception ex)
        {
            Console.Error.WriteLine($"Error: {ex.Message}");
            Environment.Exit(1);
        }
    }
}
