# frozen_string_literal: true

require "test_helper"
require "tel_selections"

class TelSelectionsTest < Minitest::Test
	def setup
		@manager = TelSelections.new(
			redis: FakeRedis.new,
			db: FakeDB.new,
			memcache: FakeMemcache.new
		)
	end

	def test_set_get
		assert_kind_of TelSelections::ChooseTel, @manager["jid@example.com"].sync
		@manager.set(
			"jid@example.com",
			TelSelections::ChooseTel::Tn.for_pending_value("+15555550000")
		).sync
		assert_kind_of TelSelections::HaveTel, @manager["jid@example.com"].sync
	end
	em :test_set_get

	def test_choose_tel_have_tel
		jid = "jid@example.com"
		@manager.set(
			jid,
			TelSelections::ChooseTel::Tn.for_pending_value("+15555550000")
		).sync
		assert_equal "+15555550000", @manager[jid].then(&:choose_tel).sync.tel
	end
	em :test_choose_tel_have_tel

	class AvailableNumberTest < Minitest::Test
		def test_for_no_rsm
			form = Blather::Stanza::X.new
			form.fields = [{ var: "q", value: "226" }]
			iris_query = TelSelections::ChooseTel::AvailableNumber
				.for(form, db: FakeDB.new, memcache: FakeMemcache.new)
				.instance_variable_get(:@iris_query)
			assert_equal(
				{ areaCode: "226", enableTNDetail: true, LCA: false, quantity: 10 },
				iris_query
			)
		end

		def test_for_rsm
			form = Blather::Stanza::X.new
			form.fields = [{ var: "q", value: "226" }]
			Nokogiri::XML::Builder.with(form) do
				set(xmlns: "http://jabber.org/protocol/rsm") do
					max 500
				end
			end
			iris_query = TelSelections::ChooseTel::AvailableNumber
				.for(form, db: FakeDB.new, memcache: FakeMemcache.new)
				.instance_variable_get(:@iris_query)
			# quantity should be 500 due to max inside tel selections
			assert_equal(
				{ areaCode: "226", enableTNDetail: true, LCA: false, quantity: 500 },
				iris_query
			)
		end

		def test_for_feelinglucky
			form = Blather::Stanza::X.new
			form.fields = [
				{ var: "q", value: "" },
				{
					var: "http://jabber.org/protocol/commands#actions",
					value: "feelinglucky"
				}
			]
			iris_query = TelSelections::ChooseTel::AvailableNumber
				.for(form, db: FakeDB.new, memcache: FakeMemcache.new)
				.instance_variable_get(:@iris_query)
			assert_equal(
				{ areaCode: "810", enableTNDetail: true, LCA: false, quantity: 10 },
				iris_query
			)
		end

		def test_fallback
			stub_request(
				:get,
				"https://dashboard.bandwidth.com/v1.0/accounts//availableNumbers" \
				"?city=Kitchener-Waterloo&enableTNDetail=true&lCA=false&" \
				"quantity=10&state=ON"
			).to_return(status: 200, body: "")

			stub_request(
				:get,
				"https://geocoder.ca/?json=1&locate=Kitchener-Waterloo,%20ON"
			).to_return(status: 200, body: {
				postal: "N2H", longt: 0, latt: 0
			}.to_json)

			stub_request(
				:get,
				"https://dashboard.bandwidth.com/v1.0/accounts//availableNumbers" \
				"?areaCode=226&enableTNDetail=true&quantity=10"
			).to_return(status: 200, body: <<~XML)
				<SearchResult>
					<TelephoneNumberList>
						<TelephoneNumber>
							<FullNumber>22655512345</FullNumber>
							<City>Somewhere</City>
							<State>ON</State>
						</TelephoneNumber>
					</TelephoneNumberList>
				</SearchResult>
			XML

			db = FakeDB.new(
				["CA", "POINT(0.0000000000 0.0000000000)", 3] =>
					[{ "area_code" => "226" }]
			)
			form = Blather::Stanza::X.new
			form.fields = [{ var: "q", value: "Kitchener, ON" }]
			tns = execute_command do
				TelSelections::ChooseTel::AvailableNumber
					.for(form, db: db, memcache: FakeMemcache.new)
					.tns
			end
			assert_equal(
				["(226) 555-12345 (Somewhere, ON)"],
				tns.map(&:to_s)
			)
		end
		em :test_fallback

		def test_local_inventory
			stub_request(
				:get,
				"https://dashboard.bandwidth.com/v1.0/accounts//availableNumbers" \
					"?city=Kitchener-Waterloo&enableTNDetail=true&lCA=false&" \
					"quantity=10&state=ON"
			).to_return(status: 200, body: "")

			db = FakeDB.new(
				["ON", "Kitchener-Waterloo"] => [{
					"tel" => "+122655512345",
					"region" => "ON",
					"locality" => "Kitchener-Waterloo"
				}]
			)
			form = Blather::Stanza::X.new
			form.fields = [{ var: "q", value: "Kitchener, ON" }]
			tns = execute_command do
				TelSelections::ChooseTel::AvailableNumber
					.for(form, db: db, memcache: FakeMemcache.new)
					.tns
			end
			assert_equal(
				["(226) 555-12345 (Kitchener-Waterloo, ON)"],
				tns.map(&:to_s)
			)
		end
		em :test_local_inventory
	end

	class TnOptionTest < Minitest::Test
		def setup
			@tn = TelSelections::ChooseTel::Tn::Option.new(
				full_number: "5551234567",
				city: "Toronto",
				state: "ON",
				garbage: "stuff"
			)
			@local_tel = TelSelections::ChooseTel::Tn::LocalInventory.new(
				@tn,
				"test_account",
				price: 10.0
			)
		end

		def test_to_s
			assert_equal "(555) 123-4567 (Toronto, ON)", @tn.to_s
		end

		def test_tel
			assert_equal "+15551234567", @tn.tel
		end

		def test_option
			assert_equal(
				Blather::Stanza::X::Field::Option.new(
					label: "(555) 123-4567 (Toronto, ON)",
					value: "+15551234567"
				),
				@tn.option
			)
		end

		def test_option_positive_price
			assert_equal(
				Blather::Stanza::X::Field::Option.new(
					label: "(555) 123-4567 (Toronto, ON) +$10.00",
					value: "+15551234567"
				),
				@local_tel.option
			)
		end

		def test_option_reference
			ref = @tn.option.find("ns:reference", ns: "urn:xmpp:reference:0").first
			assert_equal(
				@tn.formatted_tel,
				@tn.option.label[ref["begin"].to_i..ref["end"].to_i]
			)
			assert_equal "tel:+15551234567", ref["uri"]
		end
	end

	class QTest < Minitest::Test
		def test_for_area_code
			q = TelSelections::ChooseTel::Q.for("226")
			assert_equal({ areaCode: "226" }, q.iris_query)
		end

		def test_for_area_code_sql
			q = TelSelections::ChooseTel::Q.for("226")
			assert_equal(
				[
					"SELECT * FROM tel_inventory " \
					"WHERE available_after < LOCALTIMESTAMP AND tel LIKE $1",
					"+1226%"
				],
				q.sql_query
			)
		end

		def test_for_npanxx
			q = TelSelections::ChooseTel::Q.for("226666")
			assert_equal({ npaNxx: "226666" }, q.iris_query)
		end

		def test_for_npanxx_sql
			q = TelSelections::ChooseTel::Q.for("226666")
			assert_equal(
				[
					"SELECT * FROM tel_inventory " \
					"WHERE available_after < LOCALTIMESTAMP AND tel LIKE $1",
					"+1226666%"
				],
				q.sql_query
			)
		end

		def test_for_npanxxx
			q = TelSelections::ChooseTel::Q.for("2266667")
			assert_equal({ npaNxxx: "2266667" }, q.iris_query)
		end

		def test_for_npanxxx_sql
			q = TelSelections::ChooseTel::Q.for("2266667")
			assert_equal(
				[
					"SELECT * FROM tel_inventory " \
					"WHERE available_after < LOCALTIMESTAMP AND tel LIKE $1",
					"+12266667%"
				],
				q.sql_query
			)
		end

		def test_for_zip
			q = TelSelections::ChooseTel::Q.for("90210")
			assert_equal({ zip: "90210" }, q.iris_query)
		end

		def test_for_zip_sql
			q = TelSelections::ChooseTel::Q.for("90210")
			refute q.sql_query
		end

		def test_for_localvanity
			q = TelSelections::ChooseTel::Q.for("~mboa")
			assert_equal({ localVanity: "mboa" }, q.iris_query)
		end

		def test_for_localvanity_sql
			q = TelSelections::ChooseTel::Q.for("~mboa")
			assert_equal(
				[
					"SELECT * FROM tel_inventory " \
					"WHERE available_after < LOCALTIMESTAMP AND tel LIKE $1",
					"%6262%"
				],
				q.sql_query
			)
		end

		def test_for_state
			q = TelSelections::ChooseTel::Q.for("ON")
			assert_equal({ state: "ON" }, q.iris_query)
		end

		def test_for_state_sql
			q = TelSelections::ChooseTel::Q.for("ON")
			assert_equal(
				[
					"SELECT * FROM tel_inventory " \
					"WHERE available_after < LOCALTIMESTAMP AND region = $1",
					"ON"
				],
				q.sql_query
			)
		end

		def test_for_state_name
			q = TelSelections::ChooseTel::Q.for("ontario")
			assert_equal({ state: "ON" }, q.iris_query)
		end

		def test_for_state_name_sql
			q = TelSelections::ChooseTel::Q.for("ontario")
			assert_equal(
				[
					"SELECT * FROM tel_inventory " \
					"WHERE available_after < LOCALTIMESTAMP AND region = $1",
					"ON"
				],
				q.sql_query
			)
		end

		def test_for_new_york
			q = TelSelections::ChooseTel::Q.for("New York")
			assert_equal({ state: "NY" }, q.iris_query)
		end

		def test_for_new_york_sql
			q = TelSelections::ChooseTel::Q.for("New York")
			assert_equal(
				[
					"SELECT * FROM tel_inventory " \
					"WHERE available_after < LOCALTIMESTAMP AND region = $1",
					"NY"
				],
				q.sql_query
			)
		end

		def test_for_new_york_ny
			q = TelSelections::ChooseTel::Q.for(
				"New York, NY",
				db: FakeDB.new,
				memcache: FakeMemcache.new
			)
			assert_equal({ city: "New York City", state: "NY" }, q.iris_query)
		end

		def test_for_new_york_ny_sql
			q = TelSelections::ChooseTel::Q.for(
				"New York, NY",
				db: FakeDB.new,
				memcache: FakeMemcache.new
			)
			assert_equal(
				[
					"SELECT * FROM tel_inventory " \
					"WHERE available_after < LOCALTIMESTAMP " \
					"AND region = $1 AND locality = $2",
					"NY", "New York City"
				],
				q.sql_query
			)
		end

		def test_for_new_york_new_york
			q = TelSelections::ChooseTel::Q.for(
				"New York, New York",
				db: FakeDB.new,
				memcache: FakeMemcache.new
			)
			assert_equal({ city: "New York City", state: "NY" }, q.iris_query)
		end

		def test_for_new_york_new_york_sql
			q = TelSelections::ChooseTel::Q.for(
				"New York, New York",
				db: FakeDB.new,
				memcache: FakeMemcache.new
			)
			assert_equal(
				[
					"SELECT * FROM tel_inventory " \
					"WHERE available_after < LOCALTIMESTAMP " \
					"AND region = $1 AND locality = $2",
					"NY", "New York City"
				],
				q.sql_query
			)
		end

		def test_for_citystate
			q = TelSelections::ChooseTel::Q.for(
				"Toronto, ON",
				db: FakeDB.new,
				memcache: FakeMemcache.new
			)
			assert_equal({ city: "Toronto", state: "ON" }, q.iris_query)
		end

		def test_for_citystate_sql
			q = TelSelections::ChooseTel::Q.for(
				"Toronto, ON",
				db: FakeDB.new,
				memcache: FakeMemcache.new
			)
			assert_equal(
				[
					"SELECT * FROM tel_inventory " \
					"WHERE available_after < LOCALTIMESTAMP " \
					"AND region = $1 AND locality = $2",
					"ON", "Toronto"
				],
				q.sql_query
			)
		end

		def test_for_citystate_name
			q = TelSelections::ChooseTel::Q.for(
				"Toronto, Ontario",
				db: FakeDB.new,
				memcache: FakeMemcache.new
			)
			assert_equal({ city: "Toronto", state: "ON" }, q.iris_query)
		end

		def test_for_citystate_name_sql
			q = TelSelections::ChooseTel::Q.for(
				"Toronto, Ontario",
				db: FakeDB.new,
				memcache: FakeMemcache.new
			)
			assert_equal(
				[
					"SELECT * FROM tel_inventory " \
					"WHERE available_after < LOCALTIMESTAMP " \
					"AND region = $1 AND locality = $2",
					"ON", "Toronto"
				],
				q.sql_query
			)
		end

		def test_for_garbage
			assert_raises { TelSelections::ChooseTel::Q.for("garbage") }
		end
	end
end
