# 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
