# frozen_string_literal: true

require "test_helper"
require "low_balance"

ExpiringLock::REDIS = Minitest::Mock.new
CustomerPlan::REDIS = Minitest::Mock.new
CustomerFinancials::REDIS = Minitest::Mock.new
LowBalance::AutoTopUp::REDIS = Minitest::Mock.new

class LowBalanceTest < Minitest::Test
	def test_for_locked
		ExpiringLock::REDIS.expect(
			:set,
			EMPromise.resolve(nil),
			["jmp_customer_low_balance-test", Time, "EX", 604800, "NX"]
		)
		assert_kind_of LowBalance::Locked, LowBalance.for(customer).sync
	end
	em :test_for_locked

	def test_for_no_auto_top_up
		ExpiringLock::REDIS.expect(
			:set,
			EMPromise.resolve("OK"),
			["jmp_customer_low_balance-test", Time, "EX", 604800, "NX"]
		)
		CustomerFinancials::REDIS.expect(
			:smembers,
			EMPromise.resolve([]),
			["jmp_customer_btc_addresses-test"]
		)
		assert_kind_of(
			LowBalance,
			LowBalance.for(customer).sync
		)
		assert_mock ExpiringLock::REDIS
	end
	em :test_for_no_auto_top_up

	def test_for_auto_top_up
		ExpiringLock::REDIS.expect(
			:set,
			EMPromise.resolve("OK"),
			["jmp_customer_low_balance-test", Time, "EX", 604800, "NX"]
		)
		CustomerFinancials::REDIS.expect(
			:smembers,
			EMPromise.resolve([]),
			["block_credit_cards"]
		)
		LowBalance::AutoTopUp::REDIS.expect(
			:exists,
			0,
			["jmp_auto_top_up_block-abcd"]
		)
		braintree_customer = Minitest::Mock.new
		CustomerFinancials::BRAINTREE.expect(:customer, braintree_customer)
		payment_methods = OpenStruct.new(payment_methods: [
			OpenStruct.new(default?: true, unique_number_identifier: "abcd")
		])
		braintree_customer.expect(
			:find,
			EMPromise.resolve(payment_methods),
			["test"]
		)
		assert_kind_of(
			LowBalance::AutoTopUp,
			LowBalance.for(customer(auto_top_up_amount: 15)).sync
		)
		assert_mock ExpiringLock::REDIS
		assert_mock CustomerFinancials::REDIS
		assert_mock CustomerFinancials::BRAINTREE
		assert_mock braintree_customer
	end
	em :test_for_auto_top_up

	def test_for_auto_top_up_blocked
		ExpiringLock::REDIS.expect(
			:set,
			EMPromise.resolve("OK"),
			["jmp_customer_low_balance-test", Time, "EX", 604800, "NX"]
		)
		CustomerFinancials::REDIS.expect(
			:smembers,
			EMPromise.resolve([]),
			["block_credit_cards"]
		)
		CustomerFinancials::REDIS.expect(
			:smembers,
			EMPromise.resolve([]),
			["jmp_customer_btc_addresses-test"]
		)
		LowBalance::AutoTopUp::REDIS.expect(
			:exists,
			1,
			["jmp_auto_top_up_block-blocked"]
		)
		braintree_customer = Minitest::Mock.new
		CustomerFinancials::BRAINTREE.expect(:customer, braintree_customer)
		payment_methods = OpenStruct.new(payment_methods: [
			OpenStruct.new(default?: true, unique_number_identifier: "blocked")
		])
		braintree_customer.expect(
			:find,
			EMPromise.resolve(payment_methods),
			["test"]
		)
		assert_kind_of(
			LowBalance,
			LowBalance.for(customer(auto_top_up_amount: 15)).sync
		)
		assert_mock ExpiringLock::REDIS
		assert_mock CustomerFinancials::REDIS
		assert_mock CustomerFinancials::BRAINTREE
		assert_mock braintree_customer
	end
	em :test_for_auto_top_up_blocked

	class AutoTopUpTest < Minitest::Test
		LowBalance::AutoTopUp::Transaction = Minitest::Mock.new

		def setup
			@customer = Minitest::Mock.new(customer(auto_top_up_amount: 100))
			@auto_top_up = LowBalance::AutoTopUp.new(@customer)
		end

		def test_notify!
			tx = PromiseMock.new
			tx.expect(:insert, EMPromise.resolve(nil))
			LowBalance::AutoTopUp::Transaction.expect(
				:sale,
				tx,
				[@customer], amount: 100
			)
			@auto_top_up.notify!
			assert_mock tx
		end
		em :test_notify!

		def test_very_low_balance_notify!
			customer = Minitest::Mock.new(customer(
				balance: -100,
				auto_top_up_amount: 15
			))
			auto_top_up = LowBalance::AutoTopUp.new(customer)

			tx = PromiseMock.new
			tx.expect(:insert, EMPromise.resolve(nil))
			LowBalance::AutoTopUp::Transaction.expect(
				:sale,
				tx,
				[customer], amount: 115
			)
			auto_top_up.notify!
			assert_mock tx
		end
		em :test_very_low_balance_notify!

		def test_border_low_balance_notify!
			customer = Minitest::Mock.new(customer(
				balance: -11,
				auto_top_up_amount: 15
			))
			auto_top_up = LowBalance::AutoTopUp.new(customer)

			tx = PromiseMock.new
			tx.expect(:insert, EMPromise.resolve(nil))
			LowBalance::AutoTopUp::Transaction.expect(
				:sale,
				tx,
				[customer], amount: 26
			)
			auto_top_up.notify!
			assert_mock tx
		end
		em :test_border_low_balance_notify!

		def test_decline_notify!
			@customer.expect(
				:stanza_to,
				nil,
				[Matching.new { |m|
					assert_equal(
						"Automatic top-up transaction for $100 failed: test",
						m.body
					)
				}]
			)
			LowBalance::AutoTopUp::Transaction.expect(
				:sale,
				EMPromise.reject(RuntimeError.new("test")),
				[@customer], amount: 100
			)
			@auto_top_up.notify!.sync
			assert_mock @customer
		end
		em :test_decline_notify!
	end
end
