text: /**
   * @namespace Utilities
   */
  import {firebase} from './firebase';
  
  /**
  * Label and its corresponding genre.
  * @typedef Label - The label shown to the user
  * @type {Object.<string, string>} 
  * @property {string} genre - The genre associated with this label
  */
  
  /**
  * Genre and its associated label
  * @typedef Genre - The genre of the book
  * @type {Object.<string, string>} 
  * @property {string} label - The label associated with this genre
  */
  
  /**
   * Dictionary of labels and their associated genres
   * @constant
   * @type {Label}
   * @memberof Utilities
   * @default
   */
  export const labels2Genre = {
    "Action and Adventure": "action",
    "Biographies": "biographies",
    "Classics": "classics",
    "Fantasy": "fantasy", 
    "Sci-Fi": "scifi", 
    "Suspense and Thrillers": "thrillers", 
    "Cookbooks": "cookbooks", 
    "Poetry": "poetry",
  };
  
  
  /**
   * Dictionary of genres and their associated labels
   * @constant
   * @type {Genre}
   * @memberof Utilities
   * @default
   */
  export const genre2Labels = {
    "action": "Action and Adventure",
    "biographies": "Biographies",
    "classics": "Classics",
    "fantasy": "Fantasy", 
    "scifi": "Sci-Fi", 
    "thrillers": "Suspense and Thrillers", 
    "cookbooks": "Cookbooks", 
    "poetry": "Poetry",
  };
  
  /**
   * This function shuffles an array
   *
   * @function shuffle
   * @param {Array} array - The array to be shuffled
   * @memberof Utilities
   */
  const shuffle = (array) => {
    return array.sort(() => Math.random() - 0.5);
  }
  
  /**
   * This function searches for a given book using the Google Books API and returns a book object.
   *
   * @function searchBookOnGoogle
   * @param {Object} bookToSearch - The book to search for
   * @param {string} bookToSearch.title - The target book title
   * @param {string} bookToSearch.author - The target book author
   * @param {boolean} verbose - Whether to log the found book
   * @returns {Object} The first result from the Google Books API
   * @memberof Utilities
  */
  export const searchBookOnGoogle = async (bookToSearch, verbose = false) => {
    let response = await fetch(`https://www.googleapis.com/books/v1/volumes?q=${bookToSearch.title}${bookToSearch.author ? ('+inauthor:' + bookToSearch.author) : ''}`, { method: 'GET' });
    let json = await response.json();
    if (!json.items) return;
    let bookInfo = json.items[0].volumeInfo;
    let book = {
      title: bookInfo.title,
      key: bookInfo.title,
      author: bookInfo.authors?.length > 0 ? bookInfo.authors[0] : null,
      description: bookInfo.description,
      publisher: bookInfo.publisher,
      year: bookInfo.publishedDate,
      isbn: bookInfo.industryIdentifiers.find(el => el.type && el.type.startsWith('ISBN').identifier),
      coverUrl: bookInfo.imageLinks?.thumbnail
    };
  
    if (verbose) console.log(book);
    return book;
  }
  
  /**
   * This function gets the user's preferred genres from Firestore.
   *
   * @function getGenres
   * @returns {string[]} A list of genres.
   * @memberof Utilities
   */
  export const getGenres = async () => {
    const uid = firebase.auth().currentUser.uid;
    const usersDoc = firebase.firestore().collection('users').doc(uid);
    const doc = await usersDoc.get();
    if (!doc.exists) {
      console.log('User does not exist, something\'s wrong');
      return;
    }
    const genres = doc.data().genres;
    return genres;
  }
  
  /**
   * This function gets books for given genres
   *
   * @function getBooksFromGenres
   * @param {Array} genres - The genres for which books are to be got
   * @param {number} [numOfBooks=1] - The number of books to return 
   * @memberof Utilities
   */
  export const getBooksFromGenres = async (genres, numOfBooks = 1) => {
    const ret = await genres.reduce(async (acc, genre) => {
      return [...(await acc), ...(await getBooksFromGenre(genre, numOfBooks))];
    }, []);
    return ret;
  }
  
  /**
   * This function gets books for a given genre
   *
   * @function getBooksFromGenres
   * @param {string} genre - The genre for which books are to be got
   * @param {number} [numOfBooks=1] - The number of books to return 
   * @memberof Utilities
   */
  export const getBooksFromGenre = async (genre, numOfBooks = 1) => {
    const booksRef = firebase.firestore().collection('books');
    const books = await booksRef.doc(genre).get();
    const random = shuffle(Object.values(books.data()));
    const selectedBooks = random.length > numOfBooks ? random.slice(0, numOfBooks) : random;
    return await Promise.all(selectedBooks.map(async book => await searchBookOnGoogle(book, true)));
  }
  
  /**
   * This function retrieves a user's reading list from Firestore.
   *
   * @function getReadingBooks
   * @returns {Object[]} The user's reading list.
   * @memberof Utilities
   */
  export const getReadingBooks = async () => {
    const uid = firebase.auth().currentUser.uid;
    const usersDoc = firebase.firestore().collection('users').doc(uid);
    const doc = await usersDoc.get();
    if (!doc.exists) {
      console.log('User does not exist, something\'s wrong');
      return;
    }
    const reading = doc.data().reading;
    return reading;
  }
  
  /**
   * This function adds a book to a user's reading list on Firestore.
   *
   * @function addToReading
   * @param {Object} book - The book to add.
   * @memberof Utilities
   */
  export const addToReading = async (book) => {
    const uid = firebase.auth().currentUser.uid;
    const usersDoc = firebase.firestore().collection('users').doc(uid);
    const doc = await usersDoc.get();
    if (!doc.exists) {
      console.log('User does not exist, something\'s wrong');
      return;
    }
    const reading = doc.data().reading;
    if (reading?.find(b => b && b.title == book.title) != undefined)
      return;
    console.log('user reading', reading);
    for (let key of Object.keys(book))
      book[key] == undefined && delete book[key];
    await usersDoc.update({
      reading: [
        ...(reading || []), book
      ]
    });
  }
  
  /**
   * This function removes a book from a user's reading list on Firestore.
   *
   * @function removeFromReading
   * @param {Object} book - The book to remove.
   * @memberof Utilities
   */
  export const removeFromReading = async (book) => {
    const uid = firebase.auth().currentUser.uid;
    const usersDoc = firebase.firestore().collection('users').doc(uid);
    const doc = await usersDoc.get();
    if (!doc.exists) {
      console.log('User does not exist, something\'s wrong');
      return;
    }
    const reading = doc.data().reading;
    console.log(reading);
    const bookIdx = reading?.findIndex(b => b && b.title == book.title);
    console.log(bookIdx);
    if (bookIdx < 0)
      return;
    reading.splice(bookIdx, 1);
    
    await usersDoc.update({ reading: reading });
  }
  
  /**
   * This function adds a book to a user's preferred books on Firestore.
   *
   * @function addToPrefs
   * @param {Object} book - The book to add.
   * @memberof Utilities
   */
  export const addToPrefs = async (book) => {
    const uid = firebase.auth().currentUser.uid;
    const usersDoc = firebase.firestore().collection('users').doc(uid);
    const doc = await usersDoc.get();
    if (!doc.exists) {
      console.log('User does not exist, something\'s wrong');
      return;
    }
    const prefs = doc.data().prefs;
    if (prefs?.find(b => b && b.title == book.title) != undefined)
      return;
    console.log('user prefs', prefs);
    for (let key of Object.keys(book))
      book[key] == undefined && delete book[key];
    await usersDoc.update({
      prefs: [
        ...(prefs || []), {
          title: book.title,
          author: book.author
        }
      ]
    });
  }
  