# 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
LowBalance::DB = 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_on_transaction_amount
		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: 1), 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_on_transaction_amount

	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::CreditCardSale = Minitest::Mock.new

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

		def test_notify!
			tx = OpenStruct.new(total: 13)
			LowBalance::AutoTopUp::CreditCardSale.expect(
				:create,
				EMPromise.resolve(tx),
				[@customer], amount: 100
			)
			@auto_top_up.notify!
		end
		em :test_notify!

		def test_top_up_amount_when_target_greater_than_expected_balance
			customer = Minitest::Mock.new(customer(
				balance: 10,
				auto_top_up_amount: 15
			))
			auto_top_up = LowBalance::AutoTopUp.new(customer, nil, 30, margin: 5)

			assert_equal 25, auto_top_up.top_up_amount
		end
		em :test_top_up_amount_when_target_greater_than_expected_balance

		def test_top_up_amount_when_target_less_than_expected_balance
			customer = Minitest::Mock.new(customer(
				balance: 10,
				auto_top_up_amount: 15
			))
			auto_top_up = LowBalance::AutoTopUp.new(customer, nil, 12, margin: 5)

			assert_equal 15, auto_top_up.top_up_amount
		end
		em :test_top_up_amount_when_target_less_than_expected_balance

		def test_negative_balance_target_less_than_expected_balance
			customer = Minitest::Mock.new(customer(
				balance: -11,
				auto_top_up_amount: 15
			))
			auto_top_up = LowBalance::AutoTopUp.new(customer, nil, 35, margin: 5)

			assert_equal 51, auto_top_up.top_up_amount
		end
		em :test_negative_balance_target_less_than_expected_balance

		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 = OpenStruct.new(total: 13)

			LowBalance::AutoTopUp::CreditCardSale.expect(
				:create,
				EMPromise.resolve(tx),
				[customer], amount: 110
			)
			auto_top_up.notify!
		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 = OpenStruct.new(total: 13)

			LowBalance::AutoTopUp::CreditCardSale.expect(
				:create,
				EMPromise.resolve(tx),
				[customer], amount: 21
			)
			auto_top_up.notify!
		end
		em :test_border_low_balance_notify!

		def test_decline_notify!
			stub_request(:post, "https://api.churnbuster.io/v1/failed_payments")
				.with(
					body: {
						customer: {
							source: "braintree",
							source_id: "test",
							email: "test@smtp.cheogram.com",
							properties: {}
						},
						payment: {
							source: "braintree",
							source_id: "tx",
							amount_in_cents: 10000,
							currency: "USD"
						}
					}.to_json,
					headers: {
						"Authorization" => ["", ""],
						"Content-Type" => "application/json"
					}
				).to_return(status: 200, body: "", headers: {})

			@customer.expect(
				:stanza_to,
				nil,
				[Matching.new { |m|
					assert_equal(
						"Automatic top-up transaction for $100.00 failed: test",
						m.body
					)
				}]
			)
			LowBalance::AutoTopUp::CreditCardSale.expect(
				:create,
				EMPromise.reject(BraintreeFailure.new(OpenStruct.new(
					message: "test",
					transaction: OpenStruct.new(id: "tx")
				))),
				[@customer], amount: 100.to_d
			)
			@auto_top_up.notify!.sync
			assert_mock @customer
		end
		em :test_decline_notify!
	end
end
