# frozen_string_literal: true

require "test_helper"
require "admin_command"
require "admin_action"

BackendSgx::IQ_MANAGER = Minitest::Mock.new
BackendSgx::REDIS = Minitest::Mock.new
AdminAction::LaunchSnikket::IQ_MANAGER = Minitest::Mock.new
Customer::BLATHER = Minitest::Mock.new
AdminActionRepo::REDIS = Minitest::Mock.new
TrivialBackendSgxRepo::REDIS = Minitest::Mock.new
BandwidthTnRepo::DB = Minitest::Mock.new
AdminAction::NumberChange::REDIS = Minitest::Mock.new

class AdminCommandTest < Minitest::Test
	def assert_undoable_form(iq, note_type=nil, note_text=nil)
		assert_equal :form, iq.form.type
		[:execute, :next, :complete].each do |action|
			assert iq.allowed_actions.include?(action)
		end
		assert_equal iq.note_type, note_type if note_type

		assert iq.note_text = note_text if note_text
	end

	def assert_change_number_form(iq)
		assert_equal(
			iq.form.field("new_tel")&.type,
			"text-single"
		)
		assert_equal(
			iq.form.field("new_backend")&.type,
			"text-single"
		)
		assert_equal(
			iq.form.field("should_delete")&.type,
			"boolean"
		)
	end

	def change_number_form(
		new_tel: nil,
		new_backend: nil,
		should_delete: false,
		from: "test@example.com"
	)
		iq = Blather::Stanza::Iq::Command.new
		iq.form.fields = [
			{ var: "new_tel", value: new_tel },
			{ var: "should_delete", value: should_delete.to_s },
			{ var: "new_backend", value: new_backend }
		]
		iq.from = from
		iq
	end

	def setup_bandwidth_tn_repo_mock(
		should_disconnect: false,
		old_tel: nil,
		new_tel: nil
	)
		mock_repo = Minitest::Mock.new
		if should_disconnect
			expected_order_name = "cust test swap to #{new_tel}"
			mock_repo.expect(
				:disconnect,
				EMPromise.resolve(nil),
				[old_tel, expected_order_name]
			)
		end
		mock_repo
	end

	def assert_ibr_register_form(
		iq,
		tel,
		nick: "test_bw_account",
		username: "test_bw_user",
		password: "test_bw_password",
		from: "customer_test@component",
		target_backend_sgx: "sgx"
	)
		assert iq.is_a?(Blather::Stanza::Iq::IBR)
		assert_equal iq.type, :set
		assert_equal iq.nick, nick
		assert_equal iq.username, username
		assert_equal iq.password, password
		assert_equal iq.phone, tel
		assert_equal iq.from, from
		assert_equal iq.to, target_backend_sgx
	end

	def assert_ibr_deregister_form(
		iq,
		from: "customer_test@component",
		target_backend_sgx: "sgx"
	)
		assert iq.is_a?(Blather::Stanza::Iq::IBR)
		assert_equal iq.type, :set
		assert_equal iq.to, target_backend_sgx
		assert_equal iq.from, from
		assert iq.remove?
	end

	def admin_command(tel="+15556667777", registered: OpenStruct.new(phone: tel))
		sgx = Minitest::Mock.new(OpenStruct.new(
			registered?: registered
		))
		[
			sgx,
			AdminCommand.new(
				customer(sgx: sgx),
				CustomerRepo.new(db: FakeDB.new),
				AdminActionRepo.new,
				Snikket::Repo.new(db: FakeDB.new)
			)
		]
	end

	def test_no_user
		q_form = Blather::Stanza::Iq::Command.new
		q_form.action = :complete
		q_form.form.fields = [
			{ var: "q", value: "testuser" }
		]

		customer_repo = Minitest::Mock.new

		result = execute_command {
			customer_repo.expect(
				:find_by_format,
				EMPromise.resolve(OpenStruct.new(
					customer_id: "testuser",
					billing_customer_id: "testuser",
					balance: 0.to_d,
					jid: Blather::JID.new("test@example.com"),
					tndetails: {}
				)),
				["testuser"]
			)

			customer_repo.expect(
				:find_by_format,
				EMPromise.resolve(nil),
				[Blather::JID]
			)

			customer_repo.expect(
				:find_by_format,
				EMPromise.resolve(nil),
				[ProxiedJID]
			)

			TrivialBackendSgxRepo::REDIS.expect(
				:get,
				EMPromise.resolve(nil),
				["jmp_customer_backend_sgx-testuser"]
			)

			TrustLevelRepo::REDIS.expect(
				:get,
				EMPromise.resolve("Customer"),
				["jmp_customer_trust_level-testuser"]
			)

			TrustLevelRepo::DB.expect(
				:query_one,
				EMPromise.resolve({ settled_amount: 0 }),
				[String, "testuser"], default: {}
			)

			Subaccount::DB.expect(
				:query_defer,
				EMPromise.resolve([]),
				[String, ["testuser"]]
			)

			Command::COMMAND_MANAGER.expect(
				:write,
				EMPromise.resolve(q_form),
				[Matching.new do |iq|
					assert_equal :form, iq.form.type
					assert iq.form.field("q")
				end]
			)

			AdminCommand.for(nil, customer_repo).start.catch { |e| e }
		}

		assert result.stanza.completed?
		assert_mock customer_repo
		assert_mock Command::COMMAND_MANAGER
		assert_mock TrustLevelRepo::REDIS
		assert_mock Subaccount::DB
		assert_mock TrivialBackendSgxRepo::REDIS
	end
	em :test_no_user

	def test_action_launch_snikket
		sgx, admin = admin_command
		domain_form = Blather::Stanza::Iq::Command.new
		domain_form.form.fields = [
			{ var: "domain", value: "test.snikket.chat" }
		]

		launched = Snikket::Launched.new
		launched << Niceogiri::XML::Node.new(
			:launched, launched.document, "xmpp:snikket.org/hosting/v1"
		).tap { |inner|
			inner << Niceogiri::XML::Node.new(
				:'instance-id', launched.document, "xmpp:snikket.org/hosting/v1"
			).tap { |id|
				id.content = "si-1234"
			}
			inner << Niceogiri::XML::Node.new(
				:bootstrap, launched.document, "xmpp:snikket.org/hosting/v1"
			).tap { |bootstrap|
				bootstrap << Niceogiri::XML::Node.new(
					:token, launched.document, "xmpp:snikket.org/hosting/v1"
				).tap { |token|
					token.content = "TOKEN"
				}
			}
		}

		result = execute_command {
			Command::COMMAND_MANAGER.expect(
				:write,
				EMPromise.resolve(domain_form),
				[Matching.new do |iq|
					assert_equal :form, iq.form.type
					assert iq.form.field("domain")
				end]
			)
			Command::COMMAND_MANAGER.expect(
				:write,
				EMPromise.reject(:test_result),
				[Matching.new do |iq|
					assert :result, iq.type
					assert(
						"https://test.snikket.chat/invites_bootstrap?token=TOKEN",
						iq.form.field("bootstrap-uri").value
					)
				end]
			)

			AdminAction::LaunchSnikket::IQ_MANAGER.expect(
				:write,
				EMPromise.resolve(launched),
				[Matching.new do |iq|
					assert_equal :set, iq.type
					assert_equal CONFIG[:snikket_hosting_api], iq.to.to_s
					assert_equal(
						"test.snikket.chat",
						iq.xpath(
							"./ns:launch/ns:domain",
							ns: "xmpp:snikket.org/hosting/v1"
						).text
					)
				end]
			)

			admin.action_launch_snikket.catch { |e| e }
		}

		assert_equal :test_result, result
		assert_mock sgx
		assert_mock AdminAction::LaunchSnikket::IQ_MANAGER
		assert_mock Customer::BLATHER
	end
	em :test_action_launch_snikket

	def test_action_cancel_account
		req = stub_request(:post, "https://api.churnbuster.io/v1/cancellations")
			.with(
				body: {
					customer: {
						source: "braintree",
						source_id: "test",
						email: "test@smtp.cheogram.com",
						properties: {}
					},
					subscription: {
						source: "braintree",
						source_id: "test"
					}
				}.to_json
			)
			.to_return(status: 200, body: "", headers: {})

		sgx, admin = admin_command

		Customer::BLATHER.expect(
			:<<,
			EMPromise.resolve(nil),
			[
				Matching.new do |m|
					assert_equal "Your JMP account has been cancelled.", m.body
					assert_equal "test@example.net", m.to.to_s
					assert_equal "notify_from@component", m.from.to_s
				end
			]
		)

		Customer::BLATHER.expect(
			:<<,
			EMPromise.resolve(nil),
			[
				Matching.new do |iq|
					assert iq.remove?
					assert_equal "test@example.net", iq.to.to_s
					assert_equal "component", iq.from.to_s
				end
			]
		)

		sgx.expect(:deregister!, EMPromise.resolve(nil))

		stub_request(
			:post,
			"https://dashboard.bandwidth.com/v1.0/accounts//disconnects"
		).with(
			body: {
				name: "test",
				DisconnectTelephoneNumberOrderType: {
					TelephoneNumberList: {
						TelephoneNumber: "5556667777"
					}
				}
			}.to_xml(indent: 0, root: "DisconnectTelephoneNumberOrder")
		).to_return(status: 200, body: "")

		admin.action_cancel_account.sync

		assert_mock sgx
		assert_mock BackendSgx::IQ_MANAGER
		assert_mock Customer::BLATHER
		assert_requested req
	end
	em :test_action_cancel_account

	def test_action_cancel_account_keep_number
		rate_center_response = {
			"TelephoneNumberDetails": {
				State: "NY",
				RateCenter: "NWYRCYZN01"
			}
		}.to_xml(indent: 0, root: "TelephoneNumberResponse")

		bandwidth_req = stub_request(
			:get,
			"https://dashboard.bandwidth.com/v1.0/tns/5566667777/ratecenter"
		)
			.with(
				headers: {
					"Accept" => "application/xml",
					"Accept-Encoding" => "gzip, compressed",
					"Authorization" => "Basic dGVzdF9id191c2VyOnRlc3RfYndfcGFzc3dvcmQ=",
					"User-Agent" => "Ruby-Bandwidth-Iris"
				},
				body: ""
			)
			.to_return(status: 200, body: rate_center_response, headers: {})

		churnbuster_req = stub_request(
			:post,
			"https://api.churnbuster.io/v1/cancellations"
		)
			.with(
				body: {
					customer: {
						source: "braintree",
						source_id: "test",
						email: "test@smtp.cheogram.com",
						properties: {}
					},
					subscription: {
						source: "braintree",
						source_id: "test"
					}
				}.to_json
			)
			.to_return(status: 200, body: "", headers: {})

		sgx, admin = admin_command("+15566667777")

		sql_params = ["+15566667777", "NY", "NWYRCYZN01", "test_bw_account"]

		BandwidthTnRepo::DB.expect(
			:exec,
			EMPromise.resolve(nil),
			[String, sql_params]
		)

		Customer::BLATHER.expect(
			:<<,
			EMPromise.resolve(nil),
			[
				Matching.new do |m|
					assert_equal "Your JMP account has been cancelled.", m.body
					assert_equal "test@example.net", m.to.to_s
					assert_equal "notify_from@component", m.from.to_s
				end
			]
		)

		Customer::BLATHER.expect(
			:<<,
			EMPromise.resolve(nil),
			[
				Matching.new do |iq|
					assert iq.remove?
					assert_equal "test@example.net", iq.to.to_s
					assert_equal "component", iq.from.to_s
				end
			]
		)

		sgx.expect(:deregister!, EMPromise.resolve(nil))

		admin.action_cancel_account.sync

		assert_mock sgx
		assert_mock BackendSgx::IQ_MANAGER
		assert_mock Customer::BLATHER
		assert_mock BandwidthTnRepo::DB

		assert_requested churnbuster_req
		assert_requested bandwidth_req
	end
	em :test_action_cancel_account_keep_number

	def	test_change_num_for_unregistered_customer
		execute_command { |exe|
			sgx = Minitest::Mock.new(OpenStruct.new(
				registered?: nil,
				jid: Blather::JID.new(CONFIG[:sgx]) # Uses Bandwidth backend
			))
			target_customer = customer(sgx: sgx)
			admin = AdminCommand.for(target_customer, exe.customer_repo)

			error = assert_raises(
				"number change for unregistered customer should raise"
			) {
				admin.action_number_change.sync
			}

			assert_equal error.to_s, "Customer not registered"
		}
	end
	em :test_change_num_for_unregistered_customer

	def test_change_num_same_num_same_backend
		# Same as default given by `sgx.registered?` returned by `admin_command`
		tel = "+15556667777"

		execute_command { |exe|
			sgx = Minitest::Mock.new(OpenStruct.new(
				registered?: OpenStruct.new(phone: tel),
				jid: Blather::JID.new(CONFIG[:sgx]) # Uses Bandwidth backend
			))

			target_customer = customer(sgx: sgx)
			admin = AdminCommand.for(target_customer, exe.customer_repo)

			exe.customer_repo.expect(
				:find_by_jid,
				EMPromise.resolve(target_customer),
				[Matching.new do |jid|
					assert jid.is_a? Blather::JID
					assert_equal jid.stripped.to_s, "test@example.com"
				end]
			)

			AdminAction::NumberChange::REDIS.expect(
				:exists,
				EMPromise.resolve(1),
				["catapult_jid-#{tel}"]
			)

			Command::COMMAND_MANAGER.expect(
				:write,
				EMPromise.resolve(change_number_form(
					new_tel: tel,
					new_backend: "sgx"
				)),
				[Matching.new do |iq|
					assert_undoable_form(iq)
					assert_change_number_form(iq)
				end]
			)

			error = admin.action_number_change
				.catch_only(AdminAction::NoOp) { |e| e }.sync

			assert error.is_a?(AdminAction::NoOp)
			assert_mock Command::COMMAND_MANAGER
			assert_mock exe.customer_repo
			assert_mock AdminAction::NumberChange::REDIS
			assert_mock sgx
		}
	end
	em :test_change_num_same_num_same_backend

	def test_change_num_blank_num_blank_backend
		# Same as default given by `sgx.registered?` returned by `admin_command`
		tel = "+15556667777"

		execute_command { |exe|
			sgx = Minitest::Mock.new(OpenStruct.new(
				registered?: OpenStruct.new(phone: tel),
				jid: Blather::JID.new(CONFIG[:sgx]) # Uses Bandwidth backend
			))

			target_customer = customer(sgx: sgx)

			admin = AdminCommand.for(target_customer, exe.customer_repo)

			exe.customer_repo.expect(
				:find_by_jid,
				EMPromise.resolve(target_customer),
				[Matching.new do |jid|
					assert jid.is_a? Blather::JID
					assert_equal jid.stripped.to_s, "test@example.com"
				end]
			)

			AdminAction::NumberChange::REDIS.expect(
				:exists,
				EMPromise.resolve(1),
				["catapult_jid-#{tel}"]
			)

			Command::COMMAND_MANAGER.expect(
				:write,
				EMPromise.resolve(change_number_form),
				[Matching.new do |iq|
					assert_undoable_form(iq)
					assert_change_number_form(iq)
				end]
			)

			error = admin.action_number_change
				.catch_only(AdminAction::NoOp) { |e| e }.sync

			assert error.is_a?(AdminAction::NoOp)
			assert_mock Command::COMMAND_MANAGER
			assert_mock exe.customer_repo
			assert_mock AdminAction::NumberChange::REDIS
			assert_mock sgx
		}
	end
	em :test_change_num_blank_num_blank_backend

	def test_change_num_different_num_same_backend
		old_tel = "+15556667777"
		new_tel = "+12222222222"
		bandwidth_tn_repo_mock = setup_bandwidth_tn_repo_mock(
			should_disconnect: false
		)

		BandwidthTnRepo.stub :new, bandwidth_tn_repo_mock do
			execute_command { |exe|
				sgx = Minitest::Mock.new(OpenStruct.new(
					registered?: OpenStruct.new(phone: old_tel),
					jid: Blather::JID.new(CONFIG[:sgx])
				))

				target_customer = customer(sgx: sgx)
				admin = AdminCommand.for(target_customer, exe.customer_repo)

				exe.customer_repo.expect(
					:find_by_jid,
					EMPromise.resolve(target_customer),
					[Matching.new do |jid|
						assert jid.is_a? Blather::JID
						assert_equal jid.stripped.to_s, "test@example.com"
					end]
				)

				AdminAction::NumberChange::REDIS.expect(
					:rename,
					EMPromise.resolve(nil),
					["catapult_fwd-#{old_tel}", "catapult_fwd-#{new_tel}"]
				)

				AdminAction::NumberChange::REDIS.expect(
					:get,
					EMPromise.resolve(nil),
					["jmp_customer_backend_sgx-test"]
				)
				AdminAction::NumberChange::REDIS.expect(
					:get,
					EMPromise.resolve(nil),
					["jmp_customer_backend_sgx-test"]
				)

				BackendSgx::IQ_MANAGER.expect(
					:write,
					EMPromise.resolve(nil)
				) do |iq|
					assert_ibr_deregister_form(iq)
				end

				BackendSgx::IQ_MANAGER.expect(
					:write,
					EMPromise.resolve(nil)
				) do |iq|
					assert_ibr_register_form(
						iq,
						"+12222222222"
					)
				end

				AdminAction::NumberChange::REDIS.expect(
					:del,
					EMPromise.resolve(nil),
					["jmp_customer_backend_sgx-#{target_customer.customer_id}"]
				)

				BackendSgx::REDIS.expect(
					:set,
					EMPromise.resolve(nil),
					["catapult_jid-#{new_tel}", "customer_test@component"]
				)

				AdminAction::NumberChange::REDIS.expect(
					:exists,
					EMPromise.resolve(1),
					["catapult_jid-#{old_tel}"]
				)

				Command::COMMAND_MANAGER.expect(
					:write,
					EMPromise.resolve(change_number_form(
						new_tel: "+12222222222"
					))
				) do |iq|
					assert_undoable_form(iq)
					assert_change_number_form(iq)
				end

				expected_xadd_args = {
					customer_id: "test",
					old_tel: old_tel,
					new_tel: new_tel,
					should_delete: nil,
					actor_id: "test",
					class: "NumberChange",
					direction: :forward
				}.freeze

				AdminActionRepo::REDIS.expect(
					:xadd,
					EMPromise.resolve(nil)
				) do |admin_actions, star, **xadd_args|
					assert_equal admin_actions, "admin_actions"
					assert_equal star, "*"

					xadd_args.each do |k, v|
						assert_equal v, expected_xadd_args[k]
					end
				end

				result = admin.action_number_change.sync

				slug, backend_report, number_report = result.lines

				assert_equal slug.strip, "Action : Change Number"
				assert_equal(
					backend_report.strip,
					"[move backend?]: sgx -> sgx"
				)
				assert_equal(
					number_report.strip,
					"[change number?]: +15556667777 -> +12222222222"
				)

				assert_mock Command::COMMAND_MANAGER
				assert_mock exe.customer_repo
				assert_mock AdminAction::NumberChange::REDIS
				assert_mock AdminActionRepo::REDIS
				assert_mock BackendSgx::REDIS
				assert_mock BackendSgx::IQ_MANAGER
				assert_mock bandwidth_tn_repo_mock
				assert_mock sgx
			}
		end
	end
	em :test_change_num_different_num_same_backend

	def test_change_num_same_num_different_backend
		target_backend_sgx = "route_value"

		bandwidth_tn_repo_mock = setup_bandwidth_tn_repo_mock(
			should_disconnect: false
		)

		BandwidthTnRepo.stub :new, bandwidth_tn_repo_mock do
			execute_command { |exe|
				sgx = Minitest::Mock.new(OpenStruct.new(
					registered?: OpenStruct.new(phone: "+15556667777"),
					jid: Blather::JID.new(CONFIG[:sgx])
				))

				target_customer = customer(sgx: sgx)
				admin = AdminCommand.for(target_customer, exe.customer_repo)

				exe.customer_repo.expect(
					:find_by_jid,
					EMPromise.resolve(target_customer),
					[Matching.new do |jid|
						assert jid.is_a? Blather::JID
						assert_equal jid.stripped.to_s, "test@example.com"
					end]
				)

				Command::COMMAND_MANAGER.expect(
					:write,
					EMPromise.resolve(
						change_number_form(
							new_backend: target_backend_sgx
						)
					),
					[Matching.new do |iq|
						assert_undoable_form(iq)
						assert_change_number_form(iq)
					end]
				)

				BackendSgx::IQ_MANAGER.expect(
					:write,
					EMPromise.resolve(nil)
				) do |iq|
					assert_ibr_deregister_form(iq)
				end

				BackendSgx::IQ_MANAGER.expect(
					:write,
					EMPromise.resolve(nil)
				) do |iq|
					assert_ibr_register_form(
						iq,
						"+15556667777",
						target_backend_sgx: target_backend_sgx,
						nick: "test_sgx_account",
						username: "test_sgx_user",
						password: "test_sgx_password"
					)
				end

				AdminAction::NumberChange::REDIS.expect(
					:get,
					EMPromise.resolve(nil),
					["jmp_customer_backend_sgx-#{target_customer.customer_id}"]
				)

				AdminAction::NumberChange::REDIS.expect(
					:set,
					EMPromise.resolve(nil),
					[
						"jmp_customer_backend_sgx-#{target_customer.customer_id}",
						"route_value"
					]
				)

				AdminAction::NumberChange::REDIS.expect(
					:get,
					EMPromise.resolve("route_value"),
					["jmp_customer_backend_sgx-#{target_customer.customer_id}"]
				)

				AdminAction::NumberChange::REDIS.expect(
					:exists,
					EMPromise.resolve(1),
					["catapult_jid-+15556667777"]
				)

				BackendSgx::REDIS.expect(
					:set,
					EMPromise.resolve(nil),
					[
						"catapult_jid-+15556667777",
						"customer_test@component"
					]
				)

				expected_xadd_args = {
					old_tel: "+15556667777",
					new_tel: "+15556667777",
					old_backend: "sgx",
					new_backend: "route_value",
					should_delete: nil,
					actor_id: "test",
					class: "NumberChange",
					direction: :forward
				}.freeze

				AdminActionRepo::REDIS.expect(
					:xadd,
					EMPromise.resolve(nil)
				) do |admin_actions, star, **xadd_args|
					assert_equal admin_actions, "admin_actions"
					assert_equal star, "*"

					xadd_args.each do |k, v|
						assert_equal v, expected_xadd_args[k]
					end
				end

				result = admin.action_number_change.sync

				slug, backend_report, number_report = result.lines

				assert_equal slug.strip, "Action : Change Number"
				assert_equal(
					backend_report.strip,
					"[move backend?]: sgx -> route_value"
				)
				assert_equal(
					number_report.strip,
					"[change number?]: +15556667777 -> +15556667777"
				)

				assert_mock Command::COMMAND_MANAGER
				assert_mock exe.customer_repo
				assert_mock AdminAction::NumberChange::REDIS
				assert_mock AdminActionRepo::REDIS
				assert_mock BackendSgx::REDIS
				assert_mock BackendSgx::IQ_MANAGER
				assert_mock bandwidth_tn_repo_mock
				assert_mock sgx
			}
		end
	end
	em :test_change_num_same_num_different_backend

	def test_change_num_different_num_different_backend
		old_tel = "+15556667777"
		new_tel = "+12222222222"
		target_backend_sgx = "route_value"

		bandwidth_tn_repo_mock = setup_bandwidth_tn_repo_mock(
			should_disconnect: false
		)

		BandwidthTnRepo.stub :new, bandwidth_tn_repo_mock do
			execute_command { |exe|
				sgx = Minitest::Mock.new(OpenStruct.new(
					registered?: OpenStruct.new(phone: old_tel),
					jid: Blather::JID.new(CONFIG[:sgx])
				))

				target_customer = customer(sgx: sgx)
				admin = AdminCommand.for(target_customer, exe.customer_repo)

				exe.customer_repo.expect(
					:find_by_jid,
					EMPromise.resolve(target_customer),
					[Matching.new do |jid|
						assert jid.is_a? Blather::JID
						assert_equal jid.stripped.to_s, "test@example.com"
					end]
				)

				Command::COMMAND_MANAGER.expect(
					:write,
					EMPromise.resolve(
						change_number_form(
							new_tel: new_tel,
							new_backend: target_backend_sgx
						)
					),
					[Matching.new do |iq|
						assert_undoable_form(iq)
						assert_change_number_form(iq)
					end]
				)

				BackendSgx::IQ_MANAGER.expect(
					:write,
					EMPromise.resolve(nil)
				) do |iq|
					assert_ibr_deregister_form(iq)
				end

				BackendSgx::IQ_MANAGER.expect(
					:write,
					EMPromise.resolve(nil)
				) do |iq|
					assert_ibr_register_form(
						iq,
						"+12222222222",
						target_backend_sgx: target_backend_sgx,
						nick: "test_sgx_account",
						username: "test_sgx_user",
						password: "test_sgx_password"
					)
				end

				AdminAction::NumberChange::REDIS.expect(
					:exists,
					EMPromise.resolve(1),
					["catapult_jid-#{old_tel}"]
				)

				AdminAction::NumberChange::REDIS.expect(
					:rename,
					EMPromise.resolve(nil),
					["catapult_fwd-#{old_tel}", "catapult_fwd-#{new_tel}"]
				)

				AdminAction::NumberChange::REDIS.expect(
					:get,
					EMPromise.resolve(nil),
					["jmp_customer_backend_sgx-test"]
				)

				AdminAction::NumberChange::REDIS.expect(
					:set,
					EMPromise.resolve(nil),
					[
						"jmp_customer_backend_sgx-#{target_customer.customer_id}",
						"route_value"
					]
				)

				AdminAction::NumberChange::REDIS.expect(
					:get,
					EMPromise.resolve("route_value"),
					["jmp_customer_backend_sgx-test"]
				)

				BackendSgx::REDIS.expect(
					:set,
					EMPromise.resolve(nil),
					[
						"catapult_jid-#{new_tel}",
						"customer_test@component"
					]
				)

				expected_xadd_args = {
					old_tel: "+15556667777",
					new_tel: "+12222222222",
					target_backend_sgx: "route_value",
					should_delete: nil,
					actor_id: "test",
					class: "NumberChange",
					direction: :forward
				}.freeze

				AdminActionRepo::REDIS.expect(
					:xadd,
					EMPromise.resolve(nil)
				) do |admin_actions, star, **xadd_args|
					assert_equal admin_actions, "admin_actions"
					assert_equal star, "*"

					xadd_args.each do |k, v|
						assert_equal v, expected_xadd_args[k]
					end
				end

				result = admin.action_number_change.sync

				slug, backend_report, number_report = result.lines

				assert_equal slug.strip, "Action : Change Number"
				assert_equal(
					backend_report.strip,
					"[move backend?]: sgx -> route_value"
				)
				assert_equal(
					number_report.strip,
					"[change number?]: +15556667777 -> +12222222222"
				)

				assert_mock Command::COMMAND_MANAGER
				assert_mock exe.customer_repo
				assert_mock AdminAction::NumberChange::REDIS
				assert_mock AdminActionRepo::REDIS
				assert_mock BackendSgx::REDIS
				assert_mock BackendSgx::IQ_MANAGER
				assert_mock bandwidth_tn_repo_mock
				assert_mock sgx
			}
		end
	end
	em :test_change_num_different_num_different_backend

	def test_change_num_bandwidth_backend_with_delete_should_disconnect
		old_tel = "+15556667777"
		new_tel = "+12222222222"

		bandwidth_tn_repo_mock = setup_bandwidth_tn_repo_mock(
			should_disconnect: true,
			old_tel: old_tel,
			new_tel: new_tel
		)

		BandwidthTnRepo.stub :new, bandwidth_tn_repo_mock do
			execute_command { |exe|
				sgx = Minitest::Mock.new(OpenStruct.new(
					registered?: OpenStruct.new(phone: old_tel),
					jid: Blather::JID.new(CONFIG[:sgx])
				))

				target_customer = customer(sgx: sgx)
				admin = AdminCommand.for(target_customer, exe.customer_repo)

				exe.customer_repo.expect(
					:find_by_jid,
					EMPromise.resolve(customer(sgx: sgx)),
					[Matching.new do |jid|
						assert jid.is_a? Blather::JID
						assert_equal jid.stripped.to_s, "test@example.com"
					end]
				)

				AdminAction::NumberChange::REDIS.expect(
					:exists,
					EMPromise.resolve(1),
					["catapult_jid-#{old_tel}"]
				)

				AdminAction::NumberChange::REDIS.expect(
					:rename,
					EMPromise.resolve(nil),
					["catapult_fwd-#{old_tel}", "catapult_fwd-#{new_tel}"]
				)

				expected_xadd_args = {
					customer_id: "test",
					old_tel: old_tel,
					new_tel: new_tel,
					should_delete: "true",
					actor_id: "test",
					class: "NumberChange",
					direction: :forward
				}.freeze

				AdminActionRepo::REDIS.expect(
					:xadd,
					EMPromise.resolve(nil)
				) do |admin_actions, star, **xadd_args|
					assert_equal admin_actions, "admin_actions"
					assert_equal star, "*"

					xadd_args.each do |k, v|
						assert_equal v, expected_xadd_args[k]
					end
				end

				Command::COMMAND_MANAGER.expect(
					:write,
					EMPromise.resolve(
						change_number_form(
							new_tel: new_tel,
							should_delete: true
						)
					),
					[Matching.new do |iq|
						assert_undoable_form(iq)
						assert_change_number_form(iq)
					end]
				)

				AdminAction::NumberChange::REDIS.expect(
					:get,
					EMPromise.resolve(nil),
					["jmp_customer_backend_sgx-test"]
				)

				AdminAction::NumberChange::REDIS.expect(
					:del,
					EMPromise.resolve(nil),
					["jmp_customer_backend_sgx-test"]
				)

				AdminAction::NumberChange::REDIS.expect(
					:get,
					EMPromise.resolve(nil),
					["jmp_customer_backend_sgx-test"]
				)

				BackendSgx::REDIS.expect(
					:set,
					EMPromise.resolve(nil),
					[
						"catapult_jid-#{new_tel}",
						"customer_test@component"
					]
				)

				BackendSgx::IQ_MANAGER.expect(
					:write,
					EMPromise.resolve(nil)
				) do |iq|
					assert_ibr_deregister_form(iq)
				end

				BackendSgx::IQ_MANAGER.expect(
					:write,
					EMPromise.resolve(nil)
				) do |iq|
					assert_ibr_register_form(
						iq,
						new_tel,
						nick: "test_bw_account",
						username: "test_bw_user",
						password: "test_bw_password"
					)
				end

				result = admin.action_number_change.sync

				slug, backend_report, number_report = result.lines

				assert_equal slug.strip, "Action : Change Number"
				assert_equal(
					backend_report.strip,
					"[move backend?]: sgx -> sgx"
				)
				assert_equal(
					number_report.strip,
					"[change number?]: #{old_tel} -> #{new_tel}"
				)

				assert_mock Command::COMMAND_MANAGER
				assert_mock exe.customer_repo
				assert_mock AdminAction::NumberChange::REDIS
				assert_mock AdminActionRepo::REDIS
				assert_mock BackendSgx::REDIS
				assert_mock BackendSgx::IQ_MANAGER
				assert_mock bandwidth_tn_repo_mock
				assert_mock sgx
			}
		end
	end
	em :test_change_num_bandwidth_backend_with_delete_should_disconnect

	def test_change_num_non_bw_backend_should_not_disconnect
		old_tel = "+15556667777"
		new_tel = "+12222222222"
		non_bw_backend = "route_value"

		bandwidth_tn_repo_mock = setup_bandwidth_tn_repo_mock(
			should_disconnect: false
		)

		BandwidthTnRepo.stub :new, bandwidth_tn_repo_mock do
			execute_command { |exe|
				Command::COMMAND_MANAGER.expect(
					:write,
					EMPromise.resolve(change_number_form(new_tel: new_tel)),
					[Matching.new do |iq|
						assert_undoable_form(iq)
						assert_change_number_form(iq)
					end]
				)

				non_bandwidth_sgx = Minitest::Mock.new(OpenStruct.new(
					registered?: OpenStruct.new(phone: old_tel),
					jid: Blather::JID.new(non_bw_backend)
				))

				target_customer = customer(sgx: non_bandwidth_sgx)
				admin = AdminCommand.for(target_customer, exe.customer_repo)

				exe.customer_repo.expect(
					:find_by_jid,
					EMPromise.resolve(target_customer),
					[Blather::JID.new("test@example.com")]
				)

				AdminAction::NumberChange::REDIS.expect(
					:exists,
					EMPromise.resolve(1),
					["catapult_jid-+15556667777"]
				)

				AdminAction::NumberChange::REDIS.expect(
					:rename,
					EMPromise.resolve(nil),
					["catapult_fwd-#{old_tel}", "catapult_fwd-#{new_tel}"]
				)

				AdminAction::NumberChange::REDIS.expect(
					:get,
					EMPromise.resolve("route_value"),
					["jmp_customer_backend_sgx-#{target_customer.customer_id}"]
				)

				BackendSgx::IQ_MANAGER.expect(
					:write,
					EMPromise.resolve(nil)
				) do |iq|
					assert_ibr_deregister_form(
						iq,
						target_backend_sgx: "route_value"
					)
				end

				AdminAction::NumberChange::REDIS.expect(
					:set,
					EMPromise.resolve(nil),
					[
						"jmp_customer_backend_sgx-#{target_customer.customer_id}",
						"route_value"
					]
				)

				AdminAction::NumberChange::REDIS.expect(
					:get,
					EMPromise.resolve("route_value"),
					["jmp_customer_backend_sgx-#{target_customer.customer_id}"]
				)

				BackendSgx::IQ_MANAGER.expect(
					:write,
					EMPromise.resolve(nil)
				) do |iq|
					assert_ibr_register_form(
						iq,
						"+12222222222",
						nick: "test_sgx_account",
						username: "test_sgx_user",
						password: "test_sgx_password",
						target_backend_sgx: "route_value"
					)
				end

				BackendSgx::REDIS.expect(
					:set,
					EMPromise.resolve(nil),
					["catapult_jid-+12222222222", "customer_test@component"]
				)

				expected_xadd_args = {
					customer_id: "test",
					old_tel: old_tel,
					new_tel: new_tel,
					should_delete: "true",
					actor_id: "test",
					class: "NumberChange",
					direction: :forward
				}.freeze

				AdminActionRepo::REDIS.expect(
					:xadd,
					EMPromise.resolve(nil)
				) do |admin_actions, star, **xadd_args|
					assert_equal admin_actions, "admin_actions"
					assert_equal star, "*"

					xadd_args.each do |k, v|
						assert_equal v, expected_xadd_args[k]
					end
				end

				result = admin.action_number_change.sync

				slug, backend_report, number_report = result.lines

				assert_equal slug.strip, "Action : Change Number"
				assert_equal(
					backend_report.strip,
					"[move backend?]: #{non_bw_backend} -> #{non_bw_backend}"
				)
				assert_equal(
					number_report.strip,
					"[change number?]: +15556667777 -> +12222222222"
				)

				assert_mock Command::COMMAND_MANAGER
				assert_mock exe.customer_repo
				assert_mock AdminAction::NumberChange::REDIS
				assert_mock AdminActionRepo::REDIS
				assert_mock BackendSgx::REDIS
				assert_mock BackendSgx::IQ_MANAGER
				assert_mock bandwidth_tn_repo_mock
				assert_mock non_bandwidth_sgx
			}
		end
	end
	em :test_change_num_non_bw_backend_should_not_disconnect
end
