1# frozen_string_literal: true
  2
  3require "value_semantics/monkey_patched"
  4
  5require_relative "tts_template"
  6require_relative "low_balance"
  7
  8class CallAttempt
  9	def self.for(customer:, usage:, **kwargs)
 10		credit = [(customer.minute_limit&.to_d || 100) - usage, 0].max +
 11		         customer.balance
 12		@kinds.each do |kind|
 13			ca = kind.call(
 14				customer: customer, usage: usage, credit: credit,
 15				**kwargs.merge(limits(customer, usage, credit, **kwargs))
 16			)
 17			return ca if ca
 18		end
 19
 20		raise "No CallAttempt matched"
 21	end
 22
 23	def self.limits(customer, usage, credit, rate:, **)
 24		unless customer&.minute_limit && usage && rate && rate.positive?
 25			return { limit_remaining: 100, max_minutes: 100 }
 26		end
 27
 28		can_use = customer.minute_limit.to_d + customer.monthly_overage_limit
 29		{
 30			limit_remaining: ([can_use - usage, 0].max / rate).to_i,
 31			max_minutes: (credit / rate).to_i
 32		}
 33	end
 34
 35	def self.register(&maybe_mk)
 36		@kinds ||= []
 37		@kinds << maybe_mk
 38	end
 39
 40	value_semantics do
 41		customer_id String
 42		from String
 43		to(/\A\+\d+\Z/)
 44		sgx Blather::JID
 45		call_id String
 46		direction Either(:inbound, :outbound)
 47		limit_remaining Integer
 48		max_minutes Integer
 49	end
 50
 51	def to_render
 52		["#{direction}/connect", { locals: to_h }]
 53	end
 54
 55	def to_s
 56		"Allowed(max_minutes: #{max_minutes}, limit_remaining: #{limit_remaining})"
 57	end
 58
 59	def create_call(fwd, *args, &block)
 60		fwd.create_call(*args, &block)
 61	end
 62
 63	def as_json(*)
 64		to_h
 65	end
 66
 67	def to_json(*args)
 68		as_json.to_json(*args)
 69	end
 70
 71	class TollFree
 72		CallAttempt.register do |rate:, customer:, **kwargs|
 73			if rate&.zero?
 74				new(
 75					**kwargs
 76						.merge(customer_id: customer.customer_id, sgx: customer.sgx)
 77						.slice(*value_semantics.attributes.map(&:name))
 78				)
 79			end
 80		end
 81
 82		value_semantics do
 83			customer_id String
 84			from String
 85			to(/\A\+\d+\Z/)
 86			sgx Blather::JID
 87			call_id String
 88			direction Either(:inbound, :outbound)
 89		end
 90
 91		def to_render
 92			["#{direction}/connect", { locals: to_h }]
 93		end
 94
 95		def to_s
 96			"TollFree"
 97		end
 98
 99		def create_call(fwd, *args, &block)
100			fwd.create_call(*args, &block)
101		end
102
103		def as_json(*)
104			to_h
105		end
106
107		def to_json(*args)
108			as_json.to_json(*args)
109		end
110	end
111
112	class Expired
113		CallAttempt.register do |customer:, direction:, **|
114			new(direction: direction) if customer.plan_name && !customer.active?
115		end
116
117		value_semantics do
118			direction Either(:inbound, :outbound)
119		end
120
121		def view
122			"#{direction}/expired"
123		end
124
125		def tts
126			TTSTemplate.new(view).tts(self)
127		end
128
129		def to_render
130			[view]
131		end
132
133		def to_s
134			"Expired"
135		end
136
137		def create_call(*); end
138
139		def as_json(*)
140			tts.empty? ? {} : { tts: tts }
141		end
142
143		def to_json(*args)
144			as_json.to_json(*args)
145		end
146	end
147
148	class Unsupported
149		CallAttempt.register do |supported:, direction:, **|
150			new(direction: direction) unless supported
151		end
152
153		value_semantics do
154			direction Either(:inbound, :outbound)
155		end
156
157		def view
158			"#{direction}/unsupported"
159		end
160
161		def tts
162			TTSTemplate.new(view).tts(self)
163		end
164
165		def to_render
166			[view]
167		end
168
169		def to_s
170			"Unsupported"
171		end
172
173		def create_call(*); end
174
175		def as_json(*)
176			tts.empty? ? {} : { tts: tts }
177		end
178
179		def to_json(*args)
180			as_json.to_json(*args)
181		end
182	end
183
184	class NoBalance
185		CallAttempt.register do |credit:, rate:, **kwargs|
186			self.for(rate: rate, **kwargs) if credit < rate * 10
187		end
188
189		def self.for(customer:, direction:, low_balance: LowBalance, **kwargs)
190			low_balance.for(customer).then(&:notify!).then do |amount|
191				if amount&.positive?
192					CallAttempt.for(
193						customer: customer.with_balance(customer.balance + amount),
194						**kwargs.merge(direction: direction)
195					)
196				else
197					NoBalance.new(balance: customer.balance, direction: direction)
198				end
199			end
200		end
201
202		value_semantics do
203			balance Numeric
204			direction Either(:inbound, :outbound)
205		end
206
207		def view
208			"#{direction}/no_balance"
209		end
210
211		def tts
212			TTSTemplate.new(view).tts(self)
213		end
214
215		def to_render
216			[view, { locals: to_h }]
217		end
218
219		def to_s
220			"NoBalance"
221		end
222
223		def create_call(*); end
224
225		def as_json(*)
226			tts.empty? ? {} : { tts: tts }
227		end
228
229		def to_json(*args)
230			as_json.to_json(*args)
231		end
232	end
233
234	class AtLimit
235		value_semantics do
236			customer_id String
237			from String
238			to(/\A\+\d+\Z/)
239			call_id String
240			direction Either(:inbound, :outbound)
241			limit_remaining Integer
242			max_minutes Integer
243		end
244
245		CallAttempt.register do |digits: nil, limit_remaining:, customer:, **kwargs|
246			if digits != "1" && limit_remaining < 10
247				new(
248					**kwargs
249						.merge(
250							limit_remaining: limit_remaining,
251							customer_id: customer.customer_id
252						).slice(*value_semantics.attributes.map(&:name))
253				)
254			end
255		end
256
257		def view
258			"#{direction}/at_limit"
259		end
260
261		def tts
262			TTSTemplate.new(view).tts(self)
263		end
264
265		def to_render
266			[view, { locals: to_h }]
267		end
268
269		def to_s
270			"AtLimit(max_minutes: #{max_minutes}, "\
271			"limit_remaining: #{limit_remaining})"
272		end
273
274		def create_call(fwd, *args, &block)
275			fwd.create_call(*args, &block)
276		end
277
278		def as_json(*)
279			{
280				tts: tts,
281				from: from,
282				to: to,
283				customer_id: customer_id,
284				limit_remaining: limit_remaining,
285				max_minutes: max_minutes
286			}
287		end
288
289		def to_json(*args)
290			as_json.to_json(*args)
291		end
292	end
293
294	register do |customer:, **kwargs|
295		new(
296			**kwargs
297				.merge(customer_id: customer.customer_id, sgx: customer.sgx)
298				.slice(*value_semantics.attributes.map(&:name))
299		)
300	end
301end