require 'json'
require 'net/http'
require 'uri'

Result = Struct.new(:data, :error, :meta, :exec_time, keyword_init: true)

class SinglebaseClient
  def initialize(api_key:, api_url:, bearer: nil)
    @api_key, @api_url, @bearer = api_key, api_url, bearer
  end

  def call(operation:, payload:, options: nil)
    uri = URI(@api_url)
    request = Net::HTTP::Post.new(uri)
    request['Content-Type'] = 'application/json'
    request['X-API-KEY'] = @api_key
    request['Authorization'] = "Bearer #{@bearer}" if @bearer
    body = {operation: operation, payload: payload}
    body[:options] = options unless options.nil?
    request.body = JSON.generate(body)
    http = Net::HTTP.new(uri.host, uri.port)
    http.use_ssl = uri.scheme == 'https'
    http.open_timeout = 30
    http.read_timeout = 30
    response = http.request(request)
    decoded = JSON.parse(response.body)
    unless decoded.is_a?(Hash)
      raise 'Expected a JSON API envelope'
    end
    if response.code.to_i >= 400 && !decoded['error']
      raise "HTTP #{response.code} without an API error envelope"
    end
    Result.new(data: decoded['data'], error: decoded['error'], meta: decoded['meta'] || {}, exec_time: decoded['exec_time'])
  end
end
