# frozen_string_literal: true

require "test_helper"
require "customer_repo"

class CustomerRepo
	attr_reader :sgx_repo
end

CustomerPlan::DB = Minitest::Mock.new

class CustomerRepoTest < Minitest::Test
	FAKE_REDIS = FakeRedis.new(
		# sgx-jmp customer
		"jmp_customer_jid-test" => "test@example.com",
		"jmp_customer_id-test@example.com" => "test",
		"catapult_jid-+13334445555" => "customer_test@component",
		"catapult_cred-customer_test@component" => [
			"test_bw_customer", "", "", "+13334445555"
		],
		# sgx-jmp customer, empty DB
		"jmp_customer_jid-empty" => "empty@example.com",
		"jmp_customer_id-empty@example.com" => "empty",
		"catapult_jid-+16667778888" => "customer_empty@component",
		"catapult_cred-customer_empty@component" => [
			"test_bw_customer", "", "", "+16667778888"
		],
		# v2 customer
		"jmp_customer_jid-test_v2" => "test_v2@example.com",
		"jmp_customer_id-test_v2@example.com" => "test_v2",
		"catapult_jid-+14445556666" => "test_v2@example.com",
		"catapult_cred-test_v2@example.com" => [
			"test_bw_customer", "", "", "+14445556666"
		]
	)

	FAKE_DB = FakeDB.new(
		["test"] => [{
			"balance" => BigDecimal(1234),
			"plan_name" => "test_usd",
			"expires_at" => Time.now + 100
		}],
		["testp"] => [{
			"balance" => BigDecimal(1234),
			"plan_name" => "test_usd",
			"expires_at" => Time.now + 100,
			"parent_customer_id" => "1234"
		}],
		["test_v2"] => [{
			"balance" => BigDecimal(2345),
			"plan_name" => "test_usd",
			"expires_at" => Time.now + 100
		}]
	)

	def mkrepo(
		redis: FAKE_REDIS,
		db: FAKE_DB,
		braintree: Minitest::Mock.new
	)
		sgx_repo = Minitest::Mock.new(TrivialBackendSgxRepo.new)
		CustomerRepo.new(
			redis: redis,
			db: db,
			braintree: braintree,
			sgx_repo: sgx_repo
		)
	end

	def setup
		@repo = mkrepo
	end

	def test_find_by_jid
		customer = @repo.find_by_jid("test@example.com").sync
		assert_kind_of Customer, customer
		assert_equal 1234, customer.balance
		assert_equal "merchant_usd", customer.merchant_account
	end
	em :test_find_by_jid

	def test_find_by_id
		customer = @repo.find("test").sync
		assert_kind_of Customer, customer
		assert_equal 1234, customer.balance
		assert_equal "merchant_usd", customer.merchant_account
	end
	em :test_find_by_id

	def test_find_by_customer_jid
		customer = @repo.find_by_jid("customer_test@component").sync
		assert_kind_of Customer, customer
		assert_equal 1234, customer.balance
		assert_equal "merchant_usd", customer.merchant_account
	end
	em :test_find_by_customer_jid

	def test_find_by_jid_not_found
		assert_raises do
			@repo.find_by_jid("test2@example.com").sync
		end
	end
	em :test_find_by_jid_not_found

	def test_find_sgx_customer_by_phone
		customer = @repo.find_by_tel("+13334445555").sync
		assert_kind_of Customer, customer
		assert_equal "test", customer.customer_id
	end
	em :test_find_sgx_customer_by_phone

	def test_find_v2_customer_by_phone
		customer = @repo.find_by_tel("+14445556666").sync
		assert_kind_of Customer, customer
		assert_equal "test_v2", customer.customer_id
	end
	em :test_find_v2_customer_by_phone

	def test_find_missing_phone
		assert_raises do
			@repo.find_by_tel("+15556667777").sync
		end
	end
	em :test_find_missing_phone

	def test_find_db_empty
		customer = @repo.find("empty").sync
		assert_equal BigDecimal(0), customer.balance
	end
	em :test_find_db_empty

	def test_create
		redis = Minitest::Mock.new
		braintree = Minitest::Mock.new
		repo = mkrepo(redis: redis, braintree: braintree)
		braintree_customer = Minitest::Mock.new
		braintree.expect(:customer, braintree_customer)
		braintree_customer.expect(
			:create,
			EMPromise.resolve(
				OpenStruct.new(success?: true, customer: OpenStruct.new(id: "test"))
			)
		)
		redis.expect(
			:msetnx,
			EMPromise.resolve(1),
			[
				"jmp_customer_id-test@example.com", "test",
				"jmp_customer_jid-test", "test@example.com"
			]
		)
		assert_kind_of Customer, repo.create("test@example.com").sync
		assert_mock braintree
		assert_mock braintree_customer
		assert_mock redis
	end
	em :test_create

	def test_create_parented
		redis = Minitest::Mock.new
		braintree = Minitest::Mock.new
		repo = mkrepo(redis: redis, braintree: braintree)
		braintree_customer = Minitest::Mock.new
		braintree.expect(:customer, braintree_customer)
		braintree_customer.expect(
			:create,
			EMPromise.resolve(
				OpenStruct.new(success?: true, customer: OpenStruct.new(id: "testp"))
			)
		)
		redis.expect(
			:msetnx,
			EMPromise.resolve(1),
			[
				"jmp_customer_id-test@parented.example.com", "testp",
				"jmp_customer_jid-testp", "test@parented.example.com"
			]
		)
		redis.expect(
			:get,
			EMPromise.resolve("test@parented.example.com"),
			["jmp_customer_jid-testp"]
		)
		redis.expect(
			:mget,
			EMPromise.resolve([nil, nil]),
			[
				"jmp_customer_auto_top_up_amount-testp",
				"jmp_customer_monthly_overage_limit-testp"
			]
		)
		redis.expect(
			:smembers,
			EMPromise.resolve([]),
			["jmp_customer_feature_flags-testp"]
		)
		CustomerPlan::DB.expect(
			:query,
			[{ "plan_name" => "test_usd" }],
			[String, ["1234"]]
		)
		CustomerPlan::DB.expect(
			:exec_defer,
			EMPromise.resolve(nil),
			[String, ["testp", "test_usd", "1234"]]
		)
		result = repo.create("test@parented.example.com").sync
		assert_kind_of Customer::ChildCustomer, result
		assert_equal "1234", result.billing_customer_id
		assert_mock braintree
		assert_mock braintree_customer
		assert_mock redis
		assert_mock CustomerPlan::DB
	end
	em :test_create_parented

	def test_put_lidb_name
		post = stub_request(
			:post,
			"https://dashboard.bandwidth.com/v1.0/accounts//lidbs"
		).with(body: {
			LidbTnGroups: {
				LidbTnGroup: {
					TelephoneNumbers: {
						TelephoneNumber: "5556667777"
					},
					SubscriberInformation: "Hank",
					UseType: "RESIDENTIAL",
					Visibility: "PUBLIC"
				}
			}
		}.to_xml(root: "LidbOrder", indent: 0)).to_return(
			status: 201,
			headers: { location: "/boop/123" }
		)

		stub_request(
			:get,
			"https://dashboard.bandwidth.com/v1.0/accounts//lidbs/123"
		)

		@repo.put_lidb_name(
			Customer.new(
				"test",
				"test@exmple.com",
				sgx: OpenStruct.new(registered?: OpenStruct.new(phone: "+15556667777"))
			),
			"Hank"
		)

		assert_requested post
	end
	em :test_put_lidb_name

	def test_put_transcription_enabled
		@repo.sgx_repo.expect(
			:put_transcription_enabled,
			EMPromise.resolve(nil),
			["test", true]
		)
		@repo.put_transcription_enabled(
			Customer.new("test", "test@exmple.com", sgx: nil),
			true
		)
		assert_mock @repo.sgx_repo
	end
	em :test_put_transcription_enabled

	def test_put_fwd
		@repo.sgx_repo.expect(
			:put_fwd,
			EMPromise.resolve(nil),
			["test", "+15556667777", :fwd]
		)
		@repo.put_fwd(
			Customer.new(
				"test",
				"test@exmple.com",
				sgx: OpenStruct.new(registered?: OpenStruct.new(phone: "+15556667777"))
			),
			:fwd
		)
		assert_mock @repo.sgx_repo
	end
	em :test_put_fwd
end
