
module RefTool
  class Operation
    module GuardInterface
      def self.included(base_class)
        base_class.extend(ClassMethods)
      end

      def all_guards_pass?
        self.class.guard_list.each do |guard|
          return false unless send(guard)
        end
        true
      end

      module ClassMethods
        def guard_for(error_code, guard = nil, &guard_block)
          raise ArgumentError, <<~ERROR unless guard.nil? || guard_block.nil?
            Can only provide a class/lambda or a block but not both.
          ERROR
          guard = guard_block if guard.nil? && !guard_block.nil?
          guard = guard_method_name_for(error_code) if guard.nil?

          method_name = case guard
                        when Symbol, String
                          define_method_guard(error_code, guard)
                        else
                          define_object_guard(error_code, guard)
                        end

          guard_list.push(method_name)
          method_name
        end

        def guard_list
          @guard_list ||= []
        end

        def guard_method_name_for(error_code)
          "not_#{error_code}?".to_sym
        end

        def define_method_guard(error_code, guard_method_name)
          wrap_method_name = "wrap_#{guard_method_name}".to_sym
          define_method(wrap_method_name) do
            send(guard_method_name, error_code)
          end
          wrap_method_name
        end

        def define_object_guard(error_code, guard_class)
          method_name = guard_method_name_for(error_code)

          define_method(method_name) do |*args|
            guard = guard_class.respond_to?(:call) ? guard_class.call : guard_class.new
            guard.send(:setup, self, error_code)
            guard.continue?
          end

          method_name
        end
      end
    end
  end
end
