import {getProducts} from './productAPI';
import {performCancellableRequest} from './abortableRequest';

/**
 * Structure of a BasketItem object.
 *
 * [
 *   {
 *      productId: string,
 *      price: number,
 *      amount: number;
 *      productName: string
 *   }
 *   ...
 * ]
 */

/**
 * Generate test items in the basket, in case there are problems with the products page.
 *
 * @returns {Promise<BasketItem[]>} An array of BasketItem objects, wrapped in a Promise.
 */
const generateTestBasket = async () => {
    const products = await getProducts();
    return products.slice(0, 4).map(p => ({productId: p.id, price: p.price, amount: 1, productName: p.name}));
}

let basket;

/**
 * Update the basket with a new array of BasketItem objects.
 * The existing basket is replaced completely.
 *
 * @param newBasket An array of BasketItems objects.
 */
export const updateBasket = (newBasket) => {
    basket = newBasket;
    localStorage.basket = JSON.stringify(newBasket);
}

/**
 * Retrieve all the items in the basket asynchronously.
 *
 * @param abortController An AbortController that can be used to perform cleanup and cancel the asynchronous request.
 * @returns {Promise<BasketItem[]>} An array of BasketItems, wrapped in a Promise,
 * the promise WON'T resolve when it is cancelled. This means the then() callback won't ever be executed.
 */
export const getBasket = async (abortController = undefined) => {
    if (!basket) {
        const storage = localStorage.basket;
        basket = storage ? JSON.parse(storage) : await generateTestBasket();
    }
    return performCancellableRequest(() => [...basket], abortController);
}

/**
 * Add a new product to the basket, this works for new items and adding another copy of
 * the same item, i.e. incrementing the amount.
 *
 * @param productId The id of the product that is to be added to the basket.
 * @param productName The name of the product that is to be added to the basket.
 * @param price The price of the product that is to be added to the basket.
 * @returns {Promise<BasketItem[]>} The updated basket wrapped in a Promise.
 */
export const addProductToBasket = async (productId, productName, price) => {
    const b = await getBasket();
    const index = b.findIndex(i => i.productId === productId);

    if (index === -1) {
        b.push({productId, amount: 1, price, productName})
    } else {
        const tmp = b[index];
        b[index] = {...tmp, amount: tmp.amount + 1};
    }
    updateBasket(b);
    return b;
}

/**
 * Remove a product from the basket, the amount of times the product is included in the basket
 * is decremented by 1. If the amount reaches 0, the product is removed.
 *
 * @param productId The id of the product for which the amount of items must be decremented.
 * @returns {Promise<BasketItem[]>} The updated basket wrapped in a Promise.
 */
export const removeProductFromBasket = async (productId) => {
    const basket = await getBasket();
    const newBasket = [];

    for (const b of basket) {
        if (b.productId === productId) {
            if (b.amount !== 1) {
                newBasket.push({...b, amount: b.amount - 1});
            }
        } else {
            newBasket.push(b);
        }
    }
    updateBasket(newBasket);
    return newBasket;
}
