package mi.hdm.recipes;

import mi.hdm.exceptions.InvalidRecipeException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

public class Recipe implements RecipeComponent {
    //TODO: Add filepath for image
    private static final Logger log = LogManager.getLogger(Recipe.class);

    private Map<RecipeComponent, Integer> ingredients;
    private String name;
    private String description;
    private List<String> preparation;
    private List<Category> categories;
    private int preparationTimeMins;
    private NutritionTable nutritionTable;
    private final LocalDateTime creationTime;

    /**
     * This constructor will create a new recipe and calculate the nutrition values for the recipe by its ingredients.
     *
     * @param name                Name for this recipe
     * @param ingredients         Map of ingredients used in this recipe. The value for each component should be the amount that is needed of this ingredient (in its respective unit=
     * @param description         Description for this recipe
     * @param preparation         List of preparation steps for this recipe
     * @param categories          Categories that this recipe belongs to
     * @param preparationTimeMins Time that it takes to prepare this recipe in minutes
     */
    public Recipe(
            String name,
            Map<RecipeComponent, Integer> ingredients,
            String description,
            List<String> preparation,
            List<Category> categories,
            int preparationTimeMins) {

        //Das ruft den anderen Konstruktor dieser Klasse auf (siehe unten)
        this(name, ingredients, description, preparation, categories, preparationTimeMins, NutritionCalculator.calculateNutritionTable(ingredients));
    }

    /**
     * This constructor will create a new recipe and set the nutrition table provided by the user instead of calculating the nutrition values from the ingredients.
     *
     * @param name                Name for this recipe
     * @param ingredients         Map of ingredients used in this recipe. The value for each component should be the amount that is needed of this ingredient (in its respective unit=
     * @param description         Description for this recipe
     * @param preparation         List of preparation steps for this recipe
     * @param categories          Categories that this recipe belongs to
     * @param preparationTimeMins Time that it takes to prepare this recipe in minutes
     * @param nutritionTable      The nutrition table that will be set for this recipe
     */
    public Recipe(
            String name,
            Map<RecipeComponent, Integer> ingredients,
            String description,
            List<String> preparation,
            List<Category> categories,
            int preparationTimeMins,
            NutritionTable nutritionTable) {

        setIngredients(ingredients);
        setDescription(description);
        setPreparation(preparation);
        setNutritionTable(nutritionTable);
        setName(name);
        setPreparationTimeMins(preparationTimeMins);
        setCategories(categories);

        this.creationTime = LocalDateTime.now();
    }

    public void setName(String name) {
        if (name == null || name.isEmpty()) {
            throw new InvalidRecipeException("Name can not be null or empty");
        }
        this.name = name;
        log.info("Name set successfully.");
    }

    public String getName() {
        return name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
        log.info("Description set successfully.");
    }

    public List<String> getPreparation() {
        return preparation;
    }

    public void setPreparation(List<String> preparation) {
        if (preparation == null || preparation.isEmpty()) {
            throw new InvalidRecipeException("Preparation can not be null or empty");
        }
        this.preparation = preparation;
        log.info("Preparation set successfully.");
    }

    public List<Category> getCategories() {
        return categories;
    }

    public void setCategories(List<Category> categories) {
        if (categories == null) {
            categories = new ArrayList<>();
        }

        this.categories = categories;
        log.info("Categories set successfully.");
    }

    public void addCategory(Category category) {
        categories.add(category);
        log.info("Category {} added successfully.", category.getName());
    }

    public int getPreparationTimeMins() {
        return preparationTimeMins;
    }

    public void setPreparationTimeMins(int preparationTimeMins) {
        if (preparationTimeMins < 0) {
            throw new InvalidRecipeException("PreparationTime must be a positive value");
        }
        this.preparationTimeMins = preparationTimeMins;
    }

    public LocalDateTime getCreationTime() {
        return creationTime;
    }

    public void setNutritionTable(NutritionTable nutritionTable) {
        if (nutritionTable == null) {
            throw new InvalidRecipeException("Nutrition Table can not be null");
        }
        this.nutritionTable = nutritionTable;
        log.info("NutritionTable set successfully.");
    }

    public NutritionTable getNutritionTable() {
        return nutritionTable;
    }

    @Override
    public Measurement getMeasurement() {
        return Measurement.PIECE;
    }

    public Map<RecipeComponent, Integer> getIngredients() {
        return ingredients;
    }

    public void setIngredients(Map<RecipeComponent, Integer> ingredients) {
        if (ingredients == null || ingredients.size() == 0) {
            throw new InvalidRecipeException("Ingredient list can not be null or empty");
        }
        this.ingredients = ingredients;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o instanceof Recipe recipe)
            return preparationTimeMins == recipe.preparationTimeMins && Objects.equals(ingredients, recipe.ingredients) && Objects.equals(name, recipe.name) && Objects.equals(description, recipe.description) && Objects.equals(preparation, recipe.preparation) && Objects.equals(categories, recipe.categories) && Objects.equals(nutritionTable, recipe.nutritionTable) && Objects.equals(creationTime, recipe.creationTime);
        return false;
    }

    @Override
    public String toString() {
        String desc = description == null ? "No description" : description;
        return String.format("Recipe: %s%n--------%n%s%n%nCreated: %s%n", name, desc, creationTime);
    }
}