/**
 * This file is part of the Meeds project (https://meeds.io/).
 *
 * Copyright (C) 2020 - 2025 Meeds Association contact@meeds.io
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */
package org.exoplatform.groovyscript.text;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;

import org.exoplatform.management.annotations.Impact;
import org.exoplatform.management.annotations.ImpactType;
import org.exoplatform.management.annotations.Managed;
import org.exoplatform.management.annotations.ManagedDescription;
import org.exoplatform.management.annotations.ManagedName;
import org.exoplatform.management.jmx.annotations.NameTemplate;
import org.exoplatform.management.jmx.annotations.Property;
import org.exoplatform.management.rest.annotations.RESTEndpoint;

/**
 * Created by The eXo Platform SAS Author : tam.nguyen tamndrok@gmail.com Mar 17, 2009
 */

@Managed
@ManagedDescription("Template statistic service")
@NameTemplate({ @Property(key = "view", value = "portal"), @Property(key = "service", value = "statistic"),
        @Property(key = "type", value = "template") })
@RESTEndpoint(path = "templatestatistics")
public class TemplateStatisticService {

    final Map<String, TemplateStatistic> apps = new ConcurrentHashMap<String, TemplateStatistic>();

    private final String ASC = "ASC";

    private final String DESC = "DESC";

    public TemplateStatisticService() {
    }

    public TemplateStatistic findTemplateStatistic(String name) {
        if (name == null || name.length() == 0) {
            throw new IllegalArgumentException("Parameter 'templateid' is required.");
        } else {
            TemplateStatistic result = apps.get(name);
            if (result == null) {
                /* Try to prevent a potential XSS */
                String safeName = name.replaceAll("[^a-zA-Z0-9_\\-\\./]+", "");
                throw new IllegalArgumentException("There is no such template with templateid '"+ safeName +"'.");
            } else {
                return result;
            }
        }
    }

    /*
     * get TemplateStatistic by name, if TemplateStatistic isn't exits, create a new one.
     */
    public TemplateStatistic getTemplateStatistic(String name) {
        TemplateStatistic app = apps.get(name);
        if (app == null) {
            app = new TemplateStatistic(name);
            apps.put(name, app);
        }
        return app;
    }

    /*
     * returns a list of templateId sorted alphabetically
     */
    @Managed
    @ManagedDescription("The list of template identifiers sorted alphabetically")
    public String[] getTemplateList() {
        List<Map.Entry<String, TemplateStatistic>> list = new LinkedList<Map.Entry<String, TemplateStatistic>>(apps.entrySet());
        String[] app = new String[list.size()];
        int index = 0;
        for (Iterator<Entry<String, TemplateStatistic>> it = list.iterator(); it.hasNext();) {
            Map.Entry<String, TemplateStatistic> entry = it.next();
            app[index] = entry.getKey();
            index++;
        }
        return app;
    }

    /*
     * return max time of an specify template
     */
    @Managed
    @ManagedDescription("The maximum rendering time of a specified template in seconds")
    @Impact(ImpactType.READ)
    public double getMaxTime(@ManagedDescription("The template id") @ManagedName("templateId") String name) {
        TemplateStatistic app = findTemplateStatistic(name);
        return toSeconds(app.getMaxTime());
    }

    /*
     * return min time of an specify template
     */
    @Managed
    @ManagedDescription("The minimum rendering time of a specified template in seconds")
    @Impact(ImpactType.READ)
    public double getMinTime(@ManagedDescription("The template id") @ManagedName("templateId") String name) {
        TemplateStatistic app = findTemplateStatistic(name);
        return toSeconds(app.getMinTime());
    }

    /*
     * return count of an specify template
     */
    @Managed
    @ManagedDescription("The rendering count of a specified template")
    @Impact(ImpactType.READ)
    public long getExecutionCount(@ManagedDescription("The template id") @ManagedName("templateId") String name) {
        TemplateStatistic app = findTemplateStatistic(name);
        return app.executionCount();
    }

    /*
     * return average time of an specify template
     */
    @Managed
    @ManagedDescription("The average rendering time of a specified template in seconds")
    @Impact(ImpactType.READ)
    public double getAverageTime(@ManagedDescription("The template id") @ManagedName("templateId") String name) {
        TemplateStatistic app = findTemplateStatistic(name);
        return toSeconds(app.getAverageTime());
    }

    /*
     * returns 10 slowest template
     */
    @Managed
    @ManagedDescription("The list of the 10 slowest templates")
    public String[] getSlowestTemplates() {

        Map<String, Double> application = new HashMap<String, Double>();
        for (Map.Entry<String, TemplateStatistic> entry : apps.entrySet()) {
            String url = entry.getKey();
            application.put(url, getAverageTime(url));
        }

        return sort(application, DESC);
    }

    /*
     * returns 10 slowest template
     */
    @Managed
    @ManagedDescription("The list of the 10 most executed templates")
    public String[] getMostExecutedTemplates() {

        Map<String, Long> application = new HashMap<String, Long>();
        for (Map.Entry<String, TemplateStatistic> entry : apps.entrySet()) {
            String url = entry.getKey();
            application.put(url, getExecutionCount(url));
        }

        return sort(application, DESC);
    }

    /*
     * returns 10 fastest template
     */
    @Managed
    @ManagedDescription("The list of the 10 fastest templates")
    public String[] getFastestTemplates() {

        Map<String, Double> application = new HashMap<String, Double>();
        for (Map.Entry<String, TemplateStatistic> entry : apps.entrySet()) {
            String url = entry.getKey();
            application.put(url, getAverageTime(url));
        }

        return sort(application, ASC);
    }

    private <T extends Comparable<T>> String[] sort(Map<String, T> source, final String order) {
        String[] app = new String[10];
        List<Map.Entry<String, T>> list = new ArrayList<Map.Entry<String, T>>(source.entrySet());
        Collections.sort(list, new Comparator<Map.Entry<String, T>>() {
            public int compare(Map.Entry<String, T> o1, Map.Entry<String, T> o2) {
                T value1 = o1.getValue();
                T value2 = o2.getValue();
                if (DESC.equals(order)) {
                    T tmp = value1;
                    value1 = value2;
                    value2 = tmp;
                }
                return value1.compareTo(value2);
            }
        });
        int index = 0;
        for (Map.Entry<String, T> entry : list) {
            app[index] = entry.getKey();
            index++;
            if (index >= app.length) {
                break;
            }
        }
        return app;
    }

    private double toSeconds(double value) {
        return value == -1 ? -1 : value / 1000D;
    }
}
