# frozen_string_literal: true

require "test_helper"
require "bull_bitcoin"

class BullBitcoinTest < Minitest::Test
	def setup
		@subject = BullBitcoin.new
	end

	def stub_bull_btc_cad(user_price: 12_345, precision: 2)
		stub_request(:post, "https://www.bullbitcoin.com/api/price").with { |req|
			body = JSON.parse(req.body)
			expected = {
				"jsonrpc" => "2.0",
				"method" => "getUserRate",
				"params" => {
					"element" => {
						"fromCurrency" => "BTC",
						"toCurrency" => "CAD"
					}
				}
			}
			req.headers["Content-Type"] == "application/json" &&
				body["id"] =~ /^\d{13}-\d{3}$/ &&
				body.slice("jsonrpc", "method", "params") == expected
		}.to_return(
			status: 200,
			body: {
				jsonrpc: "2.0",
				result: {
					element: {
						userPrice: user_price,
						precision: precision
					}
				}
			}.to_json
		)
	end

	def test_fetch_btc_cad
		stub_bull_btc_cad
		assert_equal BigDecimal("123.45"), @subject.fetch_btc_cad.sync
	end
	em :test_fetch_btc_cad

	def test_fetch_btc_cad_raises_on_http_error
		stub_request(:post, "https://www.bullbitcoin.com/api/price").to_return(
			status: 500,
			body: "Internal Server Error"
		)

		error = assert_raises(BullBitcoin::Error) { @subject.fetch_btc_cad.sync }
		assert_equal(
			"Bull Bitcoin price request failed with HTTP 500",
			error.message
		)
	end
	em :test_fetch_btc_cad_raises_on_http_error

	def test_fetch_btc_cad_raises_on_missing_element
		stub_request(:post, "https://www.bullbitcoin.com/api/price").to_return(
			status: 200,
			body: { jsonrpc: "2.0", result: {} }.to_json
		)

		error = assert_raises(BullBitcoin::Error) { @subject.fetch_btc_cad.sync }
		assert_equal(
			"Bull Bitcoin price response missing result.element",
			error.message
		)
	end
	em :test_fetch_btc_cad_raises_on_missing_element

	def test_fetch_btc_cad_raises_on_validation_error_with_nested_data
		stub_request(:post, "https://www.bullbitcoin.com/api/price").to_return(
			status: 200,
			body: {
				jsonrpc: "2.0",
				error: {
					code: -32602,
					message: "Validation error",
					data: {
						apiError: {
							code: "ERR_API_400",
							message: "Invalid request parameters"
						}
					}
				}
			}.to_json
		)

		error = assert_raises(BullBitcoin::Error) { @subject.fetch_btc_cad.sync }
		expected = "Bull Bitcoin price response error: " \
		           "Validation error: Invalid request parameters"
		assert_equal(expected, error.message)
	end
	em :test_fetch_btc_cad_raises_on_validation_error_with_nested_data

	def test_fetch_btc_cad_raises_on_validation_error_with_string_data
		stub_request(:post, "https://www.bullbitcoin.com/api/price").to_return(
			status: 200,
			body: {
				jsonrpc: "2.0",
				error: {
					code: -32602,
					message: "Validation error",
					data: "4667aad3bf0ef71f:3017b8b112de44f2"
				}
			}.to_json
		)

		error = assert_raises(BullBitcoin::Error) { @subject.fetch_btc_cad.sync }
		assert_equal(
			"Bull Bitcoin price response error: Validation error",
			error.message
		)
	end
	em :test_fetch_btc_cad_raises_on_validation_error_with_string_data

	def test_fetch_btc_cad_raises_on_standard_json_rpc_error
		stub_request(:post, "https://www.bullbitcoin.com/api/price").to_return(
			status: 200,
			body: {
				jsonrpc: "2.0",
				error: {
					code: -32603,
					message: "No Validation schema found for [notARealMethod]",
					data: "4667aad3bf0ef71f:3017b8b112de44f2:6e6c5c73209cfe6a:0"
				}
			}.to_json
		)

		error = assert_raises(BullBitcoin::Error) { @subject.fetch_btc_cad.sync }
		expected = "Bull Bitcoin price response error: " \
		           "No Validation schema found for [notARealMethod]"
		assert_equal(expected, error.message)
	end
	em :test_fetch_btc_cad_raises_on_standard_json_rpc_error
end
