1# frozen_string_literal: true
2
3require "bigdecimal"
4require "forwardable"
5require "relative_time"
6require "value_semantics/monkey_patched"
7
8require_relative "api"
9require_relative "call_attempt_repo"
10require_relative "customer"
11require_relative "customer_plan"
12require_relative "form_template"
13require_relative "promise_hash"
14require_relative "proxied_jid"
15
16class PlanInfo
17 extend Forwardable
18
19 def_delegators :customer, :expires_at, :auto_top_up_amount
20
21 def self.for(customer)
22 return EMPromise.resolve(NoPlan.new) unless customer&.plan_name
23
24 PromiseHash.all(
25 customer: customer,
26 start_date: customer.activation_date,
27 calling_charges_this_month: customer.calling_charges_this_month,
28 billing_customer_id: customer.billing_customer_id
29 ).then(method(:new))
30 end
31
32 class NoPlan
33 def template
34 FormTemplate.new("")
35 end
36
37 def admin_template
38 FormTemplate.new("")
39 end
40
41 def status
42 "Transitional"
43 end
44 end
45
46 value_semantics do
47 method_missing :customer, Customer
48 start_date Time
49 calling_charges_this_month BigDecimal
50 billing_customer_id String
51 end
52
53 def template
54 FormTemplate.for("plan_info", plan_info: self)
55 end
56
57 def admin_template
58 FormTemplate.for("admin_plan_info", plan_info: self)
59 end
60
61 def monthly_price
62 "$%.4f / month" % customer.monthly_price
63 end
64
65 def relative_start_date
66 RelativeTime.in_words(start_date)
67 end
68
69 def formatted_start_date
70 start_date.strftime("%Y-%m-%d %H:%M:%S")
71 end
72
73 def currency
74 (customer.currency || "No Currency").to_s
75 end
76
77 def status
78 customer.status.to_s.capitalize
79 end
80
81 def remaining_included_calling_credit
82 [customer.minute_limit.to_d - calling_charges_this_month, 0].max
83 end
84end
85
86class CustomerInfo
87 value_semantics do
88 plan_info Either(PlanInfo, PlanInfo::NoPlan)
89 tel Either(String, nil)
90 balance BigDecimal
91 cnam Either(String, nil)
92 end
93
94 def self.for(customer)
95 PromiseHash.all(
96 plan_info: PlanInfo.for(customer),
97 tel: customer.registered? ? customer.registered?.phone : nil,
98 balance: customer.balance,
99 cnam: customer.tndetails.dig(:features, :lidb, :subscriber_information)
100 ).then(&method(:new))
101 end
102
103 def form
104 FormTemplate.render("customer_info", info: self)
105 end
106end
107
108class AdminInfo
109 value_semantics do
110 jid ProxiedJID, coerce: ProxiedJID.method(:new)
111 customer_id String
112 fwd Either(CustomerFwd, nil)
113 info CustomerInfo
114 api API
115 call_info String
116 trust_level String
117 backend_jid String
118 end
119
120 def self.for(
121 customer,
122 trust_level_repo: TrustLevelRepo.new,
123 call_attempt_repo: CallAttemptRepo.new,
124 backend_repo: TrivialBackendSgxRepo.new
125 )
126 PromiseHash.all(
127 jid: customer.jid, customer_id: customer.customer_id,
128 fwd: customer.fwd,
129 info: CustomerInfo.for(customer),
130 api: API.for(customer),
131 call_info: call_info(customer, call_attempt_repo),
132 trust_level: trust_level_repo.find(customer).then(&:to_s),
133 backend_jid: backend_repo.get(customer.customer_id).from_jid.to_s
134 ).then(&method(:new))
135 end
136
137 class FakeLowBalance
138 def self.for(_)
139 self
140 end
141
142 def self.notify!
143 EMPromise.resolve(0)
144 end
145 end
146
147 def self.call_info(customer, call_attempt_repo)
148 if customer.registered?
149 call_attempt_repo
150 .find_outbound(customer, "+1", call_id: "dry_run")
151 .then(&:to_s)
152 else
153 EMPromise.resolve("No calling")
154 end
155 end
156
157 def form
158 FormTemplate.render("admin_info", admin_info: self)
159 end
160
161 def tel_link
162 [
163 "https://dashboard.bandwidth.com/portal/r/a",
164 CONFIG[:creds][:account],
165 "numbers/details",
166 info.tel.gsub(/\A\+1/, "")
167 ].join("/")
168 end
169
170 def support_link
171 CONFIG[:support_link].call(backend_jid)
172 end
173end