# frozen_string_literal: true

require "value_semantics/monkey_patched"
require_relative "../admin_action"
require_relative "../form_to_h"

class AdminAction
	class NumberChange < AdminAction
		include Isomorphic
		class Command
			using FormToH

			def self.for(target_customer, reply:)
				EMPromise.resolve(
					new(
						customer_id: target_customer.customer_id,
						old_tel: target_customer.registered?.phone
					)
				).then { |x|
					reply.call(x.form).then(&x.method(:change))
				}
			end

			def initialize(**bag)
				@bag = bag
			end

			def form
				FormTemplate.render("admin_number_change")
			end

			def change(result)
				AdminAction::NumberChange.for(
					**@bag,
					**result.form.to_h
						.reject { |_k, v| v == "nil" }.transform_keys(&:to_sym)
				)
			end
		end

		NilKey = Struct.new(:key) {
			def to_s
				"Expected a key with a value, but #{key} has no value."
			end
		}

		def customer_id
			@attributes[:customer_id]
		end

		def old_tel
			@attributes[:old_tel]
		end

		def new_tel
			@attributes[:new_tel]
		end

		def should_delete?
			["1", "true"].include?(@attributes[:should_delete])
		end

		def check_forward
			EMPromise.all([
				check_noop,
				check_exist
			])
		end

		def forward
			TrivialBackendSgxRepo.new(redis: REDIS).get(customer_id).then do |sgx|
				EMPromise.all([
					REDIS.rename("catapult_fwd-#{old_tel}", "catapult_fwd-#{new_tel}"),
					sgx.register!(new_tel),
					should_delete? && first_time? ? disconnect_number : nil
				]).then { self }
			end
		end

		def to_reverse
			with(
				old_tel: new_tel,
				new_tel: old_tel
			)
		end

		def to_s
			"number_change(#{customer_id}): #{old_tel} -> #{new_tel}#{delete_warning}"
		end

	protected

		def disconnect_number
			# Order name is limited to 40 characters
			# Assuming 12 chars for new_tel and 12 for customer_id, this is tight
			# but ok
			BandwidthTnRepo.new.disconnect(
				old_tel,
				"cust #{customer_id} swap to #{new_tel}"
			)
		end

		def delete_warning
			return "" unless should_delete? && !first_time?

			" * NOT DELETING"
		end

		def check_noop
			EMPromise.reject(NoOp.new) if new_tel == old_tel
		end

		def check_exist
			cat_jid = "catapult_jid-#{old_tel}"
			REDIS.exists(cat_jid).then { |v|
				EMPromise.reject(NilKey.new(cat_jid)) unless v == 1
			}
		end
	end
end
