# frozen_string_literal: true require "value_semantics/monkey_patched" require_relative "tts_template" require_relative "low_balance" class CallAttempt def self.for(customer, rate, usage, trust_level, direction:, **kwargs) kwargs.merge!(direction: direction) credit = [customer.minute_limit.to_d - usage, 0].max + customer.balance if !rate || !trust_level.support_call?(rate) Unsupported.new(direction: direction) elsif credit < rate * 10 NoBalance.for(customer, rate, usage, trust_level, **kwargs) else for_ask_or_go(customer, rate, usage, credit, **kwargs) end end def self.for_ask_or_go(customer, rate, usage, credit, digits: nil, **kwargs) kwargs.merge!( customer_id: customer.customer_id, limit_remaining: limit_remaining(customer, usage, rate), max_minutes: (credit / rate).to_i ) if digits != "1" && limit_remaining(customer, usage, rate) < 10 AtLimit.new(**kwargs) else new(**kwargs) end end def self.limit_remaining(customer, usage, rate) can_use = customer.minute_limit.to_d + customer.monthly_overage_limit ([can_use - usage, 0].max / rate).to_i end value_semantics do customer_id String from String to(/\A\+\d+\Z/) call_id String direction Either(:inbound, :outbound) limit_remaining Integer max_minutes Integer end def to_render ["#{direction}/connect", { locals: to_h }] end def create_call(fwd, *args, &block) fwd.create_call(*args, &block) end def as_json(*) { from: from, to: to, customer_id: customer_id, limit_remaining: limit_remaining, max_minutes: max_minutes } end def to_json(*args) as_json.to_json(*args) end class Unsupported value_semantics do direction Either(:inbound, :outbound) end def view "#{direction}/unsupported" end def tts TTSTemplate.new(view).tts(self) end def to_render [view] end def create_call(*); end def as_json(*) { tts: tts } end def to_json(*args) as_json.to_json(*args) end end class NoBalance def self.for(customer, rate, usage, trust_level, direction:, **kwargs) LowBalance.for(customer).then(&:notify!).then do |amount| if amount&.positive? CallAttempt.for( customer.with_balance(customer.balance + amount), rate, usage, trust_level, direction: direction, **kwargs ) else NoBalance.new(balance: customer.balance, direction: direction) end end end value_semantics do balance Numeric direction Either(:inbound, :outbound) end def view "#{direction}/no_balance" end def tts TTSTemplate.new(view).tts(self) end def to_render [view, { locals: to_h }] end def create_call(*); end def as_json(*) { tts: tts } end def to_json(*args) as_json.to_json(*args) end end class AtLimit value_semantics do customer_id String from String to(/\A\+\d+\Z/) call_id String direction Either(:inbound, :outbound) limit_remaining Integer max_minutes Integer end def view "#{direction}/at_limit" end def tts TTSTemplate.new(view).tts(self) end def to_render [view, { locals: to_h }] end def create_call(fwd, *args, &block) fwd.create_call(*args, &block) end def as_json(*) { tts: tts, from: from, to: to, customer_id: customer_id, limit_remaining: limit_remaining, max_minutes: max_minutes } end def to_json(*args) as_json.to_json(*args) end end end