# frozen_string_literal: true

require "delegate"

module TrustLevel
	def self.for(plan_name:, settled_amount: 0, manual: nil)
		@levels.each do |level|
			tl = level.call(
				plan_name: plan_name,
				settled_amount: settled_amount,
				manual: manual
			)
			return manual ? Manual.new(tl) : tl if tl
		end

		raise "No TrustLevel matched"
	end

	def self.register(&maybe_mk)
		@levels ||= []
		@levels << maybe_mk
	end

	class Manual < SimpleDelegator
		def to_s
			"Manual(#{super})"
		end
	end

	class Tomb
		TrustLevel.register do |manual:, **|
			new if manual == "Tomb"
		end

		def max_top_up_amount
			0
		end

		def write_cdr?
			false
		end

		def support_call?(*)
			false
		end

		def send_message?(*)
			false
		end

		def validate_credit_card_transaction!(_amount, _declines)
			# Give a more ambiguous error so they don't know they're tombed.
			raise DeclinedError
		end

		def create_subaccount?(*)
			false
		end

		def to_s
			"Tomb"
		end
	end

	class Basement
		TrustLevel.register do |manual:, settled_amount:, **|
			new if manual == "Basement" || (!manual && settled_amount < 10)
		end

		def max_top_up_amount
			35
		end

		def max_declines
			2
		end

		def write_cdr?
			true
		end

		def support_call?(rate, concurrency)
			rate <= 0.02 && concurrency < 1
		end

		def send_message?(messages_today)
			messages_today < 40
		end

		def validate_credit_card_transaction!(amount, declines)
			raise DeclinedError.new(declines, max_declines) if declines > max_declines
			return unless amount > max_top_up_amount

			raise AmountTooHighError.new(amount, max_top_up_amount)
		end

		def create_subaccount?(already_have)
			already_have < 2
		end

		def to_s
			"Basement"
		end
	end

	class Paragon
		TrustLevel.register do |manual:, settled_amount:, **|
			new if manual == "Paragon" || (!manual && settled_amount > 60)
		end

		def max_top_up_amount
			500
		end

		def max_declines
			3
		end

		def write_cdr?
			true
		end

		def support_call?(_, concurrency)
			concurrency < 10
		end

		def send_message?(messages_today)
			messages_today < 700
		end

		def validate_credit_card_transaction!(amount, declines)
			raise DeclinedError.new(declines, max_declines) if declines > max_declines
			return unless amount > max_top_up_amount

			raise AmountTooHighError.new(amount, max_top_up_amount)
		end

		def create_subaccount?(already_have)
			already_have < 10
		end

		def to_s
			"Paragon"
		end
	end

	class Olympias
		TrustLevel.register do |manual:, **|
			new if manual == "Olympias"
		end

		def write_cdr?
			true
		end

		def support_call?(*)
			true
		end

		def send_message?(*)
			true
		end

		def validate_credit_card_transaction!(*) end

		def create_subaccount?(*)
			true
		end

		def to_s
			"Olympias"
		end
	end

	class Customer
		TrustLevel.register do |manual:, plan_name:, **|
			if manual && manual != "Customer"
				Sentry.capture_message("Unknown TrustLevel: #{manual}")
			end

			new(plan_name)
		end

		EXPENSIVE_ROUTE = {
			"usd_beta_unlimited-v20210223" => 0.9,
			"USD" => 0.9,
			"cad_beta_unlimited-v20210223" => 1.1,
			"CAD" => 1.1
		}.freeze

		def initialize(plan_name)
			@max_rate = EXPENSIVE_ROUTE.fetch(plan_name, 0.1)
		end

		def max_top_up_amount
			130
		end

		def max_declines
			2
		end

		def write_cdr?
			true
		end

		def support_call?(rate, concurrency)
			rate <= @max_rate && concurrency < 4
		end

		def send_message?(messages_today)
			messages_today < 500
		end

		def validate_credit_card_transaction!(amount, declines)
			raise DeclinedError.new(declines, max_declines) if declines > max_declines
			return unless amount > max_top_up_amount

			raise AmountTooHighError.new(amount, max_top_up_amount)
		end

		def create_subaccount?(already_have)
			already_have < 2
		end

		def to_s
			"Customer"
		end
	end
end
