# frozen_string_literal: true

require_relative "admin_action_repo"
require_relative "admin_actions/add_invites"
require_relative "admin_actions/add_transaction"
require_relative "admin_actions/cancel"
require_relative "admin_actions/financial"
require_relative "admin_actions/reset_declines"
require_relative "admin_actions/set_trust_level"
require_relative "admin_actions/number_change"
require_relative "admin_actions/launch_snikket"
require_relative "bill_plan_command"
require_relative "customer_info_form"
require_relative "financial_info"
require_relative "form_template"

class AdminCommand
	def self.for(
		target_customer,
		customer_repo,
		admin_action_repo=AdminActionRepo.new,
		snikket_repo=Snikket::Repo.new
	)
		if target_customer
			new(target_customer, customer_repo, admin_action_repo, snikket_repo)
		else
			NoUser.new(customer_repo, admin_action_repo, notice: "Customer Not Found")
		end
	end

	class NoUser < AdminCommand
		def initialize(
			customer_repo,
			admin_action_repo=AdminActionRepo.new,
			snikket_repo=Snikket::Repo.new,
			notice: nil
		)
			@customer_repo = customer_repo
			@admin_action_repo = admin_action_repo
			@snikket_repo = snikket_repo
			@notice = notice
		end

		def start(command_action=:execute)
			return Command.finish(@notice || "Done") if command_action == :complete

			reply(
				FormTemplate.render("customer_picker", notice: @notice)
			).then { |response|
				new_context(response.form.field("q").value, response.action)
			}
		end
	end

	def initialize(
		target_customer,
		customer_repo,
		admin_action_repo=AdminActionRepo.new,
		snikket_repo=Snikket::Repo.new
	)
		@target_customer = target_customer
		@customer_repo = customer_repo
		@admin_action_repo = admin_action_repo
		@snikket_repo = snikket_repo
	end

	def start(command_action=:execute)
		AdminInfo.for(@target_customer).then { |info|
			if command_action == :complete
				Command.finish { |iq| iq.command << info.form }
			else
				reply(info.form)
			end
		}.then { |response| menu_or_done(response.action) }
	end

	def reply(form)
		Command.reply { |reply|
			reply.allowed_actions = [:next, :complete]
			reply.command << form
		}
	end

	def menu_or_done(command_action=:execute, notice: nil)
		return Command.finish("Done") if command_action == :complete

		reply(FormTemplate.render("admin_menu", notice: notice)).then do |response|
			if response.form.field("action")
				handle(response.form.field("action").value, response.action)
			end
		end
	end

	def handle(action, command_action)
		if respond_to?("action_#{action}")
			send("action_#{action}").then do |notice|
				menu_or_done(command_action, notice: notice)
			end
		else
			new_context(action)
		end
	end

	def new_context(q, command_action=:execute)
		CustomerInfoForm.new(@customer_repo)
			.parse_something(q).then do |new_customer|
				AdminCommand.for(
					new_customer,
					@customer_repo,
					@admin_action_repo,
					@snikket_repo
				).then { |ac| ac.start(command_action) }
			end
	end

	def action_info
		# Refresh the data
		new_context(@target_customer.customer_id)
	end

	def action_bill_plan
		BillPlanCommand.for(@target_customer).call
	end

	class Undoable
		def initialize(klass)
			@klass = klass
		end

		def call(customer, admin_action_repo:, **)
			@klass.for(customer, reply: method(:reply)).then { |action|
				Command.customer.then { |actor|
					action.with(actor_id: actor.customer_id).perform.then do |performed|
						admin_action_repo.create(performed)
					end
				}
			}.then { |action| "Action #{action.id}: #{action}" }
		end

		def reply(form=nil, note_type: nil, note_text: nil)
			Command.reply { |reply|
				reply.allowed_actions = [:next, :complete]
				reply.command << form if form
				reply.note_type = note_type if note_type
				reply.note_text = note_text if note_text
			}
		end
	end

	class Simple
		def initialize(klass)
			@klass = klass
		end

		def call(customer_id, customer_repo:, snikket_repo:, **)
			@klass.call(
				customer_id,
				reply: method(:reply),
				customer_repo: customer_repo,
				snikket_repo: snikket_repo
			).then { nil }
		end

		def reply(form=nil, note_type: nil, note_text: nil)
			Command.reply { |reply|
				reply.allowed_actions = [:next, :complete]
				reply.command << form if form
				reply.note_type = note_type if note_type
				reply.note_text = note_text if note_text
			}
		end
	end

	class Undo
		def self.for(target_customer, **)
			AdminActionRepo.new
				.find(1, customer_id: target_customer.customer_id)
				.then { |actions|
					raise "No actions found" if actions.empty?

					actions.first.undo
				}
		end
	end

	[
		[:cancel_account, Simple.new(AdminAction::CancelCustomer)],
		[:financial, Simple.new(AdminAction::Financial)],
		[:undo, Undoable.new(Undo)],
		[:reset_declines, Undoable.new(AdminAction::ResetDeclines::Command)],
		[:set_trust_level, Undoable.new(AdminAction::SetTrustLevel::Command)],
		[:add_invites, Undoable.new(AdminAction::AddInvites::Command)],
		[:number_change, Undoable.new(AdminAction::NumberChange::Command)],
		[:add_transaction, Undoable.new(AdminAction::AddTransaction::Command)],
		[:launch_snikket, Simple.new(AdminAction::LaunchSnikket)]
	].each do |action, handler|
		define_method("action_#{action}") do
			handler.call(
				@target_customer,
				admin_action_repo: @admin_action_repo,
				customer_repo: @customer_repo,
				snikket_repo: @snikket_repo
			)
		end
	end
end
