package dev.fyloz.trial.colorrecipesexplorer.web.response;

import dev.fyloz.trial.colorrecipesexplorer.config.Preferences;
import dev.fyloz.trial.colorrecipesexplorer.utils.ControllerUtils;
import org.springframework.lang.Nullable;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public abstract class ResponseBuilder<T extends ResponseBuilder, R> {

    protected static final String RESPONSE_PREFIX = "response.";
    protected static final String ERROR_ATTRIBUTE_NAME = "error";
    protected static final String SUCCESS_ATTRIBUTE_NAME = "success";

    protected Map<String, Object> attributes;

    private boolean containsErrors = false;

    public ResponseBuilder() {
        attributes = new HashMap<>();

        // Ajoute l'URL de base à toutes les réponses
        attributes.put("baseUrl", ControllerUtils.getCurrentBaseUrl());
        attributes.put("referer", ControllerUtils.getLatestUrl());
    }

    protected abstract void addResponseCodeToAttribute(String responseCodeType, String responseMessagePath, String[] parameters);

    protected abstract R build();

    public T addResponseCode(ResponseCode responseCode, Object... parameters) {
        String[] parametersAsStr = new String[parameters.length];

        for (int i = 0; i < parameters.length; i++) {
            parametersAsStr[i] = parameters[i].toString();
        }

        return addResponseCode(responseCode, parametersAsStr);
    }

    public T addResponseCode(ResponseCode responseCode, String... parameters) {
        int requiredParametersNumber = responseCode.getParametersNumber();
        int givenParametersNumber = parameters.length;

        if (requiredParametersNumber != givenParametersNumber) {
            throw new IllegalArgumentException(String.format("Mauvais nombre de paramètre dans une réponse de modèle: %s requis, %s fournis", requiredParametersNumber, givenParametersNumber));
        }

        // Ajoute les paramètres, si nécessaire
        if (requiredParametersNumber > 0) {
            // Avertit s'il y a plus de paramètres que le nombre de paramètres supporté par le template thymeleaf (aussi MAX_PARAMETERS_NUMBER)
            int maxParametersNumber = ResponseCode.MAX_PARAMETERS_NUMBER;
            if (givenParametersNumber > maxParametersNumber) {
                Preferences.logger.warn(String.format("Trop de paramètres fournis pour le code de réponse %s: %s maximum, %s fournis", responseCode.name(), maxParametersNumber, givenParametersNumber));
            }
        }

        String responseCodeType;
        if (responseCode.getType() == ResponseCode.ResponseCodeType.ERROR) {
            containsErrors = true;
            responseCodeType = ERROR_ATTRIBUTE_NAME;
        } else {
            responseCodeType = SUCCESS_ATTRIBUTE_NAME;
        }

        String responseMessagePath = RESPONSE_PREFIX + responseCode.getCode();
        addResponseCodeToAttribute(responseCodeType, responseMessagePath, parameters);

        return (T) this;
    }

    public T addResponseData(ResponseDataType responseDataType, Object data) {
        Class requiredDataTypeClass = responseDataType.getDataType();
        Class requiredListDataTypeClass = responseDataType.getListDataType();
        Class givenDataTypeClass = data.getClass();

        // Vérifie le type de l'objet
        if (!requiredDataTypeClass.equals(givenDataTypeClass)) {
            throw new IllegalArgumentException(String.format("L'objet passé en paramètre n'est pas du bon type. Requis: %s, fournis: %s", requiredDataTypeClass.getName(), givenDataTypeClass.getName()));
        }

        // Si l'objet est une liste, vérifie qu'elle n'est pas vide et qu'elle est du bon type
        if (requiredDataTypeClass.equals(List.class) && requiredListDataTypeClass != null) {
            List listData = (List) data;

            if (listData.size() < 1) {
                throw new IllegalArgumentException("La liste passée en paramètre ne contient aucun élément");
            }

            Class givenListDataTypeClass = listData.get(0).getClass();
            if (givenDataTypeClass.equals(requiredListDataTypeClass)) {
                throw new IllegalArgumentException(String.format("La liste passée en paramètre contient des éléments du mauvais type. Requis: %s, fournis: %s", requiredListDataTypeClass.getName(), givenListDataTypeClass.getName()));
            }
        }

        // Ajoute l'attribut dans le Map
        addAttribute(responseDataType.getDataTypeName(), data);

        return (T) this;
    }

    public T addAttribute(String attributeName, Object content) {
        attributes.put(attributeName, content);

        return (T) this;
    }

    public boolean containsErrors() {
        return containsErrors;
    }
}
