# frozen_string_literal: true

class AdminActionRepo
	class NotFound < StandardError; end

	def initialize(redis: REDIS)
		@redis = redis
	end

	def build(klass:, direction:, **kwargs)
		dir = AdminAction::Direction.for(direction)
		dir.new(AdminAction.const_get(klass).new(**kwargs))
	end

	# I'm using hash subset test for pred
	# So if you give me any keys I'll find only things where those keys are
	# present and set to that value
	def find(limit, max="+", **pred)
		return EMPromise.resolve([]) unless limit.positive?

		xrevrange(
			"admin_actions", max: max, min: "-", count: limit
		).then { |new_max, results|
			next [] if results.empty?

			selected = results.select { |_id, values| pred < values }
				.map { |id, values| build(id: id, **rename_class(values)) }

			find(limit - selected.length, "(#{new_max}", **pred)
				.then { |r| selected + r }
		}
	end

	def create(action)
		push_to_redis(**action.to_h).then { |id|
			action.with(id: id)
		}
	end

protected

	def rename_class(hash)
		hash.transform_keys { |k| k == :class ? :klass : k }
	end

	# Turn value into a hash, paper over redis version issue, return earliest ID
	def xrevrange(stream, min:, max:, count:)
		min = next_id(min[1..-1]) if min.start_with?("(")
		max = previous_id(max[1..-1]) if max.start_with?("(")

		@redis.xrevrange(stream, max, min, "COUNT", count).then { |result|
			next ["+", []] if result.empty?

			[
				result.last.first, # Reverse order, so this is the lowest ID
				result.map { |id, values| [id, Hash[*values].transform_keys(&:to_sym)] }
			]
		}
	end

	# Versions of REDIS after 6.2 can just do "(#{current_id}" to make an
	# exclusive version
	def previous_id(current_id)
		time, seq = current_id.split("-")
		if seq == "0"
			"#{time.to_i - 1}-18446744073709551615"
		else
			"#{time}-#{seq.to_i - 1}"
		end
	end

	# Versions of REDIS after 6.2 can just do "(#{current_id}" to make an
	# exclusive version
	def next_id(current_id)
		time, seq = current_id.split("-")
		if seq == "18446744073709551615"
			"#{time.to_i + 1}-0"
		else
			"#{time}-#{seq.to_i + 1}"
		end
	end

	def push_to_redis(**kwargs)
		@redis.xadd("admin_actions", "*", *kwargs.flatten)
	end
end
