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, rate, usage, supported, **kwargs)
10 credit = [customer.minute_limit.to_d - usage, 0].max + customer.balance
11 if !supported
12 Unsupported.new(**kwargs.slice(:direction))
13 elsif credit < rate * 10
14 NoBalance.for(customer, rate, usage, supported, **kwargs)
15 else
16 for_ask_or_go(customer, rate, usage, credit, **kwargs)
17 end
18 end
19
20 def self.for_ask_or_go(customer, rate, usage, credit, digits: nil, **kwargs)
21 kwargs.merge!(
22 customer_id: customer.customer_id,
23 limit_remaining: limit_remaining(customer, usage, rate),
24 max_minutes: (credit / rate).to_i
25 )
26 if digits != "1" && limit_remaining(customer, usage, rate) < 10
27 AtLimit.new(**kwargs)
28 else
29 new(**kwargs)
30 end
31 end
32
33 def self.limit_remaining(customer, usage, rate)
34 can_use = customer.minute_limit.to_d + customer.monthly_overage_limit
35 ([can_use - usage, 0].max / rate).to_i
36 end
37
38 value_semantics do
39 customer_id String
40 from String
41 to(/\A\+\d+\Z/)
42 call_id String
43 direction Either(:inbound, :outbound)
44 limit_remaining Integer
45 max_minutes Integer
46 end
47
48 def to_render
49 ["#{direction}/connect", { locals: to_h }]
50 end
51
52 def create_call(fwd, *args, &block)
53 fwd.create_call(*args, &block)
54 end
55
56 def as_json(*)
57 {
58 from: from,
59 to: to,
60 customer_id: customer_id,
61 limit_remaining: limit_remaining,
62 max_minutes: max_minutes
63 }
64 end
65
66 def to_json(*args)
67 as_json.to_json(*args)
68 end
69
70 class Unsupported
71 value_semantics do
72 direction Either(:inbound, :outbound)
73 end
74
75 def view
76 "#{direction}/unsupported"
77 end
78
79 def tts
80 TTSTemplate.new(view).tts(self)
81 end
82
83 def to_render
84 [view]
85 end
86
87 def create_call(*); end
88
89 def as_json(*)
90 { tts: tts }
91 end
92
93 def to_json(*args)
94 as_json.to_json(*args)
95 end
96 end
97
98 class NoBalance
99 def self.for(customer, rate, usage, supported, direction:, **kwargs)
100 LowBalance.for(customer).then(&:notify!).then do |amount|
101 if amount&.positive?
102 CallAttempt.for(
103 customer.with_balance(customer.balance + amount),
104 rate, usage, supported, direction: direction, **kwargs
105 )
106 else
107 NoBalance.new(balance: customer.balance, direction: direction)
108 end
109 end
110 end
111
112 value_semantics do
113 balance Numeric
114 direction Either(:inbound, :outbound)
115 end
116
117 def view
118 "#{direction}/no_balance"
119 end
120
121 def tts
122 TTSTemplate.new(view).tts(self)
123 end
124
125 def to_render
126 [view, { locals: to_h }]
127 end
128
129 def create_call(*); end
130
131 def as_json(*)
132 { tts: tts }
133 end
134
135 def to_json(*args)
136 as_json.to_json(*args)
137 end
138 end
139
140 class AtLimit
141 value_semantics do
142 customer_id String
143 from String
144 to(/\A\+\d+\Z/)
145 call_id String
146 direction Either(:inbound, :outbound)
147 limit_remaining Integer
148 max_minutes Integer
149 end
150
151 def view
152 "#{direction}/at_limit"
153 end
154
155 def tts
156 TTSTemplate.new(view).tts(self)
157 end
158
159 def to_render
160 [view, { locals: to_h }]
161 end
162
163 def create_call(fwd, *args, &block)
164 fwd.create_call(*args, &block)
165 end
166
167 def as_json(*)
168 {
169 tts: tts,
170 from: from,
171 to: to,
172 customer_id: customer_id,
173 limit_remaining: limit_remaining,
174 max_minutes: max_minutes
175 }
176 end
177
178 def to_json(*args)
179 as_json.to_json(*args)
180 end
181 end
182end