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