package org.exoplatform.cs.portlet.ticketCharts;

import juzu.*;
import juzu.plugin.jackson.Jackson;
import juzu.template.Template;
import org.exoplatform.commons.juzu.ajax.Ajax;
import org.exoplatform.cs.dao.LogDAO;
import org.exoplatform.cs.dao.TopicDAO;
import org.exoplatform.cs.dto.*;
import org.exoplatform.cs.entity.LogEntity;
import org.exoplatform.cs.entity.SpaceEntity;
import org.exoplatform.cs.portlet.common.CSControllerBase;
import org.exoplatform.cs.service.CSConstants;
import org.exoplatform.cs.service.CSSpaceService;
import org.exoplatform.cs.service.StatsService;
import org.exoplatform.cs.service.tickets.FlowState;
import org.exoplatform.cs.service.tickets.TicketStatusFlowService;
import org.exoplatform.cs.service.util.CSUtils;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import org.exoplatform.services.organization.User;
import org.json.JSONArray;
import org.json.JSONObject;

import javax.inject.Inject;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;


public class TicketChartsController extends CSControllerBase {

    private static Log LOG = ExoLogger.getLogger(TicketChartsController.class);
    private String bundleString;
    ResourceBundle bundle;
    private List<String> statusList = new ArrayList<String>();
    SimpleDateFormat byDayDateFormat = new SimpleDateFormat("yyyy.MM.dd");
    SimpleDateFormat byMonthDateFormat = new SimpleDateFormat("MMM yyyy");
    SimpleDateFormat byWeekDateFormat = new SimpleDateFormat("YYYY-'W'ww");

    @Inject
    TopicDAO topicDAO;


    @Inject
    TicketStatusFlowService ticketStatusFlowService;

    @Inject
    CSSpaceService spaceService;

    @Inject
    StatsService statsService;


    @Inject
    LogDAO logDao;


    @Inject
    @Path("index.gtmpl")
    Template indexTmpl;

    @View
    public Response.Content index() {
        return indexTmpl.ok();
    }


    @Ajax
    @Resource
    @MimeType.JSON
    @Jackson
    public Response getBundle(String locale) {
        return super.getBundle(new Locale(locale));
    }


    /**
     * this method returns the static list of ticket types
     *
     * @return
     */
    @Ajax
    @Resource(method = HttpMethod.GET)
    @MimeType.JSON
    @Jackson
    public Map<String, List<FlowState>> loadStatuses() {
        return ticketStatusFlowService.getAllStatuses();
    }


    @Ajax
    @juzu.Resource
    @MimeType.JSON
    @Jackson
    public Response countSpaceTicketsGroupdByOwner(String spaceId) {
        try {
            JSONArray TicketsNumber = new JSONArray();
            List<Object[]> results = topicDAO.countSpaceTicketsGroupdByOwner(spaceId);
            for (int i = 0; i < results.size(); i++) {
                Object[] arr = results.get(i);
                JSONObject data = new JSONObject();
                data.put("owner", ((Owner) arr[0]) != null ? ((Owner) arr[0]).name() : "Null");
                data.put("ticketNumber", (Long) arr[1]);
                TicketsNumber.put(data);
            }


            return Response.ok(TicketsNumber.toString());
        } catch (Throwable e) {
            LOG.error("error while getting context", e);
            return Response.status(500);
        }
    }

    @Ajax
    @juzu.Resource
    @MimeType.JSON
    @Jackson
    public Response countSpaceTicketsGroupdByAssignee(String spaceId) {
        try {
            JSONArray TicketsNumber = new JSONArray();
            List<Object[]> results = topicDAO.countSpaceTicketsGroupdByAssignee(spaceId);
            for (int i = 0; i < results.size(); i++) {
                Object[] arr = results.get(i);
                JSONObject data = new JSONObject();
                String assignee = ((String) arr[0]) != null ? ((String) arr[0]).toString() : "Not Assigned";
                data.put("assignee", assignee);
                data.put("ticketNumber", (Long) arr[1]);
                TicketsNumber.put(data);
            }


            return Response.ok(TicketsNumber.toString());
        } catch (Throwable e) {
            LOG.error("error while getting context", e);
            return Response.status(500);
        }
    }

    @Ajax
    @juzu.Resource
    @MimeType.JSON
    @Jackson
    public Response countSpaceTicketsGroupdByStatus(String spaceId) {
        try {
            JSONArray spaceTicketsNumberByStatus = new JSONArray();
            List<Object[]> results = topicDAO.countSpaceTicketsGroupdByStatus(spaceId);
            for (int i = 0; i < results.size(); i++) {
                Object[] arr = results.get(i);
                JSONObject data = new JSONObject();
                data.put("status", (((String) arr[0]).toString()));
                data.put("ticketNumber", (Long) arr[1]);
                spaceTicketsNumberByStatus.put(data);
            }


            return Response.ok(spaceTicketsNumberByStatus.toString());
        } catch (Throwable e) {
            LOG.error("error while getting context", e);
            return Response.status(500);
        }
    }


    @Ajax
    @juzu.Resource
    @MimeType.JSON
    @Jackson
    public Response getTicketsGroupdByByAssignee(String spaceId) {
        try {
            JSONArray TicketsNumber = new JSONArray();
            List<Object[]> results = topicDAO.countSpaceTicketsGroupdByAssignee(spaceId);
            for (int i = 0; i < results.size(); i++) {
                Object[] arr = results.get(i);
                JSONObject data = new JSONObject();
                String assignee = ((String) arr[0]) != null ? ((String) arr[0]).toString() : "Not Assigned";
                data.put("assignee", assignee);
                data.put("ticketNumber", (Long) arr[1]);
                TicketsNumber.put(data);
            }


            return Response.ok(TicketsNumber.toString());
        } catch (Throwable e) {
            LOG.error("error while getting context", e);
            return Response.status(500);
        }
    }


    @Ajax
    @juzu.Resource
    @MimeType.JSON
    @Jackson
    public Response getTicketsGroupdByAssignee(String spaceId, Long startDate, Long endDate, Boolean internal) {
        try {
            List<Object[]> results = new ArrayList<>();
            if (startDate == 0 || endDate == 0) {
                if (spaceId.equals("")) {
                    results = topicDAO.countCustomerTicketsGroupdByAssignee(internal);
                } else {
                    results = topicDAO.countSpaceTicketsGroupdByAssignee(spaceId);
                }
            } else {
                Calendar fromDate = Calendar.getInstance();
                fromDate.setTimeInMillis(startDate);
                Calendar toDate = Calendar.getInstance();
                toDate.setTimeInMillis(startDate);
                results = topicDAO.countCustomerTicketsGroupdByAssigneeAndDates(internal, fromDate, toDate);
            }
            JSONArray TicketsNumber = new JSONArray();
            for (int i = 0; i < results.size(); i++) {
                Object[] arr = results.get(i);
                JSONObject data = new JSONObject();
                String assignee = ((String) arr[0]) != null ? ((String) arr[0]).toString() : "Not Assigned";
                data.put("assignee", assignee);
                data.put("ticketNumber", (Long) arr[1]);
                TicketsNumber.put(data);
            }


            return Response.ok(TicketsNumber.toString());
        } catch (Throwable e) {
            LOG.error("error while getting context", e);
            return Response.status(500);
        }
    }


    @Ajax
    @juzu.Resource
    @MimeType.JSON
    @Jackson
    public Response getTicketsGroupdBySpace(Long startDate, Long endDate, Boolean internal) {
        try {
            JSONArray ticketsNumberBySpaceList = new JSONArray();
            List<Object[]> results = topicDAO.countCustomerTicketsGroupdBySpace(internal);
            for (int i = 0; i < results.size(); i++) {
                Object[] arr = results.get(i);
                JSONObject data = new JSONObject();
                SpaceDTO spaceDTO = spaceService.getSpace((((SpaceEntity) arr[0]).getGroupId()));
                data.put("space", spaceDTO.getName());
                data.put("spaceId", ((SpaceEntity) arr[0]).getGroupId());
                data.put("ticketNumber", (Long) arr[1]);
                ticketsNumberBySpaceList.put(data);
            }

            return Response.ok(ticketsNumberBySpaceList.toString());
        } catch (Throwable e) {
            LOG.error("error while getting context", e);
            return Response.status(500);
        }
    }


    @Ajax
    @juzu.Resource
    @MimeType.JSON
    @Jackson
    public Response getTicketsGroupdBySeverity(String spaceId, Long startDate, Long endDate, Boolean internal) {
        try {
            List<Object[]> results = new ArrayList<>();
            if (startDate == 0 || endDate == 0) {
                if (spaceId.equals("")) {
                    results = topicDAO.countCustomerTicketsGroupdBySeverity(internal);
                } else {
                    results = topicDAO.countSpaceTicketsGroupdBySeverity(spaceId);
                }
            } else {
                Calendar fromDate = Calendar.getInstance();
                fromDate.setTimeInMillis(startDate);
                Calendar toDate = Calendar.getInstance();
                toDate.setTimeInMillis(startDate);
                results = topicDAO.countCustomerTicketsGroupdBySeverityAndDates(internal, fromDate, toDate);
            }
            JSONArray TicketsNumber = new JSONArray();
            for (int i = 0; i < results.size(); i++) {
                Object[] arr = results.get(i);
                JSONObject data = new JSONObject();
                String severity = ((IssueSeverity) arr[0]) != null ? ((IssueSeverity) arr[0]).toString() : "None";
                data.put("severity", severity);
                data.put("ticketNumber", (Long) arr[1]);
                TicketsNumber.put(data);
            }


            return Response.ok(TicketsNumber.toString());
        } catch (Throwable e) {
            LOG.error("error while getting context", e);
            return Response.status(500);
        }
    }


    @Ajax
    @juzu.Resource
    @MimeType.JSON
    @Jackson
    public Response getTicketsGroupdByOwner(String spaceId, Long startDate, Long endDate, Boolean internal) {
        try {
            JSONArray ticketsNumberByOwnerList = new JSONArray();
            List<Object[]> results = new ArrayList<>();
            if (spaceId.equals("")) {
                results = topicDAO.countCustomerTicketsGroupdByOwner(internal);
            } else {
                results = topicDAO.countSpaceTicketsGroupdByOwner(spaceId);
            }
            for (int i = 0; i < results.size(); i++) {
                try {
                    Object[] arr = results.get(i);
                    JSONObject data = new JSONObject();
                    if (arr[0] != null) {
                        data.put("owner", (((Owner) arr[0]).name()));
                    } else {
                        data.put("owner", "null");
                    }
                    data.put("ticketNumber", (Long) arr[1]);
                    ticketsNumberByOwnerList.put(data);
                } catch (Exception e) {
                }
            }

            return Response.ok(ticketsNumberByOwnerList.toString());
        } catch (Throwable e) {
            LOG.error("error while getting context", e);
            return Response.status(500);
        }
    }


    @Ajax
    @juzu.Resource
    @MimeType.JSON
    @Jackson
    public Response getTicketsGroupdByStatus(String spaceId, Long startDate, Long endDate, Boolean internal) {
        try {
            JSONArray ticketsNumberByStatusList = new JSONArray();
            List<Object[]> results = new ArrayList<>();
            if (spaceId.equals("")) {
                results = topicDAO.countCustomerTicketsGroupdByStatus(internal);
            } else {
                results = topicDAO.countSpaceTicketsGroupdByStatus(spaceId);
            }
            for (int i = 0; i < results.size(); i++) {
                Object[] arr = results.get(i);
                JSONObject data = new JSONObject();
                data.put("status", (((String) arr[0]).toString()));
                data.put("ticketNumber", (Long) arr[1]);
                ticketsNumberByStatusList.put(data);
            }

            return Response.ok(ticketsNumberByStatusList.toString());
        } catch (Throwable e) {
            LOG.error("error while getting context", e);
            return Response.status(500);
        }
    }


    @Ajax
    @juzu.Resource
    @MimeType.JSON
    @Jackson
    public Response getTicketsGroupdByStatusAndDates(Long startDate, Long endDate, Boolean internal) {
        try {
            JSONArray ticketsNumberByStatusList = new JSONArray();
            Calendar cFrom = Calendar.getInstance();
            cFrom.setTimeInMillis(startDate);
            Calendar cTo = Calendar.getInstance();
            cTo.setTimeInMillis(endDate);

            List<Object[]> results = topicDAO.countCustomerTicketsGroupdByStatusAndDates(internal, cFrom, cTo);
            for (int i = 0; i < results.size(); i++) {
                Object[] arr = results.get(i);
                JSONObject data = new JSONObject();
                data.put("status", (((String) arr[0]).toString()));
                data.put("ticketNumber", (Long) arr[1]);
                ticketsNumberByStatusList.put(data);
            }

            return Response.ok(ticketsNumberByStatusList.toString());
        } catch (Throwable e) {
            LOG.error("error while getting context", e);
            return Response.status(500);
        }
    }


    @Ajax
    @juzu.Resource
    @MimeType.JSON
    @Jackson
    public Response getOpenVsClosedTickets(String spaceId, Long startDate, Long endDate, Boolean internal) {
        try {
            JSONArray ticketsNumberClosedVsOpenedList = new JSONArray();
            Long closed = null;
            Long opened = null;
            if (spaceId.equals("")) {
                closed = topicDAO.countClosedCustomerTickets(internal);
                opened = topicDAO.countOpenCustomerTickets(internal);
            } else {
                closed = topicDAO.countClosedSpaceTickets(spaceId);
                opened = topicDAO.countOpenSpaceTickets(spaceId);
            }


            JSONObject data = new JSONObject();
            data.put("status", "closed");
            data.put("ticketNumber", closed);
            ticketsNumberClosedVsOpenedList.put(data);
            data = new JSONObject();
            data.put("status", "open");
            data.put("ticketNumber", opened);
            ticketsNumberClosedVsOpenedList.put(data);
            return Response.ok(ticketsNumberClosedVsOpenedList.toString());
        } catch (Throwable e) {
            LOG.error("error while getting context", e);
            return Response.status(500);
        }
    }


    @Ajax
    @juzu.Resource
    @MimeType.JSON
    @Jackson
    public Response getOpenVsClosedTicketsByDates(Long startDate, Long endDate, Boolean internal) {
        try {
            JSONArray ticketsNumberClosedVsOpenedList = new JSONArray();
            Calendar cFrom = Calendar.getInstance();
            cFrom.setTimeInMillis(startDate);
            Calendar cTo = Calendar.getInstance();
            cTo.setTimeInMillis(endDate);

            Long closed = logDao.countLogsByTypeAndNewValueBetweenDates(cFrom, cTo, LogType.TICKET_STATUS_CHANGED, CSConstants.STATUS_CLOSED, internal).longValue();
            Long opened = logDao.countLogsNumberBetweenDates(cFrom, cTo, LogType.TICKET_CREATED, internal).longValue();

            JSONObject data = new JSONObject();
            data.put("status", "closed");
            data.put("ticketNumber", closed);
            ticketsNumberClosedVsOpenedList.put(data);
            data = new JSONObject();
            data.put("status", "opened");
            data.put("ticketNumber", opened);
            ticketsNumberClosedVsOpenedList.put(data);
            return Response.ok(ticketsNumberClosedVsOpenedList.toString());
        } catch (Throwable e) {
            LOG.error("error while getting context", e);
            return Response.status(500);
        }
    }


    @Ajax
    @juzu.Resource
    @MimeType.JSON
    @Jackson
    public Response getOpenVSClosedStatLog(String period, Long startDate, Long endDate, Boolean internal) {
        if (period.equals("weekly")) {
            return getWeeklyOpenVSClosedStatLog("", startDate, endDate, internal);
        } else {
            return getMonthlyOpenVSClosedStatLog("", startDate, endDate, internal);
        }
    }


    @Ajax
    @juzu.Resource
    @MimeType.JSON
    @Jackson
    public Response getSpaceOpenVSClosedStatLog(String spaceId, String period, Long startDate, Long endDate, Boolean internal) {
        if (period.equals("weekly")) {
            return getWeeklyOpenVSClosedStatLog(spaceId, startDate, endDate, internal);
        } else {
            return getMonthlyOpenVSClosedStatLog(spaceId, startDate, endDate, internal);
        }
    }

    @Ajax
    @juzu.Resource
    @MimeType.JSON
    @Jackson
    public Response getWeeklyOpenVSClosedStatLog(String spaceId, Long startDate, Long endDate, Boolean internal) {
        try {
            Calendar fromDate = Calendar.getInstance();
            fromDate.setTimeInMillis(startDate);
            if (startDate == 0) {
                LogEntity log_ = null;
                if (spaceId.equals("")) {
                    List<LogEntity> logList = logDao.getFirstLogByType(LogType.TICKET_CREATED);
                    if (logList.size() > 0) {
                        log_ = logList.get(0);
                    }
                } else {
                    List<LogEntity> logList = logDao.getFirstLogByTypeAndSpace(spaceId, LogType.TICKET_CREATED);
                    if (logList.size() > 0) {
                        log_ = logList.get(0);
                    }
                }
                if (log_ != null) {
                    fromDate.setTime(log_.getWhen().getTime());
                } else
                    return Response.status(204);
            }
            JSONArray statList = new JSONArray();
            fromDate.set(Calendar.HOUR_OF_DAY, 0);
            fromDate.set(Calendar.MINUTE, 0);
            fromDate.set(Calendar.SECOND, 0);
            Calendar toDate = Calendar.getInstance();
            if (endDate != 0) {
                toDate.setTimeInMillis(endDate);
            }
            int diff = Calendar.SATURDAY - fromDate.get(Calendar.DAY_OF_WEEK) + 2;
            Calendar to_ = Calendar.getInstance();
            to_.setTime(fromDate.getTime());
            to_.add(Calendar.DATE, diff);
            while (fromDate.before(toDate)) {
                JSONObject data = new JSONObject();

                if (spaceId.equals("")) {
                    data.put("statDate", (getDateByPeriod(fromDate.getTime(), "weekly")));
                    data.put("closedTicketNumber", logDao.countLogsByTypeAndNewValueBetweenDates(fromDate, to_, LogType.TICKET_STATUS_CHANGED, CSConstants.STATUS_CLOSED, internal).longValue());
                    data.put("openedTicketNumber", logDao.countLogsNumberBetweenDates(fromDate, to_, LogType.TICKET_CREATED, internal).longValue());
                } else {
                    data.put("statDate", (getDateByPeriod(fromDate.getTime(), "weekly")));
                    data.put("closedTicketNumber", logDao.countLogsByTypeAndNewValueAndSpaceBetweenDates(spaceId, fromDate, to_, LogType.TICKET_STATUS_CHANGED, CSConstants.STATUS_CLOSED).longValue());
                    data.put("openedTicketNumber", logDao.countLogsNumberBySpaceBetweenDates(spaceId, fromDate, to_, LogType.TICKET_CREATED).longValue());
                }
                statList.put(data);
                fromDate.setTime(to_.getTime());
                to_.add(Calendar.DATE, 7);
            }

            return Response.ok(statList.toString());
        } catch (Throwable e) {
            LOG.error("error while getting context", e);
            return Response.status(500);
        }
    }


    @Ajax
    @juzu.Resource
    @MimeType.JSON
    @Jackson
    public Response getMonthlyOpenVSClosedStatLog(String spaceId, Long startDate, Long endDate, Boolean internal) {
        try {
            Calendar fromDate = Calendar.getInstance();
            fromDate.setTimeInMillis(startDate);
            if (startDate == 0) {
                LogEntity log_ = null;
                if (spaceId.equals("")) {
                    List<LogEntity> logList = logDao.getFirstLogByType(LogType.TICKET_CREATED);
                    if (logList.size() > 0) {
                        log_ = logList.get(0);
                    }
                } else {
                    List<LogEntity> logList = logDao.getFirstLogByTypeAndSpace(spaceId, LogType.TICKET_CREATED);
                    if (logList.size() > 0) {
                        log_ = logList.get(0);
                    }
                }
                if (log_ != null) {
                    fromDate.setTime(log_.getWhen().getTime());
                } else
                    return Response.status(204);
            }
            JSONArray statList = new JSONArray();

            fromDate.set(Calendar.HOUR_OF_DAY, 0);
            fromDate.set(Calendar.MINUTE, 0);
            Calendar toDate = Calendar.getInstance();
            if (endDate != 0) {
                toDate.setTimeInMillis(endDate);
            }
            Calendar to_ = Calendar.getInstance();
            to_.setTime(fromDate.getTime());
            to_.set(Calendar.DAY_OF_MONTH, fromDate.getActualMaximum(Calendar.DAY_OF_MONTH));
            while (fromDate.before(toDate)) {
                JSONObject data = new JSONObject();
                if (spaceId.equals("")) {
                    data.put("statDate", (getDateByPeriod(fromDate.getTime(), "monthly")));
                    data.put("closedTicketNumber", logDao.countLogsByTypeAndNewValueBetweenDates(fromDate, to_, LogType.TICKET_STATUS_CHANGED, CSConstants.STATUS_CLOSED, internal).longValue());
                    data.put("openedTicketNumber", logDao.countLogsNumberBetweenDates(fromDate, to_, LogType.TICKET_CREATED, internal).longValue());
                } else {
                    data.put("statDate", (getDateByPeriod(fromDate.getTime(), "monthly")));
                    data.put("closedTicketNumber", logDao.countLogsByTypeAndNewValueAndSpaceBetweenDates(spaceId, fromDate, to_, LogType.TICKET_STATUS_CHANGED, CSConstants.STATUS_CLOSED).longValue());
                    data.put("openedTicketNumber", logDao.countLogsNumberBySpaceBetweenDates(spaceId, fromDate, to_, LogType.TICKET_CREATED).longValue());
                }

                statList.put(data);
                fromDate.setTime(to_.getTime());
                fromDate.set(Calendar.MONTH, fromDate.get(Calendar.MONTH) + 1);
                fromDate.set(Calendar.DAY_OF_MONTH, 1);
                to_.setTime(fromDate.getTime());
                to_.set(Calendar.DAY_OF_MONTH, fromDate.getActualMaximum(Calendar.DAY_OF_MONTH));
            }

            return Response.ok(statList.toString());
        } catch (Throwable e) {
            LOG.error("error while getting context", e);
            return Response.status(500);
        }
    }


    @Ajax
    @juzu.Resource
    @MimeType.JSON
    @Jackson
    public Response getLogbyStatusChartData(String spaceId, String period, Long startDate, Long endDate, Boolean internal) {
        if (period.equals("weekly")) {
            return getWeeklyLogbyStatusChartData(spaceId, startDate, endDate, internal);
        } else {
            return getMonthlyLogbyStatusChartData(spaceId, startDate, endDate, internal);
        }
    }

    @Ajax
    @juzu.Resource
    @MimeType.JSON
    @Jackson
    public Response getWeeklyLogbyStatusChartData(String spaceId, Long startDate, Long endDate, Boolean internal) {
        try {
            Calendar fromDate = Calendar.getInstance();
            fromDate.setTimeInMillis(startDate);
            JSONArray ticketsNumberByStatusList = new JSONArray();
            if (startDate == 0) {
                LogEntity log_ = null;
                if (spaceId.equals("")) {
                    List<LogEntity> logList = logDao.getFirstLogByType(LogType.TICKET_STATUS_CHANGED);
                    if (logList.size() > 0) {
                        log_ = logList.get(0);
                    }
                } else {
                    List<LogEntity> logList = logDao.getFirstLogByTypeAndSpace(spaceId, LogType.TICKET_STATUS_CHANGED);
                    if (logList.size() > 0) {
                        log_ = logList.get(0);
                    }
                }
                if (log_ != null) {
                    fromDate.setTime(log_.getWhen().getTime());
                } else
                    return Response.status(204);
            }
            fromDate.set(Calendar.HOUR_OF_DAY, 0);
            fromDate.set(Calendar.MINUTE, 0);
            fromDate.set(Calendar.SECOND, 0);
            Calendar toDate = Calendar.getInstance();
            if (endDate != 0) {
                toDate.setTimeInMillis(endDate);
            }
            int diff = Calendar.SATURDAY - fromDate.get(Calendar.DAY_OF_WEEK) + 2;
            Calendar to_ = Calendar.getInstance();
            to_.setTime(fromDate.getTime());
            to_.add(Calendar.DATE, diff);
            while (fromDate.before(toDate)) {
                List<Object[]> results = new ArrayList<>();
                if (spaceId.equals("")) {
                    results = logDao.countLogsGroupdByNewValueAndDates(internal, to_);
                } else {
                    results = logDao.countLogsBySpaceGroupdByNewValueAndDates(spaceId, to_);
                }

                JSONObject data = new JSONObject();
                data.put("openedTicketNumber", 0);
                data.put("inProgressTicketNumber", 0);
                data.put("resolvedTicketNumber", 0);
                data.put("suspendedTicketNumber", 0);
                data.put("statDate", (getDateByPeriod(fromDate.getTime(), "weekly")));
                List<String> statuses = results.stream().map(n -> (String) n[1]).collect(Collectors.toList());
                Map<String, Long> statusesCompt = statuses.stream().collect(Collectors.groupingBy(x -> x, Collectors.counting()));

                for (Map.Entry<String, Long> entry : statusesCompt.entrySet()) {

                    switch (entry.getKey() != null ? entry.getKey() : "open") {

                        case "open":
                            data.put("openedTicketNumber", entry.getValue());
                            break;
                        case "inprogress":
                            if (countTicketsPFR(spaceId, internal) == null) {
                                data.put("inProgressTicketNumber", entry.getValue());
                            } else {
                                data.put("inProgressTicketNumber", entry.getValue() - countTicketsPFR(spaceId, internal));
                            }
                            break;
                        case "resolved":
                            data.put("resolvedTicketNumber", entry.getValue());
                            break;
                        case "suspended":
                            data.put("suspendedTicketNumber", entry.getValue());
                            break;

                    }

                }
                ticketsNumberByStatusList.put(data);
                fromDate.setTime(to_.getTime());
                to_.add(Calendar.DATE, 7);
            }
            return Response.ok(ticketsNumberByStatusList.toString());
        } catch (Throwable e) {
            LOG.error("error while getting context", e);
            return Response.status(500);
        }
    }

    @Ajax
    @juzu.Resource
    @MimeType.JSON
    @Jackson
    public Response getMonthlyLogbyStatusChartData(String spaceId, Long startDate, Long endDate, Boolean internal) {
        try {
            Calendar fromDate = Calendar.getInstance();
            JSONArray ticketsNumberByStatusList = new JSONArray();
            fromDate.setTimeInMillis(startDate);
            if (startDate == 0) {
                LogEntity log_ = null;
                if (spaceId.equals("")) {
                    List<LogEntity> logList = logDao.getFirstLogByType(LogType.TICKET_STATUS_CHANGED);
                    if (logList.size() > 0) {
                        log_ = logList.get(0);
                    }
                } else {
                    List<LogEntity> logList = logDao.getFirstLogByTypeAndSpace(spaceId, LogType.TICKET_STATUS_CHANGED);
                    if (logList.size() > 0) {
                        log_ = logList.get(0);
                    }
                }
                if (log_ != null) {
                    fromDate.setTime(log_.getWhen().getTime());
                } else
                    return Response.status(204);
            }
            fromDate.set(Calendar.HOUR_OF_DAY, 0);
            fromDate.set(Calendar.MINUTE, 0);
            Calendar toDate = Calendar.getInstance();
            if (endDate != 0) {
                toDate.setTimeInMillis(endDate);
            }
            Calendar to_ = Calendar.getInstance();
            to_.setTime(fromDate.getTime());
            to_.set(Calendar.DAY_OF_MONTH, fromDate.getActualMaximum(Calendar.DAY_OF_MONTH));
            while (fromDate.before(toDate)) {
                List<Object[]> results = new ArrayList<>();
                if (spaceId.equals("")) {
                    results = logDao.countLogsGroupdByNewValueAndDates(internal, to_);
                } else {
                    results = logDao.countLogsBySpaceGroupdByNewValueAndDates(spaceId, to_);
                }
                JSONObject data = new JSONObject();
                data.put("openedTicketNumber", 0);
                data.put("inProgressTicketNumber", 0);
                data.put("resolvedTicketNumber", 0);
                data.put("suspendedTicketNumber", 0);
                data.put("statDate", (getDateByPeriod(fromDate.getTime(), "monthly")));

                List<String> statuses = results.stream().map(n -> (String) n[1]).collect(Collectors.toList());
                Map<String, Long> statusesCompt = statuses.stream().collect(Collectors.groupingBy(x -> x, Collectors.counting()));

                for (Map.Entry<String, Long> entry : statusesCompt.entrySet()) {

                    switch (entry.getKey() != null ? entry.getKey() : "open") {

                        case "open":
                            data.put("openedTicketNumber", entry.getValue());
                            break;
                        case "inprogress":
                            if (countTicketsPFR(spaceId, internal) == null) {
                                data.put("inProgressTicketNumber", entry.getValue());
                            } else {
                                data.put("inProgressTicketNumber", entry.getValue() - countTicketsPFR(spaceId, internal));
                            }
                            break;
                        case "resolved":
                            data.put("resolvedTicketNumber", entry.getValue());
                            break;
                        case "suspended":
                            data.put("suspendedTicketNumber", entry.getValue());
                            break;

                    }

                }
                ticketsNumberByStatusList.put(data);
                fromDate.setTime(to_.getTime());
                fromDate.set(Calendar.MONTH, fromDate.get(Calendar.MONTH) + 1);
                fromDate.set(Calendar.DAY_OF_MONTH, 1);
                to_.setTime(fromDate.getTime());
                to_.set(Calendar.DAY_OF_MONTH, fromDate.getActualMaximum(Calendar.DAY_OF_MONTH));
            }
            return Response.ok(ticketsNumberByStatusList.toString());
        } catch (Throwable e) {
            LOG.error("error while getting context", e);
            return Response.status(500);
        }
    }


    @Ajax
    @juzu.Resource
    @MimeType.JSON
    @Jackson
    public Response getData(String currentSpace, Boolean internal, Long startDate, Long endDate) {
        try {

            String curentSpace = getCurrentSpaceId();
            if (curentSpace == null) curentSpace = currentSpace;
            if (curentSpace != null && curentSpace.equals("/spaces/exo_support")) curentSpace = currentSpace;
            if (curentSpace != null && curentSpace.equals("/spaces/exo_global_support")) curentSpace = currentSpace;

            if (startDate == 0 || endDate == 0) {
                return getMetrics(curentSpace, internal);
            } else {
                return getMetricsByDates(curentSpace, startDate, endDate, internal);
            }

        } catch (Throwable e) {
            LOG.error("error while getting context", e);
            return Response.status(500);
        }
    }


    @Ajax
    @juzu.Resource
    @MimeType.JSON
    @Jackson
    public Response getMetrics(String curentSpace, Boolean internal) {
        try {
            JSONObject data = new JSONObject();
            data.put("currentSpace", curentSpace);
            if (curentSpace.equals("")) {
                long stisfied = topicDAO.countSatisfiedTicketNumber(internal).longValue();
                long notStisfied = topicDAO.countNotSatisfiedTicketNumber(internal).longValue();
                long total = stisfied + notStisfied;
                if (total > 0) {
                    data.put("customerSatisfactionPrc", stisfied * 100 / total);
                    data.put("customerTotalVotes", total);
                } else {
                    data.put("customerSatisfactionPrc", "--");
                }
                data.put("firstTimeResponceAverage", topicDAO.getTimeToFirstResponseAvg(internal));
                data.put("timeToResolutionAverage", topicDAO.getTimeToResolutionAvg(internal));
            } else {
                long stisfied = topicDAO.countSpaceSatisfiedTicketNumber(curentSpace).longValue();
                long notStisfied = topicDAO.countSpaceNotSatisfiedTicketNumber(curentSpace).longValue();
                long total = stisfied + notStisfied;
                if (total > 0) {
                    data.put("customerSatisfactionPrc", stisfied * 100 / total);
                    data.put("customerTotalVotes", total);

                } else {
                    data.put("customerSatisfactionPrc", "--");
                    data.put("customerTotalVotes", total);
                }
                data.put("firstTimeResponceAverage", topicDAO.getSpaceTimeToFirstResponseAvg(curentSpace));
                data.put("timeToResolutionAverage", topicDAO.getSpaceTimeToResolutionAvg(curentSpace));
            }
            return Response.ok(data.toString());
        } catch (Throwable e) {
            LOG.error("error while getting context", e);
            return Response.status(500);
        }
    }

    @Ajax
    @juzu.Resource
    @MimeType.JSON
    @Jackson
    public Response getMetricsByDates(String spaceId, Long startDate, Long endDate, Boolean internal) {
        try {
            Calendar fromDate = Calendar.getInstance();
            fromDate.setTimeInMillis(startDate);
            Calendar toDate = Calendar.getInstance();
            toDate.setTimeInMillis(endDate);
            JSONObject data = new JSONObject();
            data.put("currentSpace", spaceId);
            if (spaceId.equals("")) {
                long stisfied = topicDAO.countSatisfiedTicketNumberByDates(internal, fromDate, toDate).longValue();
                long notStisfied = topicDAO.countNotSatisfiedTicketNumberByDates(internal, fromDate, toDate).longValue();
                long total = stisfied + notStisfied;
                if (total > 0) {
                    data.put("customerSatisfactionPrc", stisfied * 100 / total);
                    data.put("customerTotalVotes", total);
                } else {
                    data.put("customerSatisfactionPrc", "--");
                    data.put("customerTotalVotes", total);
                }
                data.put("firstTimeResponceAverage", logDao.getTimeToFirstResponseAvgBetweenDates(fromDate, toDate, internal));
                data.put("timeToResolutionAverage", logDao.getTimeToResolutionAvgBetweenDates(fromDate, toDate, internal));
            } else {
                long stisfied = topicDAO.countSpaceSatisfiedTicketNumberByDates(spaceId, fromDate, toDate);
                long notStisfied = topicDAO.countSpaceNotSatisfiedTicketNumberByDates(spaceId, fromDate, toDate);
                long total = stisfied + notStisfied;
                if (total > 0) {
                    data.put("customerSatisfactionPrc", stisfied * 100 / total);
                    data.put("customerTotalVotes", total);
                } else {
                    data.put("customerSatisfactionPrc", "--");
                    data.put("customerTotalVotes", total);
                }
                data.put("firstTimeResponceAverage", logDao.getTimeToFirstResponseAvgBySpaceBetweenDates(spaceId, fromDate, toDate));
                data.put("timeToResolutionAverage", logDao.getTimeToResolutionAvgBySpaceBetweenDates(spaceId, fromDate, toDate));
            }
            return Response.ok(data.toString());
        } catch (Throwable e) {
            LOG.error("error while getting context", e);
            return Response.status(500);
        }
    }


    /**
     * This service will return the names of all support team members
     *
     * @param ticketDTO
     * @return
     */
    @Ajax
    @Resource(method = HttpMethod.GET)
    @MimeType.JSON
    @Jackson
    public List<User> getSupportUsernames(@Jackson TicketDTO ticketDTO) {
        try {
            return CSUtils.getSupportMembers();
        } catch (Throwable e) {
            LOG.error("Can't retrieve the list of support engineers " + e);
            return null;
        }
    }

    /**
     * this method returns the list of customer spaces
     *
     * @return
     */
    @Ajax
    @Resource(method = HttpMethod.GET)
    @MimeType.JSON
    @Jackson
    public List<SpaceDTO> getSpaces() {
        return spaceService.getActiveSpaces();
    }


    @Override
    public Log getLogger() {
        return LOG;
    }


    @Ajax
    @juzu.Resource
    @MimeType.JSON
    @Jackson
    public Response getSatisfactionChartData(String spaceId, String period, Long startDate, Long endDate, Boolean internal) {
        if (period.equals("weekly")) {
            return getSatisfactionByWeek(spaceId, startDate, endDate, internal);
        } else {
            return getSatisfactionByMonth(spaceId, startDate, endDate, internal);
        }
    }


    @Ajax
    @juzu.Resource
    @MimeType.JSON
    @Jackson
    public Response getSatisfactionByWeek(String spaceId, Long startDate, Long endDate, Boolean internal) {
        try {
            Calendar fromDate = Calendar.getInstance();
            fromDate.setTimeInMillis(startDate);
            if (startDate == 0) {
                LogEntity log_ = null;
                if (spaceId.equals("")) {
                    List<LogEntity> logList = logDao.getFirstLogByType(LogType.CUSTOMER_SATISFACTION_ADDED);
                    if (logList.size() > 0) {
                        log_ = logList.get(0);
                    }
                } else {
                    List<LogEntity> logList = logDao.getFirstLogByTypeAndSpace(spaceId, LogType.CUSTOMER_SATISFACTION_ADDED);
                    if (logList.size() > 0) {
                        log_ = logList.get(0);
                    }

                }
                if (log_ != null) {
                    fromDate.setTime(log_.getWhen().getTime());
                } else
                    return Response.status(204);
            }
            JSONArray cSatList = new JSONArray();

            fromDate.set(Calendar.HOUR_OF_DAY, 0);
            fromDate.set(Calendar.MINUTE, 0);
            fromDate.set(Calendar.SECOND, 0);
            Calendar toDate = Calendar.getInstance();
            toDate.setTimeInMillis(endDate);
            int diff = Calendar.SATURDAY - fromDate.get(Calendar.DAY_OF_WEEK) + 2;
            Calendar to_ = Calendar.getInstance();
            to_.setTime(fromDate.getTime());
            to_.add(Calendar.DATE, diff);
            while (fromDate.before(toDate)) {
                int stisfied = 0;
                int notStisfied = 0;
                if (spaceId.equals("")) {
                    stisfied = logDao.countLogsByTypeAndNewValueBetweenDatesGroupedByTickets(fromDate, to_, LogType.CUSTOMER_SATISFACTION_ADDED, "false", internal);
                    notStisfied = logDao.countLogsByTypeAndNewValueBetweenDatesGroupedByTickets(fromDate, to_, LogType.CUSTOMER_SATISFACTION_ADDED, "true", internal);
                } else {
                    stisfied = logDao.countLogsByTypeAndNewValueAndSpaceBetweenDatesGroupedByTickets(spaceId, fromDate, to_, LogType.CUSTOMER_SATISFACTION_ADDED, "false");
                    notStisfied = logDao.countLogsByTypeAndNewValueAndSpaceBetweenDatesGroupedByTickets(spaceId, fromDate, to_, LogType.CUSTOMER_SATISFACTION_ADDED, "true");
                }
                int total = stisfied + notStisfied;

                JSONObject data = new JSONObject();
                if (total > 0) {
                    data.put("satisfactionDate", getDateByPeriod(fromDate.getTime(), "weekly"));
                    data.put("customerSatisfactionPrc", stisfied * 100 / total);
                    data.put("customerTotalVotes", total);
                } else {
                    data.put("satisfactionDate", getDateByPeriod(fromDate.getTime(), "weekly"));
                    data.put("customerSatisfactionPrc", "--");
                    data.put("customerTotalVotes", total);
                }

                cSatList.put(data);
                fromDate.setTime(to_.getTime());
                to_.add(Calendar.DATE, 7);
            }

            return Response.ok(cSatList.toString());
        } catch (Throwable e) {
            LOG.error("error while getting context", e);
            return Response.status(500);
        }
    }


    @Ajax
    @juzu.Resource
    @MimeType.JSON
    @Jackson
    public Response getSatisfactionByMonth(String spaceId, Long startDate, Long endDate, Boolean internal) {
        try {
            Calendar fromDate = Calendar.getInstance();
            fromDate.setTimeInMillis(startDate);
            if (startDate == 0) {
                LogEntity log_ = null;
                if (spaceId.equals("")) {
                    List<LogEntity> logList = logDao.getFirstLogByType(LogType.CUSTOMER_SATISFACTION_ADDED);
                    if (logList.size() > 0) {
                        log_ = logList.get(0);
                    }
                } else {
                    List<LogEntity> logList = logDao.getFirstLogByTypeAndSpace(spaceId, LogType.CUSTOMER_SATISFACTION_ADDED);
                    if (logList.size() > 0) {
                        log_ = logList.get(0);
                    }
                }
                if (log_ != null) {
                    fromDate.setTime(log_.getWhen().getTime());
                } else
                    return Response.status(204);
            }
            JSONArray cSatList = new JSONArray();

            fromDate.set(Calendar.HOUR_OF_DAY, 0);
            fromDate.set(Calendar.MINUTE, 0);
            Calendar toDate = Calendar.getInstance();
            toDate.setTimeInMillis(endDate);
            Calendar to_ = Calendar.getInstance();
            to_.setTime(fromDate.getTime());
            to_.set(Calendar.DAY_OF_MONTH, fromDate.getActualMaximum(Calendar.DAY_OF_MONTH));
            while (fromDate.before(toDate)) {
                int stisfied = 0;
                int notStisfied = 0;
                if (spaceId.equals("")) {
                    stisfied = logDao.countLogsByTypeAndNewValueBetweenDatesGroupedByTickets(fromDate, to_, LogType.CUSTOMER_SATISFACTION_ADDED, "false", internal);
                    notStisfied = logDao.countLogsByTypeAndNewValueBetweenDatesGroupedByTickets(fromDate, to_, LogType.CUSTOMER_SATISFACTION_ADDED, "true", internal);
                } else {
                    stisfied = logDao.countLogsByTypeAndNewValueAndSpaceBetweenDatesGroupedByTickets(spaceId, fromDate, to_, LogType.CUSTOMER_SATISFACTION_ADDED, "false");
                    notStisfied = logDao.countLogsByTypeAndNewValueAndSpaceBetweenDatesGroupedByTickets(spaceId, fromDate, to_, LogType.CUSTOMER_SATISFACTION_ADDED, "true");
                }
                int total = stisfied + notStisfied;

                JSONObject data = new JSONObject();
                if (total > 0) {
                    data.put("satisfactionDate", getDateByPeriod(fromDate.getTime(), "monthly"));
                    data.put("customerSatisfactionPrc", stisfied * 100 / total);
                    data.put("customerTotalVotes", total);
                } else {
                    data.put("satisfactionDate", getDateByPeriod(fromDate.getTime(), "monthly"));
                    data.put("customerSatisfactionPrc", "--");
                    data.put("customerTotalVotes", total);
                }

                cSatList.put(data);
                fromDate.setTime(to_.getTime());
                fromDate.set(Calendar.MONTH, fromDate.get(Calendar.MONTH) + 1);
                fromDate.set(Calendar.DAY_OF_MONTH, 1);
                to_.setTime(fromDate.getTime());
                to_.set(Calendar.DAY_OF_MONTH, fromDate.getActualMaximum(Calendar.DAY_OF_MONTH));
            }

            return Response.ok(cSatList.toString());
        } catch (Throwable e) {
            LOG.error("error while getting context", e);
            return Response.status(500);
        }
    }


    @Ajax
    @juzu.Resource
    @MimeType.JSON
    @Jackson
    public Response getTimeMetrics(String spaceId, String period, Long startDate, Long endDate, Boolean internal) {
        if (period.equals("weekly")) {
            return getTimeMetricsByWeek(spaceId, startDate, endDate, internal);
        } else {
            return getTimeMetricsByMonth(spaceId, startDate, endDate, internal);
        }
    }


    @Ajax
    @juzu.Resource
    @MimeType.JSON
    @Jackson
    public Response getTimeMetricsByWeek(String spaceId, Long startDate, Long endDate, Boolean internal) {
        try {
            Calendar fromDate = Calendar.getInstance();
            fromDate.setTimeInMillis(startDate);
            if (startDate == 0) {

                LogEntity log_ = logDao.getTimeToFirstResponseFirstLog().get(0);
                LogEntity log__ = logDao.getTimeToResolutionFirstLog().get(0);
                if (log_ != null) {
                    fromDate.setTime(log_.getWhen().getTime());
                } else if (log__ != null) {
                    if (log_ != null && log__.getWhen().before(log_.getWhen()))
                        fromDate.setTime(log__.getWhen().getTime());
                } else
                    return Response.status(204);
            }
            JSONArray npsList = new JSONArray();

            fromDate.set(Calendar.HOUR_OF_DAY, 0);
            fromDate.set(Calendar.MINUTE, 0);
            fromDate.set(Calendar.SECOND, 0);
            Calendar toDate = Calendar.getInstance();
            toDate.setTimeInMillis(endDate);
            int diff = Calendar.SATURDAY - fromDate.get(Calendar.DAY_OF_WEEK) + 2;
            Calendar to_ = Calendar.getInstance();
            to_.setTime(fromDate.getTime());
            to_.add(Calendar.DATE, diff);
            while (fromDate.before(toDate)) {
                Double ttfs = null;
                Double ttr = null;
                if (spaceId.equals("")) {
                    ttfs = logDao.getTimeToFirstResponseAvgBetweenDates(fromDate, to_, internal);
                    ttr = logDao.getTimeToResolutionAvgBetweenDates(fromDate, to_, internal);
                } else {
                    ttfs = logDao.getTimeToFirstResponseAvgBySpaceBetweenDates(spaceId, fromDate, to_);
                    ttr = logDao.getTimeToResolutionAvgBySpaceBetweenDates(spaceId, fromDate, to_);
                }
                JSONObject ttfs_ = new JSONObject();
                ttfs_.put("timeMetricsDate", getDateByPeriod(fromDate.getTime(), "weekly"));
                ttfs_.put("timeToFirstResponse", ttfs);
                ttfs_.put("timeToResolution", ttr);
                npsList.put(ttfs_);
                fromDate.setTime(to_.getTime());
                to_.add(Calendar.DATE, 7);
            }

            return Response.ok(npsList.toString());
        } catch (Throwable e) {
            LOG.error("error while getting context", e);
            return Response.status(500);
        }
    }


    @Ajax
    @juzu.Resource
    @MimeType.JSON
    @Jackson
    public Response getTimeMetricsByMonth(String spaceId, Long startDate, Long endDate, Boolean internal) {
        try {
            Calendar fromDate = Calendar.getInstance();
            fromDate.setTimeInMillis(startDate);
            if (startDate == 0) {
                LogEntity log_ = null;
                LogEntity log__ = null;
                List<LogEntity> logList = logDao.getTimeToFirstResponseFirstLog();
                if (logList.size() > 0) {
                    log_ = logList.get(0);
                }
                logList = logDao.getTimeToResolutionFirstLog();
                if (logList.size() > 0) {
                    log__ = logList.get(0);
                }
                if (log_ != null) {
                    fromDate.setTime(log_.getWhen().getTime());
                } else if (log__ != null) {
                    if (log_ != null && log__.getWhen().before(log_.getWhen()))
                        fromDate.setTime(log__.getWhen().getTime());
                } else
                    return Response.status(204);
            }
            JSONArray npsList = new JSONArray();

            fromDate.set(Calendar.HOUR_OF_DAY, 0);
            fromDate.set(Calendar.MINUTE, 0);
            Calendar toDate = Calendar.getInstance();
            toDate.setTimeInMillis(endDate);
            Calendar to_ = Calendar.getInstance();
            to_.setTime(fromDate.getTime());
            to_.set(Calendar.DAY_OF_MONTH, fromDate.getActualMaximum(Calendar.DAY_OF_MONTH));
            while (fromDate.before(toDate)) {
                Double ttfs = logDao.getTimeToFirstResponseAvgBetweenDates(fromDate, to_, internal);
                Double ttr = logDao.getTimeToResolutionAvgBetweenDates(fromDate, to_, internal);

                JSONObject ttfs_ = new JSONObject();
                ttfs_.put("timeMetricsDate", getDateByPeriod(fromDate.getTime(), "monthly"));
                ttfs_.put("timeToFirstResponse", ttfs);
                ttfs_.put("timeToResolution", ttr);
                npsList.put(ttfs_);
                fromDate.setTime(to_.getTime());
                fromDate.set(Calendar.MONTH, fromDate.get(Calendar.MONTH) + 1);
                fromDate.set(Calendar.DAY_OF_MONTH, 1);
                to_.setTime(fromDate.getTime());
                to_.set(Calendar.DAY_OF_MONTH, fromDate.getActualMaximum(Calendar.DAY_OF_MONTH));
            }

            return Response.ok(npsList.toString());
        } catch (Throwable e) {
            LOG.error("error while getting context", e);
            return Response.status(500);
        }
    }

    public String getDateByPeriod(Date date, String period) {
        if (period.equals("weekly")) {
            return byWeekDateFormat.format(date);
        } else if (period.equals("monthly")) {
            return byMonthDateFormat.format(date);
        }
        return byDayDateFormat.format(date);
    }


    public boolean sameDay(Calendar cal1, Calendar cal2) {
        return cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR) && cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR);
    }


    public Long countTicketsPFR(String spaceId, Boolean internal) {

        Long ticketsNumberPFR = null;
        List<Object[]> ticketsList = new ArrayList<>();

        if (spaceId.equals("")) {
            ticketsList = topicDAO.countCustomerTicketsGroupdByOwner(internal);
        } else {
            ticketsList = topicDAO.countSpaceTicketsGroupdByOwner(spaceId);
        }
        for (int i = 0; i < ticketsList.size(); i++) {
            Object[] arr = ticketsList.get(i);
            if (((Owner) arr[0]).name() == "PM") {
                ticketsNumberPFR = (Long) arr[1];
            }
        }
        return ticketsNumberPFR;
    }


}
