# frozen_string_literal: true

require "test_helper"
require_relative "../sgx-bwmsgsv2"

def panic(e)
	$panic = e
end

class ComponentTest < Minitest::Test
	def setup
		SGXbwmsgsv2.instance_variable_set(:@written, [])

		def SGXbwmsgsv2.write_to_stream(s)
			@written ||= []
			@written << s
		end

		REDIS.reset!
		REDIS.set("catapult_cred-test@example.com", [
			'account', 'user', 'password', '+15550000000'
		])
	end

	def written
		SGXbwmsgsv2.instance_variable_get(:@written)
	end

	def xmpp_error_name(error)
		error.find_first(
			"child::*[name()!='text']",
			Blather::StanzaError::STANZA_ERR_NS
		).element_name
	end

	def xmpp_error_text(error)
		error.find_first("ns:text", ns: Blather::StanzaError::STANZA_ERR_NS)&.text
	end

	def process_stanza(s)
		SGXbwmsgsv2.send(:client).receive_data(s)
		raise $panic if $panic
	end

	def test_message_unregistered
		m = Blather::Stanza::Message.new("+15551234567@component", "a"*4096)
		m.from = "unknown@example.com"
		process_stanza(m)

		assert_equal 1, written.length

		stanza = Blather::XMPPNode.parse(written.first.to_xml)
		assert stanza.error?
		error = stanza.find_first("error")
		assert_equal "auth", error["type"]
		assert_equal "registration-required", xmpp_error_name(error)
	end
	em :test_message_unregistered

	def test_message_too_long
		req = stub_request(
			:post,
			"https://messaging.bandwidth.com/api/v2/users/account/messages"
		).with(body: {
			from: "+15550000000",
			to: "+15551234567",
			text: "a"*4096,
			applicationId: nil,
			tag: " "
		}).to_return(status: 400, body: JSON.dump(
			description: "Bad text.",
			fieldErrors: [{ description: "4096 not allowed" }]
		))

		m = Blather::Stanza::Message.new("+15551234567@component", "a"*4096)
		m.from = "test@example.com"
		process_stanza(m)

		assert_requested req
		assert_equal 1, written.length

		stanza = Blather::XMPPNode.parse(written.first.to_xml)
		assert stanza.error?
		error = stanza.find_first("error")
		assert_equal "cancel", error["type"]
		assert_equal "internal-server-error", xmpp_error_name(error)
		assert_equal "Bad text. 4096 not allowed", xmpp_error_text(error)
	end
	em :test_message_too_long

	def test_message_to_component_not_group
		m = Blather::Stanza::Message.new("component", "a"*4096)
		m.from = "test@example.com"
		process_stanza(m)

		assert_equal 1, written.length

		stanza = Blather::XMPPNode.parse(written.first.to_xml)
		assert stanza.error?
		error = stanza.find_first("error")
		assert_equal "cancel", error["type"]
		assert_equal "item-not-found", xmpp_error_name(error)
	end
	em :test_message_to_component_not_group

	def test_message_to_invalid_num
		m = Blather::Stanza::Message.new("123@component", "a"*4096)
		m.from = "test@example.com"
		process_stanza(m)

		assert_equal 1, written.length

		stanza = Blather::XMPPNode.parse(written.first.to_xml)
		assert stanza.error?
		error = stanza.find_first("error")
		assert_equal "cancel", error["type"]
		assert_equal "item-not-found", xmpp_error_name(error)
	end
	em :test_message_to_invalid_num

	def test_message_to_anonymous
		m = Blather::Stanza::Message.new(
			"1;phone-context=anonymous.phone-context.soprani.ca@component",
			"a"*4096
		)
		m.from = "test@example.com"
		process_stanza(m)

		assert_equal 1, written.length

		stanza = Blather::XMPPNode.parse(written.first.to_xml)
		assert stanza.error?
		error = stanza.find_first("error")
		assert_equal "cancel", error["type"]
		assert_equal "gone", xmpp_error_name(error)
	end
	em :test_message_to_anonymous

	def test_blank_message
		m = Blather::Stanza::Message.new("+15551234567@component", " ")
		m.from = "test@example.com"
		process_stanza(m)

		assert_equal 1, written.length

		stanza = Blather::XMPPNode.parse(written.first.to_xml)
		assert stanza.error?
		error = stanza.find_first("error")
		assert_equal "modify", error["type"]
		assert_equal "policy-violation", xmpp_error_name(error)
	end
	em :test_blank_message

	def test_ibr_bad_tel
		iq = Blather::Stanza::Iq::IBR.new(:set, "component")
		iq.from = "newuser@example.com"
		iq.phone = "5551234567"
		process_stanza(iq)

		assert_equal 1, written.length

		stanza = Blather::XMPPNode.parse(written.first.to_xml)
		assert stanza.error?
		error = stanza.find_first("error")
		assert_equal "cancel", error["type"]
		assert_equal "item-not-found", xmpp_error_name(error)
	end
	em :test_ibr_bad_tel

	def test_ibr_bad_creds
		stub_request(
			:get,
			"https://messaging.bandwidth.com/api/v2/users/acct/media"
		).with(basic_auth: ["user", "pw"]).to_return(status: 401)

		iq = Blather::Stanza::Iq::IBR.new(:set, "component")
		iq.from = "newuser@example.com"
		iq.phone = "+15551234567"
		iq.nick = "acct"
		iq.username = "user"
		iq.password = "pw"
		process_stanza(iq)

		assert_equal 1, written.length

		stanza = Blather::XMPPNode.parse(written.first.to_xml)
		assert stanza.error?
		error = stanza.find_first("error")
		assert_equal "auth", error["type"]
		assert_equal "not-authorized", xmpp_error_name(error)
	end
	em :test_ibr_bad_creds

	def test_ibr_number_not_found
		stub_request(
			:get,
			"https://messaging.bandwidth.com/api/v2/users/acct/media"
		).with(basic_auth: ["user", "pw"]).to_return(status: 404)

		iq = Blather::Stanza::Iq::IBR.new(:set, "component")
		iq.from = "newuser@example.com"
		iq.phone = "+15551234567"
		iq.nick = "acct"
		iq.username = "user"
		iq.password = "pw"
		process_stanza(iq)

		assert_equal 1, written.length

		stanza = Blather::XMPPNode.parse(written.first.to_xml)
		assert stanza.error?
		error = stanza.find_first("error")
		assert_equal "cancel", error["type"]
		assert_equal "item-not-found", xmpp_error_name(error)
	end
	em :test_ibr_number_not_found

	def test_ibr_other_error
		stub_request(
			:get,
			"https://messaging.bandwidth.com/api/v2/users/acct/media"
		).with(basic_auth: ["user", "pw"]).to_return(status: 400)

		iq = Blather::Stanza::Iq::IBR.new(:set, "component")
		iq.from = "newuser@example.com"
		iq.phone = "+15551234567"
		iq.nick = "acct"
		iq.username = "user"
		iq.password = "pw"
		process_stanza(iq)

		assert_equal 1, written.length

		stanza = Blather::XMPPNode.parse(written.first.to_xml)
		assert stanza.error?
		error = stanza.find_first("error")
		assert_equal "modify", error["type"]
		assert_equal "not-acceptable", xmpp_error_name(error)
	end
	em :test_ibr_other_error

	def test_ibr_conflict
		stub_request(
			:get,
			"https://messaging.bandwidth.com/api/v2/users/acct/media"
		).with(basic_auth: ["user", "pw"]).to_return(status: 200, body: "[]")

		iq = Blather::Stanza::Iq::IBR.new(:set, "component")
		iq.from = "test@example.com"
		iq.phone = "+15550000000"
		iq.nick = "acct"
		iq.username = "user"
		iq.password = "pw"
		process_stanza(iq)

		assert_equal 1, written.length

		stanza = Blather::XMPPNode.parse(written.first.to_xml)
		assert stanza.error?
		error = stanza.find_first("error")
		assert_equal "cancel", error["type"]
		assert_equal "conflict", xmpp_error_name(error)
		assert_equal(
			"Another user exists for +15550000000",
			xmpp_error_text(error)
		)
	end
	em :test_ibr_conflict

	def test_ibr_remove
		iq = Blather::Stanza::Iq::IBR.new(:set, "component")
		iq.from = "test@example.com"
		iq.remove!
		process_stanza(iq)

		refute REDIS.get("catapult_cred-test@example.com").sync

		assert_equal 1, written.length

		stanza = Blather::XMPPNode.parse(written.first.to_xml)
		assert stanza.result?
	end
	em :test_ibr_remove

	def test_ibr_form
		stub_request(
			:get,
			"https://messaging.bandwidth.com/api/v2/users/acct/media"
		).with(basic_auth: ["user", "pw"]).to_return(status: 200, body: "[]")

		iq = Blather::Stanza::Iq::IBR.new(:set, "component")
		iq.from = "formuser@example.com"
		form = Blather::Stanza::X.find_or_create(iq.query)
		form.fields = [
			{
				var: "nick",
				value: "acct"
			},
			{
				var: "username",
				value: "user"
			},
			{
				var: "password",
				value: "pw"
			},
			{
				var: "phone",
				value: "+15551234567"
			}
		]
		process_stanza(iq)

		assert_equal(
			["acct", "user", "pw", "+15551234567"],
			REDIS.get("catapult_cred-formuser@example.com").sync
		)

		assert_equal(
			"formuser@example.com",
			REDIS.get("catapult_jid-+15551234567").sync
		)

		assert_equal 1, written.length
		stanza = Blather::XMPPNode.parse(written.first.to_xml)
		assert stanza.result?
	end
	em :test_ibr_form

	def test_ibr_get_form_registered
		iq = Blather::Stanza::Iq::IBR.new(:get, "component")
		iq.from = "test@example.com"
		process_stanza(iq)

		assert_equal 1, written.length
		stanza = Blather::XMPPNode.parse(written.first.to_xml)
		assert stanza.result?
		assert stanza.registered?
		assert_equal(
			["nick", "username", "password", "phone"],
			stanza.form.fields.map(&:var)
		)
		assert stanza.instructions
		assert stanza.nick
		assert stanza.username
		assert stanza.password
		assert stanza.phone
		refute stanza.email
	end
	em :test_ibr_get_form_registered
end
