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