# frozen_string_literal: true

require "bigdecimal"
require "forwardable"
require "relative_time"
require "value_semantics/monkey_patched"

require_relative "call_attempt_repo"
require_relative "customer"
require_relative "customer_plan"
require_relative "form_template"
require_relative "promise_hash"
require_relative "proxied_jid"
require_relative "subaccount"

class PlanInfo
	extend Forwardable

	def_delegators :customer, :expires_at, :auto_top_up_amount

	def self.for(customer)
		return EMPromise.resolve(NoPlan.new) unless customer&.plan_name

		PromiseHash.all(
			customer: customer,
			start_date: customer.activation_date,
			calling_charges_this_month: customer.calling_charges_this_month,
			billing_customer_id: customer.billing_customer_id
		).then(method(:new))
	end

	class NoPlan
		def template
			FormTemplate.new("")
		end

		def admin_template
			FormTemplate.new("")
		end

		def status
			"Transitional"
		end
	end

	value_semantics do
		method_missing :customer, Customer
		start_date Time
		calling_charges_this_month BigDecimal
		billing_customer_id String
	end

	def template
		FormTemplate.for("plan_info", plan_info: self)
	end

	def admin_template
		FormTemplate.for("admin_plan_info", plan_info: self)
	end

	def monthly_price
		"$%.4f / month" % customer.monthly_price
	end

	def relative_start_date
		RelativeTime.in_words(start_date)
	end

	def formatted_start_date
		start_date.strftime("%Y-%m-%d %H:%M:%S")
	end

	def currency
		(customer.currency || "No Currency").to_s
	end

	def status
		customer.status.to_s.capitalize
	end

	def remaining_included_calling_credit
		[customer.minute_limit.to_d - calling_charges_this_month, 0].max
	end
end

class CustomerInfo
	value_semantics do
		plan_info Either(PlanInfo, PlanInfo::NoPlan)
		tel Either(String, nil)
		balance BigDecimal
		cnam Either(String, nil)
	end

	def self.for(customer)
		PromiseHash.all(
			plan_info: PlanInfo.for(customer),
			tel: customer.registered? ? customer.registered?.phone : nil,
			balance: customer.balance,
			cnam: customer.tndetails.dig(:features, :lidb, :subscriber_information)
		).then(&method(:new))
	end

	def form
		FormTemplate.render("customer_info", info: self)
	end
end

class AdminInfo
	value_semantics do
		jid ProxiedJID, coerce: ProxiedJID.method(:new)
		customer_id String
		fwd Either(CustomerFwd, nil)
		info CustomerInfo
		call_info String
		trust_level String
		backend BackendSgx
		subaccounts ArrayOf(::Subaccount), default: []
	end

	def self.for(
		customer,
		trust_level_repo: TrustLevelRepo.new,
		call_attempt_repo: CallAttemptRepo.new,
		backend_repo: TrivialBackendSgxRepo.new
	)
		PromiseHash.all(
			jid: customer.jid, customer_id: customer.customer_id,
			fwd: customer.fwd,
			info: CustomerInfo.for(customer),
			call_info: call_info(customer, call_attempt_repo),
			trust_level: trust_level_repo.find(customer).then(&:to_s),
			backend: backend_repo.get(customer.customer_id),
			subaccounts: Subaccount.get_subaccounts(customer.billing_customer_id)
		).then(&method(:new))
	end

	def backend_jid
		backend.from_jid.to_s
	end

	def route
		backend.jid
	end

	class FakeLowBalance
		def self.for(_)
			self
		end

		def self.notify!
			EMPromise.resolve(0)
		end
	end

	def self.call_info(customer, call_attempt_repo)
		if customer.registered?
			call_attempt_repo
				.find_outbound(customer, "+1", call_id: "dry_run")
				.then(&:to_s)
		else
			EMPromise.resolve("No calling")
		end
	end

	def form
		FormTemplate.render("admin_info", admin_info: self)
	end

	def tel_link
		[
			"https://dashboard.bandwidth.com/portal/r/a",
			CONFIG[:creds][:account],
			"numbers/details",
			info.tel.gsub(/\A\+1/, "")
		].join("/")
	end

	def support_link
		CONFIG[:support_link].call(backend_jid)
	end
end
