import axios from 'axios';
import { mutate } from 'swr';
import { getCookie } from '../helpers/misc-helpers';
import AppConfigStore from '../stores/AppConfigStore';

/**
 * Classe abstraite pour les classes effectuant les opérations de chargement et mise à jour des données via l'API REST.
 *
 * @abstract
 */
class AbstractDataManager {
    static NOTATION_ENDPOINT = 'notations';

    /**
     * Constructeur, rafraichit le header CSRF de la requête avec la valeur du token CSRF dans les cookies
     *
     * @constructor
     * @param {?AppConfigStore} appConfig
     */
    constructor(appConfig) {
        /** @type {?AppConfigStore} appConfig */
        this.appConfig = appConfig;
        axios.defaults.headers.common['X-CSRFToken'] = getCookie('csrftoken');
    }

    /**
     * Construit l'URL pour une requête de récupération de données (GET)
     * @param {string} endPointName - Nom du endpoint de l'API à requêter
     * @param {?import('../jsdoc/types').ParamsManager} [params] - paramètres d'appel
     * @returns {string} URL générée
     */
    buildFetchURL(endPointName, params) {
        // Adaptation des paramètres
        params = this.adaptGetRequestParameters(params);
        //Construction de la querystring de la requête
        let querystring = Object.keys(params)
            .map((key) => key + '=' + params[key])
            .join('&');
        querystring = querystring.length ? '?' + querystring : '';
        //let querystring = Object.keys(params).map(key => encodeURIComponent(key) + '=' + encodeURIComponent(params[key])).join('&');
        return '/api/' + endPointName + '/' + querystring;
    }

    /**
     * Adapte les paramètres de requête GET.
     *
     * @param {Object} params - Paramètres de requête à adapter
     * @returns {Object} paramètres adaptés
     */
    adaptGetRequestParameters(params = {}) {
        let adaptedParams = { ...params };

        // Taille de la page
        this.renameObjectProperty(adaptedParams, 'pageSize', 'page_size');

        // Page courante
        //this.renameObjectProperty(adaptedParams, 'page', 'page');

        return adaptedParams;
    }

    /**
     * Construit l'URL pour une requête de mise à jour de données (PATCH)
     * @param {sring} endPointName - Nom du endpoint de l'API à requêter
     * @param {sring} dataId - ID de l'administré à update
     * @returns {string} URL générée
     */
    buildPatchURL(endPointName, dataId, params) {
        //let querystring = Object.keys(params).map(key => encodeURIComponent(key) + '=' + encodeURIComponent(params[key])).join('&');
        params = this.adaptGetRequestParameters(params);
        //Construction de la querystring de la requête
        let querystring = Object.keys(params)
            .map((key) => key + '=' + params[key])
            .join('&');
        querystring = querystring.length ? '?' + querystring : '';
        return '/api/' + endPointName + '/' + dataId + '/' + querystring;
    }

    /**
     * Construit l'URL pour une requête de récupération de données (GET)
     * @param {string} endPointName - Nom du endpoint de l'API à requêter
     * @param {Object} params - Paramètres de requête
     * @returns {string} URL générée
     */
    buildPutURL(endPointName) {
        return this.buildFetchURL(endPointName);
    }

    /**
     * Renomme une propriété d'un objet, si elle existe.
     *
     * @param {*} obj - L'objet
     * @param {*} oldPropName - Ancien nom de propriété
     * @param {*} newPropName - Nouveau nom de propriété
     */
    renameObjectProperty(obj, oldPropName, newPropName) {
        if (oldPropName !== newPropName) {
            const descriptor = Object.getOwnPropertyDescriptor(obj, oldPropName);
            if (descriptor) {
                Object.defineProperty(obj, newPropName, Object.getOwnPropertyDescriptor(obj, oldPropName));
                delete obj[oldPropName];
            }
        }
    }

    /**
     * Met à jour le cache sans revalidation puis déclenche une revalidation pour obtenir le statut
     *
     * @param {string} key - clé de cache
     * @param {*} newData

     */
    updateCache(key, newData) {
        mutate(key, newData, false);
    }
}

export default AbstractDataManager;
