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