# frozen_string_literal: true

require "em-http"
require "em_promise"
require "em-synchrony/em-http" # For aget vs get
require "json"

require_relative "em"

class BullBitcoin
	Error = Class.new(StandardError)

	def fetch_btc_cad
		EM::HttpRequest.new(
			"https://www.bullbitcoin.com/api/price",
			tls: { verify_peer: true }
		).apost(
			head: {
				"Accept" => "application/json",
				"Content-Type" => "application/json"
			},
			body: {
				id: request_id,
				jsonrpc: "2.0",
				method: "getUserRate",
				params: {
					element: {
						fromCurrency: "BTC",
						toCurrency: "CAD"
					}
				}
			}.to_json
		).then(&method(:parse_response))
	end

private

	def request_id
		"#{(Time.now.to_f * 1000).to_i}-#{rand(100...999)}"
	end

	def parse_response(http)
		status = http.response_header.status
		if status != 200
			raise Error, "Bull Bitcoin price request failed with HTTP #{status}"
		end

		el = extract_element(http.response)
		BigDecimal(el.fetch("userPrice").to_s) / (10**el.fetch("precision"))
	rescue JSON::ParserError, KeyError => e
		raise Error, "Bull Bitcoin price response invalid: #{e.message}"
	end

	def extract_element(response)
		json = JSON.parse(response)
		if json["error"]
			raise Error,
			      "Bull Bitcoin price response error: #{format_error(json['error'])}"
		end

		el = json.dig("result", "element")
		return el if el

		raise Error, "Bull Bitcoin price response missing result.element"
	end

	def format_error(error)
		if error["message"] == "Validation error" && error["data"].is_a?(Hash)
			nested = error.dig("data", "apiError", "message")
			return "#{error['message']}: #{nested}" if nested
		end

		error["message"]
	end
end
