/**
 * Synor ZK SDK for C++
 *
 * Zero-Knowledge proof systems for ZK-Rollups and privacy.
 */

#ifndef SYNOR_ZK_HPP
#define SYNOR_ZK_HPP

#include <string>
#include <vector>
#include <memory>
#include <optional>
#include <chrono>
#include <future>
#include <stdexcept>
#include <map>
#include <cstdint>

namespace synor {
namespace zk {

/* ============================================================================
 * Enumerations
 * ============================================================================ */

/** Proof system backends */
enum class ProofSystem {
    Groth16,  ///< Smallest proofs (~192 bytes), fastest verification
    Plonk,    ///< Universal trusted setup, medium proofs (~512 bytes)
    Stark     ///< No trusted setup, largest proofs (~50KB)
};

/** Circuit types for different operations */
enum class CircuitType {
    Transfer,  ///< Single transfer between accounts
    Batch,     ///< Batch of multiple transfers
    Deposit,   ///< Deposit from L1 to L2
    Withdraw   ///< Withdrawal from L2 to L1
};

/** Rollup state */
enum class RollupState {
    Active,
    Paused,
    Finalizing,
    Finalized
};

/** Proof status */
enum class ProofStatus {
    Generating,
    Completed,
    Failed,
    Verified
};

/* ============================================================================
 * Exception
 * ============================================================================ */

class ZkError : public std::runtime_error {
public:
    ZkError(const std::string& message,
            const std::optional<std::string>& code = std::nullopt,
            const std::optional<int>& status = std::nullopt)
        : std::runtime_error(message), code_(code), status_(status) {}

    const std::optional<std::string>& code() const { return code_; }
    const std::optional<int>& status() const { return status_; }

private:
    std::optional<std::string> code_;
    std::optional<int> status_;
};

/* ============================================================================
 * Data Types
 * ============================================================================ */

/** Zero-knowledge proof */
struct Proof {
    std::string id;
    ProofSystem system;
    CircuitType circuit_type;
    std::vector<uint8_t> data;
    std::vector<std::string> public_inputs;
    size_t size;
    uint64_t generation_time_ms;
    uint64_t created_at;
};

/** Verification key for a circuit */
struct VerificationKey {
    std::string circuit_id;
    CircuitType circuit_type;
    ProofSystem system;
    std::vector<uint8_t> data;
    size_t size;
};

/** Proving key for generating proofs */
struct ProvingKey {
    std::string circuit_id;
    CircuitType circuit_type;
    ProofSystem system;
    std::vector<uint8_t> data;
    size_t size;
};

/** Circuit configuration */
struct CircuitConfig {
    CircuitType type;
    std::optional<size_t> max_batch_size;
    std::optional<size_t> tree_depth;
    std::optional<bool> verify_signatures;
    std::optional<std::map<std::string, std::string>> custom_constraints;
};

/** Account state in the rollup */
struct AccountState {
    std::string address;
    std::string balance;
    uint64_t nonce;
    std::string pubkey_hash;
};

/** Transfer operation */
struct Transfer {
    std::string from;
    std::string to;
    std::string amount;
    std::string fee;
    uint64_t nonce;
    std::optional<std::string> signature;
};

/** Deposit operation (L1 -> L2) */
struct Deposit {
    std::string l1_tx_hash;
    std::string recipient;
    std::string amount;
    std::optional<std::string> token;
};

/** Withdrawal operation (L2 -> L1) */
struct Withdrawal {
    std::string sender;
    std::string recipient;
    std::string amount;
    std::optional<std::string> token;
    uint64_t nonce;
    std::optional<std::string> signature;
};

/** Rollup batch */
struct Batch {
    uint64_t batch_number;
    std::string prev_state_root;
    std::string new_state_root;
    std::vector<Transfer> transfers;
    std::vector<Deposit> deposits;
    std::vector<Withdrawal> withdrawals;
    std::optional<Proof> proof;
    uint64_t timestamp;
};

/** Rollup statistics */
struct RollupStats {
    uint64_t current_batch;
    uint64_t total_transactions;
    uint64_t total_proofs;
    uint64_t avg_proof_time_ms;
    std::string state_root;
    uint64_t account_count;
    std::string tvl;
};

/** Proof request */
struct ProofRequest {
    CircuitType circuit_type;
    std::vector<std::string> public_inputs;
    std::vector<std::string> private_inputs;
    std::optional<ProofSystem> system;
};

/** Verification result */
struct VerificationResult {
    bool valid;
    uint64_t verification_time_ms;
    std::optional<std::string> error;
};

/** Merkle proof for state inclusion */
struct MerkleProof {
    std::string leaf;
    std::vector<std::string> path;
    std::vector<uint8_t> indices;
    std::string root;
};

/** Ceremony contribution */
struct CeremonyContribution {
    std::string contributor_id;
    std::string hash;
    uint64_t timestamp;
    bool verified;
};

/** Trusted setup ceremony */
struct TrustedSetup {
    std::string id;
    CircuitType circuit_type;
    ProofSystem system;
    size_t contribution_count;
    std::string latest_hash;
    bool complete;
    std::vector<CeremonyContribution> contributions;
};

/** SDK configuration */
struct ZkConfig {
    std::string api_key;
    std::string endpoint = "https://zk.synor.io/v1";
    std::string ws_endpoint = "wss://zk.synor.io/v1/ws";
    std::chrono::milliseconds timeout{60000};
    int retries = 3;
    ProofSystem default_proof_system = ProofSystem::Groth16;
    bool debug = false;
};

/** Proof system characteristics */
struct ProofSystemInfo {
    std::string name;
    size_t proof_size;
    uint32_t verification_time_ms;
    bool trusted_setup;
    bool universal;
};

/* ============================================================================
 * Client Classes
 * ============================================================================ */

class SynorZk;

/** Proofs sub-client */
class ProofsClient {
public:
    explicit ProofsClient(SynorZk& zk) : zk_(zk) {}

    std::future<Proof> generate(const ProofRequest& request);
    std::future<VerificationResult> verify(const Proof& proof);
    std::future<Proof> get(const std::string& proof_id);
    std::future<std::vector<Proof>> list(std::optional<int> limit = std::nullopt,
                                          std::optional<int> offset = std::nullopt);
    std::future<std::vector<uint8_t>> serialize(const Proof& proof);
    std::future<Proof> deserialize(const std::vector<uint8_t>& data, ProofSystem system);

private:
    SynorZk& zk_;
};

/** Circuits sub-client */
class CircuitsClient {
public:
    explicit CircuitsClient(SynorZk& zk) : zk_(zk) {}

    std::future<VerificationKey> getVerificationKey(CircuitType circuit_type,
                                                     std::optional<ProofSystem> system = std::nullopt);
    std::future<ProvingKey> getProvingKey(CircuitType circuit_type,
                                           std::optional<ProofSystem> system = std::nullopt);
    std::future<std::vector<CircuitConfig>> list();
    std::future<CircuitConfig> getConfig(CircuitType circuit_type);
    std::future<std::string> compile(const CircuitConfig& config);

private:
    SynorZk& zk_;
};

/** Rollup sub-client */
class RollupClient {
public:
    explicit RollupClient(SynorZk& zk) : zk_(zk) {}

    std::future<RollupStats> getStats();
    std::future<Batch> getCurrentBatch();
    std::future<Batch> getBatch(uint64_t batch_number);
    std::future<std::string> submitTransfer(const Transfer& transfer);
    std::future<std::string> submitDeposit(const Deposit& deposit);
    std::future<std::string> submitWithdrawal(const Withdrawal& withdrawal);
    std::future<Batch> finalizeBatch();
    std::future<std::vector<Transfer>> getPendingTransactions();

private:
    SynorZk& zk_;
};

/** State sub-client */
class StateClient {
public:
    explicit StateClient(SynorZk& zk) : zk_(zk) {}

    std::future<std::string> getRoot();
    std::future<AccountState> getAccount(const std::string& address);
    std::future<MerkleProof> getMerkleProof(const std::string& address);
    std::future<bool> verifyMerkleProof(const MerkleProof& proof);
    std::future<std::map<std::string, std::string>> getStateAtBatch(uint64_t batch_number);

private:
    SynorZk& zk_;
};

/** Ceremony sub-client */
class CeremonyClient {
public:
    explicit CeremonyClient(SynorZk& zk) : zk_(zk) {}

    std::future<TrustedSetup> getStatus(CircuitType circuit_type,
                                         std::optional<ProofSystem> system = std::nullopt);
    std::future<CeremonyContribution> contribute(CircuitType circuit_type,
                                                  const std::vector<uint8_t>& entropy,
                                                  std::optional<ProofSystem> system = std::nullopt);
    std::future<bool> verifyContribution(CircuitType circuit_type,
                                          const std::string& contribution_hash);
    std::future<std::vector<CeremonyContribution>> listContributions(CircuitType circuit_type);

private:
    SynorZk& zk_;
};

/**
 * Synor ZK SDK Client
 *
 * Zero-Knowledge proof systems for ZK-Rollups and privacy.
 *
 * Example:
 * @code
 * synor::zk::ZkConfig config;
 * config.api_key = "your-api-key";
 * synor::zk::SynorZk zk(config);
 *
 * // Generate a proof
 * synor::zk::ProofRequest request;
 * request.circuit_type = synor::zk::CircuitType::Transfer;
 * request.public_inputs = {...};
 * request.private_inputs = {...};
 * auto proof = zk.proofs().generate(request).get();
 *
 * // Verify the proof
 * auto result = zk.proofs().verify(proof).get();
 * std::cout << "Valid: " << result.valid << std::endl;
 * @endcode
 */
class SynorZk {
public:
    explicit SynorZk(const ZkConfig& config);
    ~SynorZk();

    // Non-copyable
    SynorZk(const SynorZk&) = delete;
    SynorZk& operator=(const SynorZk&) = delete;

    // Movable
    SynorZk(SynorZk&&) noexcept;
    SynorZk& operator=(SynorZk&&) noexcept;

    /** Get the default proof system */
    ProofSystem defaultProofSystem() const { return config_.default_proof_system; }

    /** Health check */
    std::future<bool> healthCheck();

    /** Get service info */
    std::future<std::map<std::string, std::string>> getInfo();

    /** Close the client */
    void close();

    /** Check if client is closed */
    bool isClosed() const { return closed_; }

    /** Sub-clients */
    ProofsClient& proofs() { return proofs_; }
    CircuitsClient& circuits() { return circuits_; }
    RollupClient& rollup() { return rollup_; }
    StateClient& state() { return state_; }
    CeremonyClient& ceremony() { return ceremony_; }

    // Internal HTTP methods (public for sub-clients)
    std::future<std::map<std::string, std::string>> get(const std::string& path);
    std::future<std::map<std::string, std::string>> post(const std::string& path,
                                                          const std::map<std::string, std::string>& body);

private:
    ZkConfig config_;
    bool closed_ = false;
    ProofsClient proofs_;
    CircuitsClient circuits_;
    RollupClient rollup_;
    StateClient state_;
    CeremonyClient ceremony_;
};

/* ============================================================================
 * Utility Functions
 * ============================================================================ */

/** Get proof system info */
ProofSystemInfo getProofSystemInfo(ProofSystem system);

/** Convert proof system to string */
std::string proofSystemToString(ProofSystem system);

/** Convert circuit type to string */
std::string circuitTypeToString(CircuitType type);

/* ============================================================================
 * Constants
 * ============================================================================ */

constexpr size_t MAX_BATCH_SIZE = 1000;
constexpr size_t STATE_TREE_DEPTH = 32;
constexpr uint64_t PROOF_EXPIRY_BLOCKS = 100;

} // namespace zk
} // namespace synor

#endif // SYNOR_ZK_HPP
