# frozen_string_literal: true

require "test_helper"
require "customer"

Customer::BLATHER = Minitest::Mock.new
Customer::BRAINTREE = Minitest::Mock.new
Customer::REDIS = Minitest::Mock.new
Customer::DB = Minitest::Mock.new
Customer::IQ_MANAGER = Minitest::Mock.new
CustomerPlan::DB = Minitest::Mock.new
CustomerUsage::REDIS = Minitest::Mock.new
CustomerUsage::DB = Minitest::Mock.new
CustomerFinancials::REDIS = Minitest::Mock.new
CustomerFinancials::ELECTRUM = Minitest::Mock.new
CustomerFinancials::ELECTRUM_BCH = Minitest::Mock.new
CustomerFinancials::BRAINTREE = Minitest::Mock.new

class CustomerTest < Minitest::Test
	def test_bill_plan_activate
		CustomerPlan::DB.expect(:transaction, nil) do |&block|
			block.call
			true
		end
		CustomerPlan::DB.expect(
			:exec,
			nil,
			[
				String,
				Matching.new do |params|
					params[0] == "test" &&
					params[1].is_a?(String) &&
					BigDecimal(-1) == params[2]
				end
			]
		)
		CustomerPlan::DB.expect(
			:exec,
			OpenStruct.new(cmd_tuples: 1),
			[String, ["test", "test_usd", nil]]
		)
		CustomerPlan::DB.expect(
			:exec,
			OpenStruct.new(cmd_tuples: 0),
			[String, ["test"]]
		)
		customer(plan_name: "test_usd").bill_plan.sync
		CustomerPlan::DB.verify
	end
	em :test_bill_plan_activate

	def test_bill_plan_reactivate_child
		CustomerPlan::DB.expect(
			:query,
			[{ "plan_name" => "test_usd" }],
			[String, ["parent"]]
		)
		CustomerPlan::DB.expect(:transaction, nil) do |&block|
			block.call
			true
		end
		CustomerPlan::DB.expect(
			:exec,
			nil,
			[
				String,
				Matching.new do |params|
					params[0] == "test" &&
					params[1].is_a?(String) &&
					BigDecimal(-1) == params[2]
				end
			]
		)
		CustomerPlan::DB.expect(
			:exec,
			OpenStruct.new(cmd_tuples: 1),
			[String, ["test", "test_usd", "parent"]]
		)
		CustomerPlan::DB.expect(
			:exec,
			OpenStruct.new(cmd_tuples: 0),
			[String, ["test"]]
		)
		customer(plan_name: "test_usd", parent_customer_id: "parent").bill_plan.sync
		CustomerPlan::DB.verify
	end
	em :test_bill_plan_reactivate_child

	def test_bill_plan_update
		CustomerPlan::DB.expect(:transaction, nil) do |&block|
			block.call
			true
		end
		CustomerPlan::DB.expect(
			:exec,
			nil,
			[
				String,
				Matching.new do |params|
					params[0] == "test" &&
					params[1].is_a?(String) &&
					BigDecimal(-1) == params[2]
				end
			]
		)
		CustomerPlan::DB.expect(
			:exec,
			OpenStruct.new(cmd_tuples: 0),
			[String, ["test", "test_usd", nil]]
		)
		CustomerPlan::DB.expect(:exec, nil, [String, ["test"]])
		customer(plan_name: "test_usd").bill_plan.sync
		CustomerPlan::DB.verify
	end
	em :test_bill_plan_update

	def test_stanza_to
		Customer::BLATHER.expect(
			:<<,
			nil,
			[Matching.new do |stanza|
				assert_equal "+15555550000@component/a", stanza.from.to_s
				assert_equal "test@example.net/b", stanza.to.to_s
			end]
		)
		m = Blather::Stanza::Message.new
		m.from = "+15555550000@sgx/a"
		m.to = "customer_test@component/b"
		customer.stanza_to(m)
		assert_mock Customer::BLATHER
	end
	em :test_stanza_to

	def test_stanza_from
		Customer::BLATHER.expect(
			:<<,
			nil,
			[Matching.new do |stanza|
				assert_equal "customer_test@component/a", stanza.from.to_s
				assert_equal "+15555550000@sgx/b", stanza.to.to_s
			end]
		)
		m = Blather::Stanza::Message.new
		m.from = "test@example.com/a"
		m.to = "+15555550000@component/b"
		customer.stanza_from(m)
		Customer::BLATHER.verify
	end

	def test_fetch_pep
		result = Blather::Stanza::PubSub::Items.new(:result)
		result.items_node <<
			Blather::Stanza::PubSubItem.new("current", Nokogiri.parse(<<~XML).root)
				<vcard xmlns="urn:ietf:params:xml:ns:vcard-4.0">
					<fn><text>A Human</text></fn>
				</vcard4>
			XML
		Customer::IQ_MANAGER.expect(
			:method,
			->(*) { EMPromise.resolve(result) },
			[:write]
		)
		assert_equal(
			"A Human",
			customer.fetch_pep("urn:xmpp:vcard4", "+15551234567").sync
				.first.payload_node.find_first(
					"./ns:fn/ns:text",
					ns: "urn:ietf:params:xml:ns:vcard-4.0"
				)&.content
		)
	end
	em :test_fetch_pep

	def test_customer_usage_report
		report_for = (Date.today..(Date.today - 1))
		report_for.first.downto(report_for.last).each.with_index do |day, idx|
			CustomerUsage::REDIS.expect(
				:zscore,
				EMPromise.resolve(idx),
				["jmp_customer_outbound_messages-test", day.strftime("%Y%m%d")]
			)
		end
		CustomerUsage::DB.expect(
			:query_defer,
			EMPromise.resolve([{ "day" => report_for.first, "minutes" => 123 }]),
			[String, ["test", report_for.first, report_for.last]]
		)
		assert_equal(
			UsageReport.new(
				report_for, {
					Date.today => 0,
					(Date.today - 1) => 1
				},
				Date.today => 123
			),
			customer.usage_report(report_for).sync
		)
	end
	em :test_customer_usage_report

	def test_sip_account_new
		req = stub_request(
			:get,
			"https://dashboard.bandwidth.com/v1.0/accounts//sipcredentials/ctest"
		).with(
			headers: {
				"Authorization" => "Basic Og=="
			}
		).to_return(
			status: 404,
			body:
				"<r><ResponseStatus><ErrorCode>0</ErrorCode>" \
				"<Description>desc</Description></ResponseStatus></r>"
		)
		sip = customer.sip_account
		assert_kind_of SipAccount::New, sip
		assert_equal "ctest", sip.username
		assert_requested req
	end
	em :test_sip_account_new

	def test_sip_account_existing
		req1 = stub_request(
			:get,
			"https://dashboard.bandwidth.com/v1.0/accounts//sipcredentials/ctest"
		).with(
			headers: {
				"Authorization" => "Basic Og=="
			}
		).to_return(status: 200, body: {
			SipCredential: {
				UserName: "ctest",
				Realm: "sip.example.com"
			}
		}.to_xml)

		sip = customer.sip_account
		assert_kind_of SipAccount, sip
		assert_equal "ctest", sip.username

		assert_requested req1
	end
	em :test_sip_account_existing

	def test_sip_account_error
		stub_request(
			:get,
			"https://dashboard.bandwidth.com/v1.0/accounts//sipcredentials/ctest"
		).to_return(
			status: 404,
			body:
				"<r><ResponseStatus><ErrorCode>0</ErrorCode>" \
				"<Description>desc</Description></ResponseStatus></r>"
		)

		assert_equal "ctest", customer.sip_account.username
	end
	em :test_sip_account_error

	def test_btc_addresses
		CustomerFinancials::REDIS.expect(
			:smembers,
			EMPromise.resolve(["testaddr"]),
			["jmp_customer_btc_addresses-test"]
		)
		assert_equal ["testaddr"], customer.btc_addresses.sync
		assert_mock Customer::REDIS
	end
	em :test_btc_addresses

	def test_add_btc_address
		CustomerFinancials::REDIS.expect(
			:spopsadd,
			EMPromise.resolve("testaddr"),
			[["jmp_available_btc_addresses", "jmp_customer_btc_addresses-test"]]
		)
		CustomerFinancials::ELECTRUM.expect(
			:notify,
			EMPromise.resolve(nil),
			["testaddr", "http://notify.example.com"]
		)
		assert_equal "testaddr", customer.add_btc_address.sync
		assert_mock CustomerFinancials::REDIS
		assert_mock CustomerFinancials::ELECTRUM
	end
	em :test_add_btc_address

	def test_add_bch_address
		CustomerFinancials::REDIS.expect(
			:spopsadd,
			EMPromise.resolve("testaddr"),
			[["jmp_available_bch_addresses", "jmp_customer_bch_addresses-test"]]
		)
		CustomerFinancials::ELECTRUM_BCH.expect(
			:notify,
			EMPromise.resolve(nil),
			["testaddr", "http://notify.example.com"]
		)
		assert_equal "testaddr", customer.add_bch_address.sync
		assert_mock CustomerFinancials::REDIS
		assert_mock CustomerFinancials::ELECTRUM_BCH
	end
	em :test_add_bch_address
end
