package fyloz.trial.ColorRecipesExplorer.services;

import fyloz.trial.ColorRecipesExplorer.core.io.FileHandler;
import fyloz.trial.ColorRecipesExplorer.dao.RecipeDao;
import fyloz.trial.ColorRecipesExplorer.model.*;
import fyloz.trial.ColorRecipesExplorer.web.StringBank;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.MultiValueMap;

import java.io.File;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class RecipeService extends GenericService<Recipe> {

    private RecipeDao recipeDao;
    private CompanyService companyService;
    private StepService stepService;
    private MixService mixService;

    @Autowired
    public RecipeService(RecipeDao recipeDao, CompanyService companyService, StepService stepService, MixService mixService) {
        super(recipeDao);
        this.recipeDao = recipeDao;
        this.companyService = companyService;
        this.stepService = stepService;
        this.mixService = mixService;
    }

    /**
     * Récupère la liste des recettes associées à une compagnie.
     *
     * @param company La compagnie.
     * @return La liste des recettes associées à cette compagnie.
     */
    public List<Recipe> getByCompany(Company company) {
        return recipeDao.findAllByCompany(company);
    }

    /**
     * Crée et assigne des étapes à une recette, en se basant sur l'entrée de utilisateur.
     *
     * @param recipe La recette à assigner les étapes
     * @param input  L'entrée de l'utilisateur, contenant les étapes (key = "step_*")
     * @return La recette avec les étapes.
     */
    public Recipe createAndSetSteps(Recipe recipe, MultiValueMap<String, Object> input) {
        List<RecipeStep> steps = new ArrayList<>();
        for (String key : input.keySet()) {
            Object field = input.get(key);
            if (key.startsWith("step_") && field instanceof LinkedList) {
                steps.add(new RecipeStep(recipe, ((LinkedList) field).get(0).toString()));
            }
        }

        List<RecipeStep> recipeSteps = recipe.getRecipeSteps();
        if (recipeSteps != null) {
            stepService.deleteAll(recipeSteps);
        }
        recipe.setRecipeSteps(steps);
        save(recipe);

        return recipe;
    }

    /**
     * Supprime complètement une recette ainsi que les éléments qui y sont attachés de la base de données.
     *
     * @param recipe La recette à supprimer
     */
    public void deleteRecipe(Recipe recipe) {
        List<Mix> mixes = recipe.getRecipeMixes();
        mixService.deleteAll(mixes);

        stepService.deleteAll(recipe.getRecipeSteps());
        delete(recipe);

        FileHandler fileHandler;
        for (String image : getImageFiles(recipe)) {
            fileHandler = new FileHandler(image.replace(".jpeg", ""), FileHandler.FileContext.IMAGE, FileHandler.FileExtension.JPEG);
            fileHandler.deleteFile();
        }
    }

    /**
     * Récupère une liste triée des mélanges pour une recette.
     *
     * @param recipe La recette dont on veut récupérer les mélanges
     * @return Une liste triée des mélanges.
     */
    public List<Mix> getSortedMixes(Recipe recipe) {
        List<Mix> mixes = recipe.getRecipeMixes();
        mixes.sort(Comparator.comparing(Mix::getMixID));
        return mixes;
    }

    /**
     * Récupère toutes les recettes dans la base de données et les classes par compagnie.
     *
     * @return Un Map contenant les recettes classées par compagnie.
     */
    public Map<Company, List<Recipe>> getRecipesByCompany() {
        List<Company> companies = companyService.getAll();
        List<Recipe> recipes = getAll();

        Map<Company, List<Recipe>> mappedRecipes = new HashMap<>();

        for (Company company : companies) {
            mappedRecipes.put(company, recipes.stream().filter(r -> r.getCompany().equals(company)).collect(Collectors.toList()));
        }

        return mappedRecipes;
    }

    /**
     * Récupère le nom des images liées à une recette.
     *
     * @param recipe La recette dont on veut récupérer les images
     * @return Une liste contenant le nom des images liées à la recette.
     */
    public List<String> getImageFiles(Recipe recipe) {
        int recipeID = recipe.getRecipeID();
        String recipeCode = recipe.getRecipeCode();
        String fileName = String.format("%s_%s", recipeID, recipeCode);

        File imageLocation = new File(StringBank.IMAGES_LOCATION);
        File[] result = imageLocation.listFiles((d, n) -> n.startsWith(fileName) && n.endsWith("jpeg"));

        if (result == null) return null;

        return Arrays.stream(result).map(File::getName).collect(Collectors.toList());
    }

    /**
     * Récupère les types de mélanges associés à la recette.
     *
     * @param recipe La recette dont on veut récupérer les types de mélange
     * @return Une liste contenant les types de mélanges.
     */
    public List<MixType> getAssociatedMixesTypes(Recipe recipe) {
        return recipe.getRecipeMixes().stream().map(Mix::getMixType).collect(Collectors.toList());
    }
}
