package fyloz.trial.ColorRecipesExplorer.web.controller;

import fyloz.trial.ColorRecipesExplorer.model.Material;
import fyloz.trial.ColorRecipesExplorer.model.Mix;
import fyloz.trial.ColorRecipesExplorer.model.MixQuantity;
import fyloz.trial.ColorRecipesExplorer.services.InventoryService;
import fyloz.trial.ColorRecipesExplorer.services.MaterialService;
import fyloz.trial.ColorRecipesExplorer.services.MaterialTypeService;
import fyloz.trial.ColorRecipesExplorer.services.MixService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static fyloz.trial.ColorRecipesExplorer.web.PagesPaths.INVENTORY;
import static fyloz.trial.ColorRecipesExplorer.web.PagesPaths.USE_INVENTORY;
import static fyloz.trial.ColorRecipesExplorer.web.StringBank.*;

@Controller
public class InventoryController {

    private InventoryService inventoryService;
    private MaterialService materialService;
    private MixService mixService;
    private MaterialTypeService materialTypeService;

    @Autowired
    public InventoryController(InventoryService inventoryService, MaterialService materialService, MixService mixService, MaterialTypeService materialTypeService) {
        this.inventoryService = inventoryService;
        this.materialService = materialService;
        this.mixService = mixService;
        this.materialTypeService = materialTypeService;
    }

    /**
     * Affiche la page de l'inventaire.
     * <p>
     * Modèle de la page:
     * - materials: Contient la liste de tous les matériaux
     *
     * @param model Le Model injecté par Thymeleaf
     * @return La page à afficher.
     */
    @GetMapping(INVENTORY)
    public String getInventory(Model model) {
        model.addAttribute(MATERIALS, materialService.getAll().stream().filter(m -> !m.isMixType()).collect(Collectors.toList()));
        model.addAttribute(MATERIAL_TYPES, materialTypeService.getAll());

        return INVENTORY;
    }

    /**
     * Déduit les quantités utilisées de chaque matériaux présents dans le corps de la requête (JSON) de l'inventaire.
     * Le JSON dans le corps de la requête doit prendre cette forme:
     * {
     * "mixID 1": {
     * "materialID 1": "quantité 1",
     * "materialID 2": "quantité 2",
     * "materialID 3": "quantité 3"
     * },
     * "mixID 2": {
     * "materialID 1": "quantité 1"
     * },
     * ..
     * }
     * S'il y a une erreur, l'opération est annulée et aucun changement n'est fait dans l'inventaire.
     * <p>
     * Réponse de la méthode:
     * - error: Contient le message d'erreur, s'il y a lieu
     * - reason: Contient la raison de l'erreur (le champ fautif)
     * - success: Contient le message à affiche lors du succès de la méthode
     *
     * @param form Le JSON contenant les quantités à utiliser.
     * @return Une réponse sous forme de Map (-> Json).
     */
    @PostMapping(value = USE_INVENTORY, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    @Transactional
    public Map<String, String> consumeMaterials(@RequestBody Map<String, Object> form) {
        Map<String, String> response = new HashMap<>();

        List<Mix> mixes = new ArrayList<>();
        Map<Mix, Map<Material, Float>> quantities = new HashMap<>();

        for (String mixIDStr : form.keySet()) {
            int mixID = Integer.parseInt(mixIDStr);

            Mix mix = mixService.getByID(mixID);
            if (mix == null) {
                response.put(RESPONSE_ERROR, String.format(MIX_NOT_FOUND, mixID));
                return response;
            }

            mixes.add(mix);

            Map<String, String> formMaterials = (Map<String, String>) form.get(mixIDStr);
            Map<Material, Float> mixQuantities = new HashMap<>();

            for (Material material : mix.getMixQuantities().stream().map(MixQuantity::getMaterial).collect(Collectors.toList())) {
                String materialIDAsString = String.valueOf(material.getMaterialID());

                if (formMaterials.containsKey(materialIDAsString)) {
                    mixQuantities.put(material, Float.parseFloat(formMaterials.get(materialIDAsString)));
                }
            }

            quantities.put(mix, mixQuantities);
        }

        for (Mix mix : mixes) {
            String errorCode = inventoryService.checkQuantities(mix, quantities.get(mix));
            if (errorCode != null) {
                response.put(RESPONSE_ERROR, String.format(NOT_ENOUGH_MATERIAL, materialService.getByID(Integer.parseInt(errorCode.split("-")[1])).getMaterialCode()));
                response.put(RESPONSE_REASON, errorCode);
                return response;
            }
        }

        for (Mix mix : mixes) {
            if (!inventoryService.useMix(mix, quantities.get(mix))) {
                response.put(RESPONSE_ERROR, ERROR_SAVING);
                return response;
            }
        }

        response.put(RESPONSE_SUCCESS, SUCCESS_USING_MATERIALS);
        return response;
    }
}
