package io.synor.rpc;

import java.util.List;
import java.util.concurrent.CompletionStage;

/** Network type. */
public enum Network {
    MAINNET("mainnet"), TESTNET("testnet");
    private final String value;
    Network(String value) { this.value = value; }
    public String getValue() { return value; }
}

/** Transaction priority. */
public enum Priority {
    LOW("low"), MEDIUM("medium"), HIGH("high"), URGENT("urgent");
    private final String value;
    Priority(String value) { this.value = value; }
    public String getValue() { return value; }
}

/** Transaction status. */
public enum TransactionStatus {
    PENDING("pending"), CONFIRMED("confirmed"), FAILED("failed"), REPLACED("replaced");
    private final String value;
    TransactionStatus(String value) { this.value = value; }
    public String getValue() { return value; }
}

/** RPC configuration. */
class RpcConfig {
    private final String apiKey;
    private final String endpoint;
    private final String wsEndpoint;
    private final Network network;
    private final long timeoutSeconds;
    private final boolean debug;

    private RpcConfig(Builder builder) {
        this.apiKey = builder.apiKey;
        this.endpoint = builder.endpoint;
        this.wsEndpoint = builder.wsEndpoint;
        this.network = builder.network;
        this.timeoutSeconds = builder.timeoutSeconds;
        this.debug = builder.debug;
    }

    public String getApiKey() { return apiKey; }
    public String getEndpoint() { return endpoint; }
    public String getWsEndpoint() { return wsEndpoint; }
    public Network getNetwork() { return network; }
    public long getTimeoutSeconds() { return timeoutSeconds; }
    public boolean isDebug() { return debug; }

    public static Builder builder() { return new Builder(); }

    public static class Builder {
        private String apiKey;
        private String endpoint = "https://rpc.synor.cc/api/v1";
        private String wsEndpoint = "wss://rpc.synor.cc/ws";
        private Network network = Network.MAINNET;
        private long timeoutSeconds = 30;
        private boolean debug = false;

        public Builder apiKey(String apiKey) { this.apiKey = apiKey; return this; }
        public Builder endpoint(String endpoint) { this.endpoint = endpoint; return this; }
        public Builder wsEndpoint(String wsEndpoint) { this.wsEndpoint = wsEndpoint; return this; }
        public Builder network(Network network) { this.network = network; return this; }
        public Builder timeoutSeconds(long seconds) { this.timeoutSeconds = seconds; return this; }
        public Builder debug(boolean debug) { this.debug = debug; return this; }
        public RpcConfig build() { return new RpcConfig(this); }
    }
}

/** Block header. */
class BlockHeader {
    private String hash;
    private long height;
    private int version;
    private String previousHash;
    private String merkleRoot;
    private long timestamp;
    private String difficulty;
    private long nonce;

    public String getHash() { return hash; }
    public long getHeight() { return height; }
    public int getVersion() { return version; }
    public String getPreviousHash() { return previousHash; }
    public String getMerkleRoot() { return merkleRoot; }
    public long getTimestamp() { return timestamp; }
    public String getDifficulty() { return difficulty; }
    public long getNonce() { return nonce; }
}

/** Full block. */
class Block extends BlockHeader {
    private List<String> transactions;
    private int size;
    private int weight;
    private int txCount;

    public List<String> getTransactions() { return transactions; }
    public int getSize() { return size; }
    public int getWeight() { return weight; }
    public int getTxCount() { return txCount; }
}

/** Transaction. */
class Transaction {
    private String txid;
    private String blockHash;
    private long blockHeight;
    private int confirmations;
    private long timestamp;
    private TransactionStatus status;
    private String raw;
    private int size;
    private String fee;

    public String getTxid() { return txid; }
    public String getBlockHash() { return blockHash; }
    public long getBlockHeight() { return blockHeight; }
    public int getConfirmations() { return confirmations; }
    public long getTimestamp() { return timestamp; }
    public TransactionStatus getStatus() { return status; }
    public String getRaw() { return raw; }
    public int getSize() { return size; }
    public String getFee() { return fee; }
}

/** Fee estimate. */
class FeeEstimate {
    private Priority priority;
    private String feeRate;
    private int estimatedBlocks;

    public Priority getPriority() { return priority; }
    public String getFeeRate() { return feeRate; }
    public int getEstimatedBlocks() { return estimatedBlocks; }
}

/** Chain information. */
class ChainInfo {
    private String chain;
    private String network;
    private long height;
    private String bestBlockHash;
    private String difficulty;
    private long medianTime;
    private String chainWork;
    private boolean syncing;
    private double syncProgress;

    public String getChain() { return chain; }
    public String getNetwork() { return network; }
    public long getHeight() { return height; }
    public String getBestBlockHash() { return bestBlockHash; }
    public String getDifficulty() { return difficulty; }
    public long getMedianTime() { return medianTime; }
    public String getChainWork() { return chainWork; }
    public boolean isSyncing() { return syncing; }
    public double getSyncProgress() { return syncProgress; }
}

/** Mempool information. */
class MempoolInfo {
    private int size;
    private long bytes;
    private long usage;
    private long maxMempool;
    private String minFee;

    public int getSize() { return size; }
    public long getBytes() { return bytes; }
    public long getUsage() { return usage; }
    public long getMaxMempool() { return maxMempool; }
    public String getMinFee() { return minFee; }
}

/** UTXO. */
class UTXO {
    private String txid;
    private int vout;
    private String amount;
    private String address;
    private int confirmations;

    public String getTxid() { return txid; }
    public int getVout() { return vout; }
    public String getAmount() { return amount; }
    public String getAddress() { return address; }
    public int getConfirmations() { return confirmations; }
}

/** Balance. */
class Balance {
    private String confirmed;
    private String unconfirmed;
    private String total;

    public String getConfirmed() { return confirmed; }
    public String getUnconfirmed() { return unconfirmed; }
    public String getTotal() { return total; }
}

/** Subscription. */
class Subscription {
    private final String id;
    private final String type;
    private final long createdAt;
    private final Runnable unsubscribe;

    public Subscription(String id, String type, long createdAt, Runnable unsubscribe) {
        this.id = id;
        this.type = type;
        this.createdAt = createdAt;
        this.unsubscribe = unsubscribe;
    }

    public String getId() { return id; }
    public String getType() { return type; }
    public long getCreatedAt() { return createdAt; }
    public void unsubscribe() { unsubscribe.run(); }
}

/** RPC exception. */
class RpcException extends RuntimeException {
    private final int statusCode;
    private final String code;

    public RpcException(String message, int statusCode, String code) {
        super(message);
        this.statusCode = statusCode;
        this.code = code;
    }

    public int getStatusCode() { return statusCode; }
    public String getCode() { return code; }
}
