# frozen_string_literal: true

module SynorContract
  # Configuration for the Contract SDK.
  class Config
    attr_accessor :api_key, :endpoint, :timeout, :retries

    def initialize(api_key:, endpoint: "https://contract.synor.io", timeout: 30, retries: 3)
      @api_key = api_key
      @endpoint = endpoint
      @timeout = timeout
      @retries = retries
    end
  end

  # ABI parameter.
  class AbiParameter
    attr_accessor :name, :type, :indexed, :components

    def initialize(name: nil, type:, indexed: nil, components: nil)
      @name = name
      @type = type
      @indexed = indexed
      @components = components
    end

    def to_h
      h = { type: @type }
      h[:name] = @name if @name
      h[:indexed] = @indexed unless @indexed.nil?
      h[:components] = @components.map(&:to_h) if @components
      h
    end
  end

  # ABI entry.
  class AbiEntry
    attr_accessor :type, :name, :inputs, :outputs, :state_mutability, :anonymous

    def initialize(type:, name: nil, inputs: nil, outputs: nil, state_mutability: nil, anonymous: nil)
      @type = type
      @name = name
      @inputs = inputs
      @outputs = outputs
      @state_mutability = state_mutability
      @anonymous = anonymous
    end

    def to_h
      h = { type: @type }
      h[:name] = @name if @name
      h[:inputs] = @inputs.map(&:to_h) if @inputs
      h[:outputs] = @outputs.map(&:to_h) if @outputs
      h[:stateMutability] = @state_mutability if @state_mutability
      h[:anonymous] = @anonymous unless @anonymous.nil?
      h
    end
  end

  # Deploy contract options.
  class DeployContractOptions
    attr_accessor :bytecode, :abi, :constructor_args, :value, :gas_limit, :gas_price, :nonce

    def initialize(bytecode:, abi: nil, constructor_args: nil, value: nil, gas_limit: nil, gas_price: nil, nonce: nil)
      @bytecode = bytecode
      @abi = abi
      @constructor_args = constructor_args
      @value = value
      @gas_limit = gas_limit
      @gas_price = gas_price
      @nonce = nonce
    end
  end

  # Deployment result.
  class DeploymentResult
    attr_accessor :contract_address, :transaction_hash, :deployer, :gas_used, :block_number, :block_hash

    def initialize(contract_address:, transaction_hash:, deployer: nil, gas_used: nil, block_number: nil, block_hash: nil)
      @contract_address = contract_address
      @transaction_hash = transaction_hash
      @deployer = deployer
      @gas_used = gas_used
      @block_number = block_number
      @block_hash = block_hash
    end
  end

  # Call contract options.
  class CallContractOptions
    attr_accessor :contract, :method, :args, :abi

    def initialize(contract:, method:, args: [], abi:)
      @contract = contract
      @method = method
      @args = args
      @abi = abi
    end
  end

  # Send contract options.
  class SendContractOptions
    attr_accessor :contract, :method, :args, :abi, :value, :gas_limit, :gas_price, :nonce

    def initialize(contract:, method:, args: [], abi:, value: nil, gas_limit: nil, gas_price: nil, nonce: nil)
      @contract = contract
      @method = method
      @args = args
      @abi = abi
      @value = value
      @gas_limit = gas_limit
      @gas_price = gas_price
      @nonce = nonce
    end
  end

  # Event log.
  class EventLog
    attr_accessor :address, :topics, :data, :block_number, :transaction_hash, :log_index, :block_hash, :removed

    def initialize(address:, topics:, data:, block_number: nil, transaction_hash: nil, log_index: nil, block_hash: nil, removed: nil)
      @address = address
      @topics = topics
      @data = data
      @block_number = block_number
      @transaction_hash = transaction_hash
      @log_index = log_index
      @block_hash = block_hash
      @removed = removed
    end

    def to_h
      h = { address: @address, topics: @topics, data: @data }
      h[:block_number] = @block_number if @block_number
      h[:transaction_hash] = @transaction_hash if @transaction_hash
      h[:log_index] = @log_index if @log_index
      h[:block_hash] = @block_hash if @block_hash
      h[:removed] = @removed unless @removed.nil?
      h
    end
  end

  # Transaction result.
  class TransactionResult
    attr_accessor :transaction_hash, :block_number, :block_hash, :gas_used, :effective_gas_price, :status, :logs

    def initialize(transaction_hash:, block_number: nil, block_hash: nil, gas_used: nil, effective_gas_price: nil, status: nil, logs: nil)
      @transaction_hash = transaction_hash
      @block_number = block_number
      @block_hash = block_hash
      @gas_used = gas_used
      @effective_gas_price = effective_gas_price
      @status = status
      @logs = logs
    end
  end

  # Decoded event.
  class DecodedEvent
    attr_accessor :name, :signature, :args, :log

    def initialize(name:, signature: nil, args: nil, log: nil)
      @name = name
      @signature = signature
      @args = args
      @log = log
    end
  end

  # Event filter.
  class EventFilter
    attr_accessor :contract, :event, :from_block, :to_block, :topics, :abi

    def initialize(contract:, event: nil, from_block: nil, to_block: nil, topics: nil, abi: nil)
      @contract = contract
      @event = event
      @from_block = from_block
      @to_block = to_block
      @topics = topics
      @abi = abi
    end
  end

  # Encode call options.
  class EncodeCallOptions
    attr_accessor :method, :args, :abi

    def initialize(method:, args: [], abi:)
      @method = method
      @args = args
      @abi = abi
    end
  end

  # Decode result options.
  class DecodeResultOptions
    attr_accessor :data, :method, :abi

    def initialize(data:, method:, abi:)
      @data = data
      @method = method
      @abi = abi
    end
  end

  # Estimate gas options.
  class EstimateGasOptions
    attr_accessor :contract, :method, :args, :abi, :value

    def initialize(contract:, method:, args: [], abi:, value: nil)
      @contract = contract
      @method = method
      @args = args
      @abi = abi
      @value = value
    end
  end

  # Gas estimation result.
  class GasEstimation
    attr_accessor :gas_limit, :gas_price, :max_fee_per_gas, :max_priority_fee_per_gas, :estimated_cost

    def initialize(gas_limit:, gas_price: nil, max_fee_per_gas: nil, max_priority_fee_per_gas: nil, estimated_cost: nil)
      @gas_limit = gas_limit
      @gas_price = gas_price
      @max_fee_per_gas = max_fee_per_gas
      @max_priority_fee_per_gas = max_priority_fee_per_gas
      @estimated_cost = estimated_cost
    end
  end

  # Bytecode info.
  class BytecodeInfo
    attr_accessor :bytecode, :deployed_bytecode, :size, :is_contract

    def initialize(bytecode:, deployed_bytecode: nil, size: nil, is_contract: nil)
      @bytecode = bytecode
      @deployed_bytecode = deployed_bytecode
      @size = size
      @is_contract = is_contract
    end
  end

  # Verify contract options.
  class VerifyContractOptions
    attr_accessor :address, :source_code, :compiler_version, :constructor_args, :optimization, :optimization_runs, :license

    def initialize(address:, source_code:, compiler_version:, constructor_args: nil, optimization: nil, optimization_runs: nil, license: nil)
      @address = address
      @source_code = source_code
      @compiler_version = compiler_version
      @constructor_args = constructor_args
      @optimization = optimization
      @optimization_runs = optimization_runs
      @license = license
    end
  end

  # Verification result.
  class VerificationResult
    attr_accessor :verified, :address, :compiler_version, :optimization, :optimization_runs, :license, :abi, :source_code

    def initialize(verified:, address: nil, compiler_version: nil, optimization: nil, optimization_runs: nil, license: nil, abi: nil, source_code: nil)
      @verified = verified
      @address = address
      @compiler_version = compiler_version
      @optimization = optimization
      @optimization_runs = optimization_runs
      @license = license
      @abi = abi
      @source_code = source_code
    end
  end

  # Multicall request.
  class MulticallRequest
    attr_accessor :contract, :method, :args, :abi

    def initialize(contract:, method:, args: [], abi:)
      @contract = contract
      @method = method
      @args = args
      @abi = abi
    end

    def to_h
      { contract: @contract, method: @method, args: @args, abi: @abi.map(&:to_h) }
    end
  end

  # Multicall result.
  class MulticallResult
    attr_accessor :success, :result, :error

    def initialize(success:, result: nil, error: nil)
      @success = success
      @result = result
      @error = error
    end
  end

  # Read storage options.
  class ReadStorageOptions
    attr_accessor :contract, :slot, :block_number

    def initialize(contract:, slot:, block_number: nil)
      @contract = contract
      @slot = slot
      @block_number = block_number
    end
  end

  # Client closed error.
  class ClientClosedError < StandardError; end

  # HTTP error.
  class HttpError < StandardError
    attr_reader :status_code, :code

    def initialize(message, status_code: nil, code: nil)
      super(message)
      @status_code = status_code
      @code = code
    end
  end
end
