/**
 * Synor Contract SDK for C++
 * Smart contract deployment, interaction, and event handling.
 */

#ifndef SYNOR_CONTRACT_HPP
#define SYNOR_CONTRACT_HPP

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

namespace synor::contract {

inline constexpr const char* VERSION = "0.1.0";

// ==================== Exceptions ====================

class ContractException : public std::runtime_error {
public:
    ContractException(const std::string& message,
                      std::optional<int> status_code = std::nullopt,
                      std::optional<std::string> code = std::nullopt)
        : std::runtime_error(message), status_code_(status_code), code_(code) {}

    std::optional<int> status_code() const { return status_code_; }
    std::optional<std::string> code() const { return code_; }

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

// ==================== Configuration ====================

struct ContractConfig {
    std::string api_key;
    std::string endpoint = "https://contract.synor.io";
    int timeout_ms = 30000;
    int retries = 3;
};

// ==================== ABI Types ====================

struct AbiParameter {
    std::optional<std::string> name;
    std::string type;
    std::optional<bool> indexed;
    std::optional<std::vector<AbiParameter>> components;
};

struct AbiEntry {
    std::string type;
    std::optional<std::string> name;
    std::optional<std::vector<AbiParameter>> inputs;
    std::optional<std::vector<AbiParameter>> outputs;
    std::optional<std::string> state_mutability;
    std::optional<bool> anonymous;
};

using Abi = std::vector<AbiEntry>;

// ==================== Deployment ====================

struct DeployContractOptions {
    std::string bytecode;
    std::optional<Abi> abi;
    std::optional<std::vector<std::any>> constructor_args;
    std::optional<std::string> value;
    std::optional<int64_t> gas_limit;
    std::optional<std::string> gas_price;
    std::optional<int64_t> nonce;
};

struct DeploymentResult {
    std::string contract_address;
    std::string transaction_hash;
    std::optional<std::string> deployer;
    std::optional<int64_t> gas_used;
    std::optional<int64_t> block_number;
    std::optional<std::string> block_hash;
};

// ==================== Contract Interaction ====================

struct CallContractOptions {
    std::string contract;
    std::string method;
    std::vector<std::any> args;
    Abi abi;
};

struct SendContractOptions {
    std::string contract;
    std::string method;
    std::vector<std::any> args;
    Abi abi;
    std::optional<std::string> value;
    std::optional<int64_t> gas_limit;
    std::optional<std::string> gas_price;
    std::optional<int64_t> nonce;
};

struct EventLog {
    std::string address;
    std::vector<std::string> topics;
    std::string data;
    std::optional<int64_t> block_number;
    std::optional<std::string> transaction_hash;
    std::optional<int> log_index;
    std::optional<std::string> block_hash;
    std::optional<bool> removed;
};

struct TransactionResult {
    std::string transaction_hash;
    std::optional<int64_t> block_number;
    std::optional<std::string> block_hash;
    std::optional<int64_t> gas_used;
    std::optional<std::string> effective_gas_price;
    std::optional<std::string> status;
    std::optional<std::vector<EventLog>> logs;
};

// ==================== Events ====================

struct DecodedEvent {
    std::string name;
    std::optional<std::string> signature;
    std::optional<std::map<std::string, std::any>> args;
    std::optional<EventLog> log;
};

struct EventFilter {
    std::string contract;
    std::optional<std::string> event;
    std::optional<int64_t> from_block;
    std::optional<int64_t> to_block;
    std::optional<std::vector<std::optional<std::string>>> topics;
    std::optional<Abi> abi;
};

// ==================== ABI Utilities ====================

struct EncodeCallOptions {
    std::string method;
    std::vector<std::any> args;
    Abi abi;
};

struct DecodeResultOptions {
    std::string data;
    std::string method;
    Abi abi;
};

// ==================== Gas Estimation ====================

struct EstimateGasOptions {
    std::string contract;
    std::string method;
    std::vector<std::any> args;
    Abi abi;
    std::optional<std::string> value;
};

struct GasEstimation {
    int64_t gas_limit;
    std::optional<std::string> gas_price;
    std::optional<std::string> max_fee_per_gas;
    std::optional<std::string> max_priority_fee_per_gas;
    std::optional<std::string> estimated_cost;
};

// ==================== Contract Information ====================

struct BytecodeInfo {
    std::string bytecode;
    std::optional<std::string> deployed_bytecode;
    std::optional<int> size;
    std::optional<bool> is_contract;
};

struct VerifyContractOptions {
    std::string address;
    std::string source_code;
    std::string compiler_version;
    std::optional<std::string> constructor_args;
    std::optional<bool> optimization;
    std::optional<int> optimization_runs;
    std::optional<std::string> license;
};

struct VerificationResult {
    bool verified;
    std::optional<std::string> address;
    std::optional<std::string> compiler_version;
    std::optional<bool> optimization;
    std::optional<int> optimization_runs;
    std::optional<std::string> license;
    std::optional<Abi> abi;
    std::optional<std::string> source_code;
};

// ==================== Multicall ====================

struct MulticallRequest {
    std::string contract;
    std::string method;
    std::vector<std::any> args;
    Abi abi;
};

struct MulticallResult {
    bool success;
    std::optional<std::any> result;
    std::optional<std::string> error;
};

// ==================== Storage ====================

struct ReadStorageOptions {
    std::string contract;
    std::string slot;
    std::optional<int64_t> block_number;
};

// ==================== Client ====================

class ContractClient {
public:
    explicit ContractClient(const ContractConfig& config);
    ~ContractClient();

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

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

    // Deployment
    std::future<DeploymentResult> deploy(const DeployContractOptions& options);
    std::future<DeploymentResult> deploy_create2(const DeployContractOptions& options, const std::string& salt);
    std::future<std::string> predict_address(const std::string& bytecode, const std::string& salt,
                                              const std::optional<std::string>& deployer = std::nullopt);

    // Contract Interaction
    std::future<std::any> call(const CallContractOptions& options);
    std::future<TransactionResult> send(const SendContractOptions& options);

    // Events
    std::future<std::vector<DecodedEvent>> get_events(const EventFilter& filter);
    std::future<std::vector<EventLog>> get_logs(const std::string& contract,
                                                  std::optional<int64_t> from_block = std::nullopt,
                                                  std::optional<int64_t> to_block = std::nullopt);
    std::future<std::vector<DecodedEvent>> decode_logs(const std::vector<EventLog>& logs, const Abi& abi);

    // ABI Utilities
    std::future<std::string> encode_call(const EncodeCallOptions& options);
    std::future<std::any> decode_result(const DecodeResultOptions& options);
    std::future<std::string> get_selector(const std::string& signature);

    // Gas Estimation
    std::future<GasEstimation> estimate_gas(const EstimateGasOptions& options);

    // Contract Information
    std::future<BytecodeInfo> get_bytecode(const std::string& address);
    std::future<VerificationResult> verify(const VerifyContractOptions& options);
    std::future<VerificationResult> get_verification_status(const std::string& address);

    // Multicall
    std::future<std::vector<MulticallResult>> multicall(const std::vector<MulticallRequest>& requests);

    // Storage
    std::future<std::string> read_storage(const ReadStorageOptions& options);

    // Lifecycle
    std::future<bool> health_check();
    void close();
    bool is_closed() const;

private:
    class Impl;
    std::unique_ptr<Impl> impl_;
};

} // namespace synor::contract

#endif // SYNOR_CONTRACT_HPP
