Source: js/database/RecipeStore.js

import { IDBService } from './IDBService.js';
import { deleteMetadata, upsertMetadata } from './localStorageService.js';

/** Class representing the store interface for recipes in IndexedDB */
export class RecipeStore {
    /**
     * Create a RecipeStore object.
     * @constructor
     */
    constructor() {
        this.idbService = new IDBService('recipeStore');
    }

    /**
     * Adds a recipe to app's IndexedDB database.
     * @param {Object} data - Recipe data (similar to Google's recipe schema).
     * @returns {Promise<number>} - A Promise that resolves to new recipe's ID number.
     */
    async addRecipe(data) {
        try {
            const id = await this.idbService.set(data);

            // Send data information to localStorage
            const updatedData = { ...data, id };
            this.syncToLocalStorage(updatedData, 'insert');

            return id;
        } catch (error) {
            console.error(`Failed to add recipe to storage: ${error}`);
            throw error;
        }
    }

    /**
     * Retrieves a recipe from the app's IndexedDB database.
     * @param {number} id - A number corresponding to the target recipe's id.
     * @returns {Promise<Object>} - A Promise that resolves to the recipe Object.
     */
    async getRecipe(id) {
        try {
            const recipe = await this.idbService.get(id);
            return recipe;
        } catch (error) {
            console.error(
                `Failed to get recipe (ID #${id}) from storage: ${error}`
            );
            throw error;
        }
    }

    /**
     * Retrieves a recipe's image from the app's IndexedDB database.
     * @param {number} id - A number corresponding to the target recipe's id.
     * @returns {Promise<string>} - A Promise that resolves to an Object URL (via `URL.createObjectURL`) for use in <img> tags.
     */
    async getRecipeImageURL(id) {
        try {
            const recipe = await this.idbService.get(id);

            if (!recipe || !recipe.image) {
                throw new Error(`Recipe or recipe image not found (ID #${id})`);
            }

            return recipe.image;
        } catch (error) {
            // console.error(
            //     `Failed to get recipe image (ID #${id}) from storage: ${error}`
            // );

            console.error(`[IMAGE ERR]: ${error.message}`);

            // throw error;
        }
    }

    /**
     * Retrieves all recipes from the app's IndexedDB database.
     * @returns {Promise<Object[]>} - A promise that resolves to an array of recipe Objects.
     */
    async getAllRecipes() {
        try {
            const recipes = await this.idbService.getAll();
            return recipes;
        } catch (error) {
            console.error(`Failed to retrieve all recipes: ${error}`);
            throw error;
        }
    }

    /**
     * Updates an existing recipe entry in the app's IndexedDB database.
     * @param {number} id - A number corresponding to the target recipe's id.
     * @param {Object} data - New recipe data to replace the old entry's data (similar to Google's recipe schema).
     * @returns {Promise<number>} - A Promise that resolves to new recipe's ID number.
     */
    async updateRecipe(id, data) {
        try {
            const doesIdExist = await this.idbService.get(id);
            if (!doesIdExist) {
                throw new Error(`Recipe with ID #${id} not found`);
            }

            const updatedData = { ...data, id };
            const updatedId = await this.idbService.set(updatedData);
            this.syncToLocalStorage(updatedData, 'update');

            return updatedId;
        } catch (error) {
            console.error(`Failed to update recipe: ${error}`);
            throw error;
        }
    }

    /**
     * Removes an existing recipe entry from the app's IndexedDB database.
     * @param {Object} data - An object of the target recipe
     */
    async deleteRecipe(data) {
        try {
            await this.idbService.delete(data.id);

            this.syncToLocalStorage(data, 'delete');
        } catch (error) {
            console.error(`Failed to delete recipe: ${error}`);
            throw error;
        }
    }

    /**
     * Synchronizes localStorage and IndexedDB to hold the equivalent recipe info.
     * @param {Object} recipe - The full recipe object to sync.
     * @param {string} mode - The sync mode: 'insert', 'update', or 'delete'.
     * @private
     */
    async syncToLocalStorage(recipe, mode) {
        if (mode === 'delete') {
            console.log('recipe ID: ', recipe.id);
            deleteMetadata(recipe.id);
        } else if (mode === 'insert' || mode === 'update') {
            const metadata = {
                id: recipe.id,
                name: recipe.name || '',
                isFavorite: recipe.isFavorite || false,
                recipeCategory: recipe.recipeCategory || '',
                recipeCuisine: recipe.recipeCuisine || '',
                totalTime: recipe.totalTime || 0,
                recipeIngredient: recipe.recipeIngredient || [],
                calories: recipe.calories || 0,
            };

            console.log('METADATA = ', metadata);
            upsertMetadata(metadata);
        }
    }
}