# frozen_string_literal: true

require_relative "./oob"

class Registration
	def self.for(iq, customer, web_register_manager)
		EMPromise.resolve(customer&.registered?).then do |registered|
			if registered
				Registered.new(iq, registered.phone)
			else
				web_register_manager.choose_tel(iq).then do |(riq, tel)|
					Activation.for(riq, customer, tel)
				end
			end
		end
	end

	class Registered
		def initialize(iq, tel)
			@reply = iq.reply
			@reply.status = :completed
			@tel = tel
		end

		def write
			@reply.note_type = :error
			@reply.note_text = <<~NOTE
				You are already registered with JMP number #{@tel}
			NOTE
			BLATHER << @reply
			nil
		end
	end

	class Activation
		def self.for(iq, customer, tel)
			if customer&.active?
				Finish.new(iq, customer, tel)
			elsif customer
				EMPromise.resolve(new(iq, customer, tel))
			else
				# Create customer_id
				raise "TODO"
			end
		end

		def initialize(iq, customer, tel)
			@reply = iq.reply
			reply.allowed_actions = [:next]

			@customer = customer
			@tel = tel
		end

		attr_reader :reply, :customer, :tel

		FORM_FIELDS = [
			{
				var: "activation_method",
				type: "list-single",
				label: "Activate using",
				required: true,
				options: [
					{
						value: "bitcoin",
						label: "Bitcoin"
					},
					{
						value: "credit_card",
						label: "Credit Card ($#{CONFIG[:activation_amount]})"
					},
					{
						value: "code",
						label: "Referral or Activation Code"
					}
				]
			},
			{
				var: "plan_name",
				type: "list-single",
				label: "What currency should your account balance be in?",
				required: true,
				options: [
					{
						value: "cad_beta_unlimited-v20210223",
						label: "Canadian Dollars"
					},
					{
						value: "usd_beta_unlimited-v20210223",
						label: "United States Dollars"
					}
				]
			}
		].freeze

		def write
			form = reply.form
			form.type = :form
			form.title = "Activate JMP"
			form.instructions = "Going to activate #{tel} (TODO RATE CTR)"
			form.fields = FORM_FIELDS

			COMMAND_MANAGER.write(reply).then { |iq|
				Payment.for(iq, customer, tel)
			}.then(&:write)
		end
	end

	module Payment
		def self.kinds
			@kinds ||= {}
		end

		def self.for(iq, customer, tel)
			plan_name = iq.form.field("plan_name").value.to_s
			customer = customer.with_plan(plan_name)
			kinds.fetch(iq.form.field("activation_method")&.value&.to_s&.to_sym) {
				raise "Invalid activation method"
			}.call(iq, customer, tel)
		end

		class Bitcoin
			Payment.kinds[:bitcoin] = method(:new)

			def initialize(iq, customer, tel)
				@reply = iq.reply
				reply.note_type = :info
				reply.status = :completed

				@customer = customer
				@customer_id = customer.customer_id
				@tel = tel
				@addr = ELECTRUM.createnewaddress
			end

			attr_reader :reply, :customer_id, :tel

			def save
				EMPromise.all([
					REDIS.mset(
						"pending_tel_for-#{customer_id}", tel,
						"pending_plan_for-#{customer_id}", @customer.plan_name
					),
					@addr.then do |addr|
						REDIS.sadd("jmp_customer_btc_addresses-#{customer_id}", addr)
					end
				])
			end

			def note_text(amount, addr)
				<<~NOTE
					Activate your account by sending at least #{'%.6f' % amount} BTC to
					#{addr}

					You will receive a notification when your payment is complete.
				NOTE
			end

			def write
				EMPromise.all([
					@addr,
					save,
					BTC_SELL_PRICES.public_send(@customer.currency.to_s.downcase)
				]).then do |(addr, _, rate)|
					min = CONFIG[:activation_amount] / rate
					reply.note_text = note_text(min, addr)
					BLATHER << reply
					nil
				end
			end
		end

		class CreditCard
			Payment.kinds[:credit_card] = ->(*args) { self.for(*args) }

			def self.for(iq, customer, tel)
				customer.payment_methods.then do |payment_methods|
					if (method = payment_methods.default_payment_method)
						Activate.new(iq, customer, method, tel)
					else
						new(iq, customer, tel)
					end
				end
			end

			def initialize(iq, customer, tel)
				@customer = customer
				@tel = tel

				@reply = iq.reply
				@reply.allowed_actions = [:next]
				@reply.note_type = :info
				@reply.note_text = "#{oob.desc}: #{oob.url}"
			end

			attr_reader :reply

			def oob
				oob = OOB.find_or_create(@reply.command)
				oob.url = CONFIG[:credit_card_url].call(
					@reply.to.stripped.to_s,
					@customer.customer_id
				)
				oob.desc = "Add credit card, then return here and choose next"
				oob
			end

			def write
				COMMAND_MANAGER.write(@reply).then do |riq|
					CreditCard.for(riq, @customer, @tel)
				end
			end

			class Activate
				def initialize(iq, customer, payment_method, tel)
					@iq = iq
					@customer = customer
					@payment_method = payment_method
					@tel = tel
				end

				def write
					Transaction.sale(
						@customer,
						CONFIG[:activation_amount],
						@payment_method
					).then(
						method(:sold),
						->(_) { declined }
					)
				end

			protected

				def sold(tx)
					tx.insert.then {
						@customer.bill_plan
					}.then do
						Finish.new(@iq, @customer, @tel).write
					end
				end

				DECLINE_MESSAGE =
					"Your bank declined the transaction. " \
					"Often this happens when a person's credit card " \
					"is a US card that does not support international " \
					"transactions, as JMP is not based in the USA, though " \
					"we do support transactions in USD.\n\n" \
					"If you were trying a prepaid card, you may wish to use "\
					"Privacy.com instead, as they do support international " \
					"transactions.\n\n " \
					"You may add another card and then choose next"

				def decline_oob(reply)
					oob = OOB.find_or_create(reply.command)
					oob.url = CONFIG[:credit_card_url].call(
						reply.to.stripped.to_s,
						@customer.customer_id
					)
					oob.desc = DECLINE_MESSAGE
					oob
				end

				def declined
					reply = @iq.reply
					reply_oob = decline_oob(reply)
					reply.allowed_actions = [:next]
					reply.note_type = :error
					reply.note_text = "#{reply_oob.desc}: #{reply_oob.url}"
					COMMAND_MANAGER.write(reply).then do |riq|
						CreditCard.for(riq, @customer, @tel)
					end
				end
			end
		end
	end

	class Finish
		def initialize(iq, customer, tel)
			@reply = iq.reply
			@reply.status = :completed
			@reply.note_type = :info
			@reply.note_text = "Your JMP account has been activated as #{tel}"
			@customer = customer
			@tel = tel
		end

		def write
			BandwidthTNOrder.create(@tel).then(&:poll).then(
				->(_) { @customer.register!(@tel).then { BLATHER << @reply } },
				lambda do |_|
					@reply.note_type = :error
					@reply.note_text =
						"The JMP number #{@tel} is no longer available, " \
						"please visit https://jmp.chat and choose another."
					BLATHER << @reply
				end
			)
		end
	end
end
