# frozen_string_literal: true

require "bigdecimal/util"
require "securerandom"
require "time"
require "value_semantics/monkey_patched"

require_relative "../admin_action"
require_relative "../form_to_h"

class AdminAction
	class AddTransaction < AdminAction
		class Command
			using FormToH

			def self.for(target_customer, reply:)
				time = DateTime.now.iso8601
				EMPromise.resolve(
					new(
						customer_id: target_customer.customer_id,
						created_at: time, settled_after: time
					)
				).then { |x|
					reply.call(x.form).then(&x.method(:create))
				}
			end

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

			def form
				FormTemplate.render("admin_add_transaction")
			end

			def create(result)
				hash = result.form.to_h
					.reject { |_k, v| v == "nil" }.transform_keys(&:to_sym)
				hash[:transaction_id] = hash[:transaction_id]
					.sub("%", SecureRandom.uuid)

				AdminAction::AddTransaction.for(
					**@bag,
					**hash
				)
			end
		end

		TransactionExists = Struct.new(:transaction_id) do
			def to_s
				"The transaction #{transaction_id} already exists"
			end
		end

		TransactionDoesNotExist = Struct.new(:transaction_id) do
			def to_s
				"The transaction #{transaction_id} doesn't exist"
			end
		end

		def customer_id
			@attributes[:customer_id]
		end

		def amount
			@attributes[:amount].to_d
		end

		def transaction_id
			@attributes[:transaction_id]
		end

		def created_at
			@attributes[:created_at]
		end

		def settled_after
			@attributes[:settled_after]
		end

		def note
			@attributes[:note]
		end

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

		def transaction
			@transaction ||= Transaction.new(
				**@attributes.slice(:customer_id, :transaction_id, :amount, :note),
				created_at: created_at, settled_after: settled_after,
				bonus_eligible?: bonus_eligible?
			)
		end

		def check_forward
			EMPromise.resolve(nil)
				.then { check_noop }
				.then { transaction.exists? }
				.then { |e|
					EMPromise.reject(TransactionExists.new(transaction_id)) if e
				}
		end

		def check_reverse
			EMPromise.resolve(nil)
				.then { check_noop }
				.then { transaction.exists? }
				.then { |e|
					EMPromise.reject(TransactionDoesNotExist.new(transaction_id)) unless e
				}
		end

		def to_s
			"add_transaction(#{customer_id}): #{note} (#{transaction_id}) "\
			"#{transaction}"
		end

		def forward
			transaction.insert.then {
				self
			}
		end

		def reverse
			transaction.delete.then {
				self
			}
		end

	protected

		def check_noop
			EMPromise.reject(NoOp.new) if amount.zero?
		end
	end
end
