package io.synor.zk;

import java.util.List;
import java.util.Map;

/**
 * Synor ZK SDK Types for Java
 *
 * Zero-Knowledge proof systems for ZK-Rollups and privacy.
 */
public class Types {

    /** Proof system backends */
    public enum ProofSystem {
        /** Groth16 - smallest proofs (~192 bytes), fastest verification */
        GROTH16("groth16"),
        /** PLONK - universal trusted setup, medium proofs (~512 bytes) */
        PLONK("plonk"),
        /** STARK - no trusted setup, largest proofs (~50KB) */
        STARK("stark");

        private final String value;

        ProofSystem(String value) {
            this.value = value;
        }

        public String getValue() {
            return value;
        }

        public static ProofSystem fromString(String value) {
            for (ProofSystem system : values()) {
                if (system.value.equals(value)) {
                    return system;
                }
            }
            return GROTH16;
        }
    }

    /** Circuit types for different operations */
    public enum CircuitType {
        /** Single transfer between accounts */
        TRANSFER("transfer"),
        /** Batch of multiple transfers */
        BATCH("batch"),
        /** Deposit from L1 to L2 */
        DEPOSIT("deposit"),
        /** Withdrawal from L2 to L1 */
        WITHDRAW("withdraw");

        private final String value;

        CircuitType(String value) {
            this.value = value;
        }

        public String getValue() {
            return value;
        }

        public static CircuitType fromString(String value) {
            for (CircuitType type : values()) {
                if (type.value.equals(value)) {
                    return type;
                }
            }
            return TRANSFER;
        }
    }

    /** Rollup state */
    public enum RollupState {
        ACTIVE, PAUSED, FINALIZING, FINALIZED
    }

    /** Proof status */
    public enum ProofStatus {
        GENERATING, COMPLETED, FAILED, VERIFIED
    }

    /** Zero-knowledge proof */
    public static class Proof {
        private String id;
        private ProofSystem system;
        private CircuitType circuitType;
        private String data;
        private List<String> publicInputs;
        private int size;
        private long generationTimeMs;
        private long createdAt;

        public Proof() {}

        public Proof(String id, ProofSystem system, CircuitType circuitType, String data,
                     List<String> publicInputs, int size, long generationTimeMs, long createdAt) {
            this.id = id;
            this.system = system;
            this.circuitType = circuitType;
            this.data = data;
            this.publicInputs = publicInputs;
            this.size = size;
            this.generationTimeMs = generationTimeMs;
            this.createdAt = createdAt;
        }

        public String getId() { return id; }
        public void setId(String id) { this.id = id; }
        public ProofSystem getSystem() { return system; }
        public void setSystem(ProofSystem system) { this.system = system; }
        public CircuitType getCircuitType() { return circuitType; }
        public void setCircuitType(CircuitType circuitType) { this.circuitType = circuitType; }
        public String getData() { return data; }
        public void setData(String data) { this.data = data; }
        public List<String> getPublicInputs() { return publicInputs; }
        public void setPublicInputs(List<String> publicInputs) { this.publicInputs = publicInputs; }
        public int getSize() { return size; }
        public void setSize(int size) { this.size = size; }
        public long getGenerationTimeMs() { return generationTimeMs; }
        public void setGenerationTimeMs(long generationTimeMs) { this.generationTimeMs = generationTimeMs; }
        public long getCreatedAt() { return createdAt; }
        public void setCreatedAt(long createdAt) { this.createdAt = createdAt; }
    }

    /** Verification key for a circuit */
    public static class VerificationKey {
        private String circuitId;
        private CircuitType circuitType;
        private ProofSystem system;
        private String data;
        private int size;

        public String getCircuitId() { return circuitId; }
        public void setCircuitId(String circuitId) { this.circuitId = circuitId; }
        public CircuitType getCircuitType() { return circuitType; }
        public void setCircuitType(CircuitType circuitType) { this.circuitType = circuitType; }
        public ProofSystem getSystem() { return system; }
        public void setSystem(ProofSystem system) { this.system = system; }
        public String getData() { return data; }
        public void setData(String data) { this.data = data; }
        public int getSize() { return size; }
        public void setSize(int size) { this.size = size; }
    }

    /** Proving key for generating proofs */
    public static class ProvingKey {
        private String circuitId;
        private CircuitType circuitType;
        private ProofSystem system;
        private String data;
        private int size;

        public String getCircuitId() { return circuitId; }
        public void setCircuitId(String circuitId) { this.circuitId = circuitId; }
        public CircuitType getCircuitType() { return circuitType; }
        public void setCircuitType(CircuitType circuitType) { this.circuitType = circuitType; }
        public ProofSystem getSystem() { return system; }
        public void setSystem(ProofSystem system) { this.system = system; }
        public String getData() { return data; }
        public void setData(String data) { this.data = data; }
        public int getSize() { return size; }
        public void setSize(int size) { this.size = size; }
    }

    /** Circuit configuration */
    public static class CircuitConfig {
        private CircuitType type;
        private Integer maxBatchSize;
        private Integer treeDepth;
        private Boolean verifySignatures;
        private Map<String, Object> customConstraints;

        public CircuitType getType() { return type; }
        public void setType(CircuitType type) { this.type = type; }
        public Integer getMaxBatchSize() { return maxBatchSize; }
        public void setMaxBatchSize(Integer maxBatchSize) { this.maxBatchSize = maxBatchSize; }
        public Integer getTreeDepth() { return treeDepth; }
        public void setTreeDepth(Integer treeDepth) { this.treeDepth = treeDepth; }
        public Boolean getVerifySignatures() { return verifySignatures; }
        public void setVerifySignatures(Boolean verifySignatures) { this.verifySignatures = verifySignatures; }
        public Map<String, Object> getCustomConstraints() { return customConstraints; }
        public void setCustomConstraints(Map<String, Object> customConstraints) { this.customConstraints = customConstraints; }
    }

    /** Account state in the rollup */
    public static class AccountState {
        private String address;
        private String balance;
        private long nonce;
        private String pubkeyHash;

        public String getAddress() { return address; }
        public void setAddress(String address) { this.address = address; }
        public String getBalance() { return balance; }
        public void setBalance(String balance) { this.balance = balance; }
        public long getNonce() { return nonce; }
        public void setNonce(long nonce) { this.nonce = nonce; }
        public String getPubkeyHash() { return pubkeyHash; }
        public void setPubkeyHash(String pubkeyHash) { this.pubkeyHash = pubkeyHash; }
    }

    /** Transfer operation */
    public static class Transfer {
        private String from;
        private String to;
        private String amount;
        private String fee;
        private long nonce;
        private String signature;

        public Transfer() {}

        public Transfer(String from, String to, String amount, String fee, long nonce) {
            this.from = from;
            this.to = to;
            this.amount = amount;
            this.fee = fee;
            this.nonce = nonce;
        }

        public String getFrom() { return from; }
        public void setFrom(String from) { this.from = from; }
        public String getTo() { return to; }
        public void setTo(String to) { this.to = to; }
        public String getAmount() { return amount; }
        public void setAmount(String amount) { this.amount = amount; }
        public String getFee() { return fee; }
        public void setFee(String fee) { this.fee = fee; }
        public long getNonce() { return nonce; }
        public void setNonce(long nonce) { this.nonce = nonce; }
        public String getSignature() { return signature; }
        public void setSignature(String signature) { this.signature = signature; }
    }

    /** Deposit operation (L1 -> L2) */
    public static class Deposit {
        private String l1TxHash;
        private String recipient;
        private String amount;
        private String token;

        public Deposit() {}

        public Deposit(String l1TxHash, String recipient, String amount) {
            this.l1TxHash = l1TxHash;
            this.recipient = recipient;
            this.amount = amount;
        }

        public String getL1TxHash() { return l1TxHash; }
        public void setL1TxHash(String l1TxHash) { this.l1TxHash = l1TxHash; }
        public String getRecipient() { return recipient; }
        public void setRecipient(String recipient) { this.recipient = recipient; }
        public String getAmount() { return amount; }
        public void setAmount(String amount) { this.amount = amount; }
        public String getToken() { return token; }
        public void setToken(String token) { this.token = token; }
    }

    /** Withdrawal operation (L2 -> L1) */
    public static class Withdrawal {
        private String sender;
        private String recipient;
        private String amount;
        private String token;
        private long nonce;
        private String signature;

        public Withdrawal() {}

        public Withdrawal(String sender, String recipient, String amount, long nonce) {
            this.sender = sender;
            this.recipient = recipient;
            this.amount = amount;
            this.nonce = nonce;
        }

        public String getSender() { return sender; }
        public void setSender(String sender) { this.sender = sender; }
        public String getRecipient() { return recipient; }
        public void setRecipient(String recipient) { this.recipient = recipient; }
        public String getAmount() { return amount; }
        public void setAmount(String amount) { this.amount = amount; }
        public String getToken() { return token; }
        public void setToken(String token) { this.token = token; }
        public long getNonce() { return nonce; }
        public void setNonce(long nonce) { this.nonce = nonce; }
        public String getSignature() { return signature; }
        public void setSignature(String signature) { this.signature = signature; }
    }

    /** Rollup batch */
    public static class Batch {
        private long batchNumber;
        private String prevStateRoot;
        private String newStateRoot;
        private List<Transfer> transfers;
        private List<Deposit> deposits;
        private List<Withdrawal> withdrawals;
        private Proof proof;
        private long timestamp;

        public long getBatchNumber() { return batchNumber; }
        public void setBatchNumber(long batchNumber) { this.batchNumber = batchNumber; }
        public String getPrevStateRoot() { return prevStateRoot; }
        public void setPrevStateRoot(String prevStateRoot) { this.prevStateRoot = prevStateRoot; }
        public String getNewStateRoot() { return newStateRoot; }
        public void setNewStateRoot(String newStateRoot) { this.newStateRoot = newStateRoot; }
        public List<Transfer> getTransfers() { return transfers; }
        public void setTransfers(List<Transfer> transfers) { this.transfers = transfers; }
        public List<Deposit> getDeposits() { return deposits; }
        public void setDeposits(List<Deposit> deposits) { this.deposits = deposits; }
        public List<Withdrawal> getWithdrawals() { return withdrawals; }
        public void setWithdrawals(List<Withdrawal> withdrawals) { this.withdrawals = withdrawals; }
        public Proof getProof() { return proof; }
        public void setProof(Proof proof) { this.proof = proof; }
        public long getTimestamp() { return timestamp; }
        public void setTimestamp(long timestamp) { this.timestamp = timestamp; }
    }

    /** Rollup statistics */
    public static class RollupStats {
        private long currentBatch;
        private long totalTransactions;
        private long totalProofs;
        private long avgProofTimeMs;
        private String stateRoot;
        private long accountCount;
        private String tvl;

        public long getCurrentBatch() { return currentBatch; }
        public void setCurrentBatch(long currentBatch) { this.currentBatch = currentBatch; }
        public long getTotalTransactions() { return totalTransactions; }
        public void setTotalTransactions(long totalTransactions) { this.totalTransactions = totalTransactions; }
        public long getTotalProofs() { return totalProofs; }
        public void setTotalProofs(long totalProofs) { this.totalProofs = totalProofs; }
        public long getAvgProofTimeMs() { return avgProofTimeMs; }
        public void setAvgProofTimeMs(long avgProofTimeMs) { this.avgProofTimeMs = avgProofTimeMs; }
        public String getStateRoot() { return stateRoot; }
        public void setStateRoot(String stateRoot) { this.stateRoot = stateRoot; }
        public long getAccountCount() { return accountCount; }
        public void setAccountCount(long accountCount) { this.accountCount = accountCount; }
        public String getTvl() { return tvl; }
        public void setTvl(String tvl) { this.tvl = tvl; }
    }

    /** Proof generation request */
    public static class ProofRequest {
        private CircuitType circuitType;
        private List<String> publicInputs;
        private List<String> privateInputs;
        private ProofSystem system;

        public ProofRequest() {}

        public ProofRequest(CircuitType circuitType, List<String> publicInputs, List<String> privateInputs) {
            this.circuitType = circuitType;
            this.publicInputs = publicInputs;
            this.privateInputs = privateInputs;
        }

        public CircuitType getCircuitType() { return circuitType; }
        public void setCircuitType(CircuitType circuitType) { this.circuitType = circuitType; }
        public List<String> getPublicInputs() { return publicInputs; }
        public void setPublicInputs(List<String> publicInputs) { this.publicInputs = publicInputs; }
        public List<String> getPrivateInputs() { return privateInputs; }
        public void setPrivateInputs(List<String> privateInputs) { this.privateInputs = privateInputs; }
        public ProofSystem getSystem() { return system; }
        public void setSystem(ProofSystem system) { this.system = system; }
    }

    /** Proof verification result */
    public static class VerificationResult {
        private boolean valid;
        private long verificationTimeMs;
        private String error;

        public boolean isValid() { return valid; }
        public void setValid(boolean valid) { this.valid = valid; }
        public long getVerificationTimeMs() { return verificationTimeMs; }
        public void setVerificationTimeMs(long verificationTimeMs) { this.verificationTimeMs = verificationTimeMs; }
        public String getError() { return error; }
        public void setError(String error) { this.error = error; }
    }

    /** Merkle proof for state inclusion */
    public static class MerkleProof {
        private String leaf;
        private List<String> path;
        private List<Integer> indices;
        private String root;

        public String getLeaf() { return leaf; }
        public void setLeaf(String leaf) { this.leaf = leaf; }
        public List<String> getPath() { return path; }
        public void setPath(List<String> path) { this.path = path; }
        public List<Integer> getIndices() { return indices; }
        public void setIndices(List<Integer> indices) { this.indices = indices; }
        public String getRoot() { return root; }
        public void setRoot(String root) { this.root = root; }
    }

    /** Trusted setup ceremony contribution */
    public static class CeremonyContribution {
        private String contributorId;
        private String hash;
        private long timestamp;
        private boolean verified;

        public String getContributorId() { return contributorId; }
        public void setContributorId(String contributorId) { this.contributorId = contributorId; }
        public String getHash() { return hash; }
        public void setHash(String hash) { this.hash = hash; }
        public long getTimestamp() { return timestamp; }
        public void setTimestamp(long timestamp) { this.timestamp = timestamp; }
        public boolean isVerified() { return verified; }
        public void setVerified(boolean verified) { this.verified = verified; }
    }

    /** Trusted setup ceremony */
    public static class TrustedSetup {
        private String id;
        private CircuitType circuitType;
        private ProofSystem system;
        private int contributionCount;
        private String latestHash;
        private boolean complete;
        private List<CeremonyContribution> contributions;

        public String getId() { return id; }
        public void setId(String id) { this.id = id; }
        public CircuitType getCircuitType() { return circuitType; }
        public void setCircuitType(CircuitType circuitType) { this.circuitType = circuitType; }
        public ProofSystem getSystem() { return system; }
        public void setSystem(ProofSystem system) { this.system = system; }
        public int getContributionCount() { return contributionCount; }
        public void setContributionCount(int contributionCount) { this.contributionCount = contributionCount; }
        public String getLatestHash() { return latestHash; }
        public void setLatestHash(String latestHash) { this.latestHash = latestHash; }
        public boolean isComplete() { return complete; }
        public void setComplete(boolean complete) { this.complete = complete; }
        public List<CeremonyContribution> getContributions() { return contributions; }
        public void setContributions(List<CeremonyContribution> contributions) { this.contributions = contributions; }
    }

    /** ZK SDK configuration */
    public static class ZkConfig {
        private String apiKey;
        private String endpoint = "https://zk.synor.io/v1";
        private String wsEndpoint = "wss://zk.synor.io/v1/ws";
        private int timeout = 60000;
        private int retries = 3;
        private ProofSystem defaultProofSystem = ProofSystem.GROTH16;
        private boolean debug = false;

        public ZkConfig(String apiKey) {
            this.apiKey = apiKey;
        }

        public String getApiKey() { return apiKey; }
        public void setApiKey(String apiKey) { this.apiKey = apiKey; }
        public String getEndpoint() { return endpoint; }
        public void setEndpoint(String endpoint) { this.endpoint = endpoint; }
        public String getWsEndpoint() { return wsEndpoint; }
        public void setWsEndpoint(String wsEndpoint) { this.wsEndpoint = wsEndpoint; }
        public int getTimeout() { return timeout; }
        public void setTimeout(int timeout) { this.timeout = timeout; }
        public int getRetries() { return retries; }
        public void setRetries(int retries) { this.retries = retries; }
        public ProofSystem getDefaultProofSystem() { return defaultProofSystem; }
        public void setDefaultProofSystem(ProofSystem defaultProofSystem) { this.defaultProofSystem = defaultProofSystem; }
        public boolean isDebug() { return debug; }
        public void setDebug(boolean debug) { this.debug = debug; }
    }

    /** ZK SDK exception */
    public static class ZkException extends RuntimeException {
        private final String code;
        private final Integer status;

        public ZkException(String message) {
            super(message);
            this.code = null;
            this.status = null;
        }

        public ZkException(String message, String code) {
            super(message);
            this.code = code;
            this.status = null;
        }

        public ZkException(String message, String code, Integer status) {
            super(message);
            this.code = code;
            this.status = status;
        }

        public String getCode() { return code; }
        public Integer getStatus() { return status; }
    }

    /** Proof system characteristics */
    public static class ProofSystemInfo {
        public final String name;
        public final int proofSize;
        public final int verificationTimeMs;
        public final boolean trustedSetup;
        public final boolean universal;

        public ProofSystemInfo(String name, int proofSize, int verificationTimeMs,
                               boolean trustedSetup, boolean universal) {
            this.name = name;
            this.proofSize = proofSize;
            this.verificationTimeMs = verificationTimeMs;
            this.trustedSetup = trustedSetup;
            this.universal = universal;
        }
    }

    public static ProofSystemInfo getProofSystemInfo(ProofSystem system) {
        switch (system) {
            case GROTH16:
                return new ProofSystemInfo("Groth16", 192, 10, true, false);
            case PLONK:
                return new ProofSystemInfo("PLONK", 512, 15, true, true);
            case STARK:
                return new ProofSystemInfo("STARK", 50000, 30, false, true);
            default:
                return null;
        }
    }

    // Constants
    public static final int MAX_BATCH_SIZE = 1000;
    public static final int STATE_TREE_DEPTH = 32;
    public static final int PROOF_EXPIRY_BLOCKS = 100;
}
