import json


def _GetBotWithFewestNamedCaches(bots):
  """Selects the bot that has the fewest named caches.

  To break ties, the bot with the most available disk space is selected.

  Args:
    bots(list): A list of bot dicts as returned by the swarming.bots.list api
      with a minimum length of 1.

  Returns:
    One bot from the list.
  """
  # This list will contain a triplet (cache_count, -free_space, bot) for each
  # bot.
  candidates = []
  for b in bots:
    try:
      caches_dimension = [
          d['value'] for d in b['dimensions'] if d['key'] == 'caches'
      ][0]
      # We only care about caches whose name starts with 'builder_' as that is
      # the convention that we use in GetCacheName.
      cache_count = len(
          [cache for cache in caches_dimension if cache.startswith('builder_')])
      bot_state = json.loads(b['state'])
      free_space = sum(
          [disk['free_mb'] for _, disk in bot_state['disks'].iteritems()])
    except (KeyError, TypeError, ValueError):
      # If we can't determine the values, we add the bot to the end of the list.
      candidates.append((1000, 0, b))
    else:
      # We use negative free space in this triplet so that a single sort will
      # put the one with the most free space first if there is a tie in cache
      # count with a single sort.
      candidates.append((cache_count, -free_space, b))
  return sorted(candidates)[0][2]