# frozen_string_literal: true

require "value_semantics/monkey_patched"

class ReachabilityRepo
	value_semantics do
		redis Anything(), default: LazyObject.new { REDIS }
		senders ArrayOf(String), default: CONFIG[:reachability_senders]
	end

	EXPIRY = 60 * 60 # 1 hr

	class SMS < self
		def type
			"sms"
		end
	end

	class Voice < self
		def type
			"voice"
		end
	end

	class NotTest
		def filter(**)
			EMPromise.resolve(yield)
		end
	end

	class Test
		def initialize(redis, key)
			@redis = redis
			@key = key
		end

		def filter(if_yes: ->(_) {}, **)
			@redis.incr(@key).then(&if_yes)
		end
	end

	# This one is for things that don't have a body.
	# I still don't want to deliver them to the user, but I don't want to count
	# them as a message either
	class Ignore
		def filter(if_yes: ->(_) {}, **)
			EMPromise.resolve(if_yes.call(nil))
		end
	end

	ACCEPTABLE_STANZA = Blather::Stanza::Message.new(nil, "I'm cool").freeze

	# The customer is who is being contacted
	# The initiator is the phone number trying to reach them
	def find(customer, initiator, stanza: ACCEPTABLE_STANZA)
		return EMPromise.resolve(NotTest.new) unless potential?(initiator)

		testing?(customer).then do |active|
			if active
				stanza.body ? Test.new(redis, key(customer)) : Ignore.new
			else
				NotTest.new
			end
		end
	end

	# This creates the keys if they don't exist, and returns the value
	def get_or_create(customer)
		redis.set(key(customer), 0, "NX", "EX", EXPIRY).then {
			redis.get(key(customer))
		}
	end

protected

	# This is basically an optimization to bail early without hitting a
	# datastore for 99% of all messages
	def potential?(tel)
		senders.include?(tel)
	end

	# This checks if this particular customer has reachability turned on
	def testing?(customer)
		redis.exists(key(customer)).then { |v| v == 1 }
	end

	# This gets the particular key for this check
	def key(customer)
		"jmp_customer_reachability_#{type}-#{customer.customer_id}"
	end
end
