package org.exoplatform.cs.service;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;


import javax.annotation.security.RolesAllowed;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.*;
import javax.ws.rs.core.CacheControl;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.ext.RuntimeDelegate;
import org.exoplatform.container.ExoContainer;
import org.exoplatform.container.ExoContainerContext;
import org.exoplatform.container.PortalContainer;
import org.exoplatform.container.RootContainer;
import org.exoplatform.cs.dao.LogDAO;
import org.exoplatform.cs.dto.Owner;
import org.exoplatform.cs.dto.TicketDTO;
import org.exoplatform.cs.entity.LogEntity;
import org.exoplatform.cs.entity.TopicEntity;
import org.exoplatform.cs.service.util.ForumUtils;
import org.exoplatform.forum.service.Category;
import org.exoplatform.forum.service.Forum;
import org.exoplatform.forum.service.ForumService;
import org.exoplatform.forum.service.Topic;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import org.exoplatform.services.organization.OrganizationService;
import org.exoplatform.services.organization.User;
import org.exoplatform.services.rest.impl.RuntimeDelegateImpl;
import org.exoplatform.services.rest.resource.ResourceContainer;
import org.exoplatform.services.security.ConversationState;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

/**
 * Created by eXo Platform SAS.
 *
 * @author Ali Hamdi
 * @since 03/04/17
 */
@Path("customerspace")
public class TicketUpdatesService implements ResourceContainer {

  private static final Log LOG = ExoLogger.getLogger(TicketUpdatesService.class);
  private static final CacheControl cc;
  private static final String PATTERN = "dd-MM-yyyy HH:mm:ss Z";
  private static final SimpleDateFormat sdf = new SimpleDateFormat(PATTERN);


  static {
    RuntimeDelegate.setInstance(new RuntimeDelegateImpl());
    cc = new CacheControl();
    cc.setNoCache(true);
    cc.setNoStore(true);
  }

  /**
   * This endpoint will provide the lately updated tickets
   * @param uriInfo URI info
   * @param from Date to get updates from
   * @param to Date to get updates to
   * @return Json array of updated tickets
   */
  @GET
  @RolesAllowed("users")
  @Path("cstickets/updates.json")
  @Produces(MediaType.APPLICATION_JSON)
  public Response getUpdates(@Context UriInfo uriInfo,
                             @QueryParam("from") String from,
                             @QueryParam("to") String to) {
    TicketService ticketService = getService(TicketService.class, null);
    LogDAO logDAO = getService(LogDAO.class, null);
    String DATE_FORMAT = "dd-MM-yyyy";
    DateFormat df = new SimpleDateFormat(DATE_FORMAT);
    Calendar fromDate = Calendar.getInstance();
    Calendar toDate = Calendar.getInstance();
    try {
      if(from != null && !from.isEmpty()) {
        fromDate.setTime(df.parse(from));
      } else {
        fromDate.add(Calendar.MONTH, -1); // default value for from is the value of To Date minus one month
      }
      if(to != null && !to.isEmpty()) {
        toDate.setTime(df.parse(to));
      }
    } catch (ParseException e) {
      LOG.error("Start and End Date format should be {}", DATE_FORMAT);
      return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
    }
    List<LogEntity> updates = logDAO.getupdates(fromDate,toDate);
    String response = buildJsonArrayOfUpdates(updates);
    return Response.ok(response, MediaType.APPLICATION_JSON).cacheControl(cc).build();
  }

  private String buildJsonArrayOfUpdates(List<LogEntity> updatesList) {
    JSONArray ticketsJSON = new JSONArray();
    Map<TopicEntity,List<LogEntity>> updatesMap = new HashMap<>();
    for(LogEntity logEntity : updatesList) {
      updatesMap.computeIfAbsent(logEntity.getTopic(), k -> new ArrayList<>()).add(logEntity);
    }
    updatesMap.forEach((k,v)-> {
      JSONObject ticketJSON = new JSONObject();
        try {
          ticketJSON.put("ticket", buildJsonObject(k));
          for (LogEntity entity : v) {
            JSONObject update = new JSONObject();
            update.put("id", entity.getTopic().getId());
            update.put("topicTitle", entity.getTopic().getTitle());
            update.put("updater", entity.getUserID());
            update.put("updateType", entity.getType().name());
            update.put("oldValue", entity.getOriginalValue());
            update.put("newValue", entity.getNewValue());
            update.put("when", sdf.format(entity.getWhen().getTime()));
            ticketJSON.append("updates",update);
          }
          ticketsJSON.put(ticketJSON);
        }catch(Exception e) {
          LOG.error("A problem when building JSON for LogEntity {}", k.getId(), e);
        }
      }
    );
    return ticketsJSON.toString();
    }

  /**
   * 
   * @param clazz service class
   * @param containerName portal container name
   * @param <T> Type of class
   * @return the selected service
   */
  public static <T> T getService(Class<T> clazz, String containerName) {
    ExoContainer container = ExoContainerContext.getCurrentContainer();
    if (containerName != null) {
      container = RootContainer.getInstance().getPortalContainer(containerName);
    }
    if (container.getComponentInstanceOfType(clazz) == null) {
      containerName = PortalContainer.getCurrentPortalContainerName();
      container = RootContainer.getInstance().getPortalContainer(containerName);
    }
    return clazz.cast(container.getComponentInstanceOfType(clazz));
  }

  @GET
  @Path("cstickets/addcreator")
  @RolesAllowed("administrators")
  @Consumes({MediaType.APPLICATION_JSON})
  public Response addCreator(@Context HttpServletRequest request,
                       @Context UriInfo uriInfo) throws Exception {

    try {
      TicketService ticketService = getService(TicketService.class, null);
      ForumService forumService = getService(ForumService.class, null);
      OrganizationService organizationService = getService(OrganizationService.class, null);
      String currentUser = ConversationState.getCurrent().getIdentity().getUserId();
      User user = organizationService.getUserHandler().findUserByName(currentUser);
      List<TicketDTO> tickets = ticketService.getTickets("",true,"");
      for(TicketDTO ticket : tickets){
        if(ticket.getCreator()==null||ticket.getCreator().equals("")){
          String topicId = ticket.getId();
          Category spaceCategory = forumService.getCategoryIncludedSpace();
          Forum forum = ForumUtils.getSpaceForum(forumService, ticket.getSpaceGroupId());
          if (spaceCategory != null && forum != null) {
            try {
              Topic topic = forumService.getTopic(spaceCategory.getId(), forum.getId(), topicId, null);
              ticketService.updateTicketCreator(ticket,topic.getOwner());
              LOG.info("Ticket "+ ticket.getTitle()+"'s creator set to "+topic.getOwner());
            } catch (Exception e) {
              LOG.error("Can not get data of Topic {}", topicId);
            }
          }
        }
      }



      return Response.ok("Tickets Updates").build();
    } catch (Exception e) {
      LOG.error(e);
      return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity("An internal error has occured").build();
    }
  }



  @GET
  @Path("cstickets/addowner")
  @RolesAllowed("administrators")
  @Consumes({MediaType.APPLICATION_JSON})
  public Response addOwner(@Context HttpServletRequest request,
                       @Context UriInfo uriInfo) throws Exception {

    try {
      TicketService ticketService = getService(TicketService.class, null);
      List<TicketDTO> tickets = ticketService.getTickets("",true,"");
      String owner = "";
      for(TicketDTO ticket : tickets){
        if(ticket.getOwner()==null||ticket.getOwner().equals("")){
            try {
              if(ticket.getStatus().equals("closed")){
                ticketService.updateTicketOwner(ticket,Owner.EMPTY);
                owner = Owner.EMPTY.name();
              }else if (ticket.getStatus().equals("suspended_wfi") || ticket.getStatus().equals("resolved_wfv")){
                ticketService.updateTicketOwner(ticket, Owner.CUSTOMER);
                owner =  Owner.CUSTOMER.name();
              }else if(ticket.getStatus().equals("resolved_maintenance")){
                ticketService.updateTicketOwner(ticket, Owner.MAINTENANCE);
                owner = Owner.MAINTENANCE.name();
              } else if(ticket.getStatus().equals("resolved_improvement")){
                ticketService.updateTicketOwner(ticket, Owner.PM);
                owner = Owner.PM.name();
              } else{
                ticketService.updateTicketOwner(ticket, Owner.SUPPORT);
                owner = Owner.SUPPORT.name();
              }

              LOG.info("Ticket "+ ticket.getTitle()+"'s owner set to "+owner);
            } catch (Exception e) {
              LOG.error("Can not update owner");
            }

        }
      }



      return Response.ok("Tickets Updates").build();
    } catch (Exception e) {
      LOG.error(e);
      return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity("An internal error has occured").build();
    }
  }



  private JSONObject buildJsonObject(TopicEntity topicEntity) {
    JSONObject ticket = new JSONObject();
    try {
      ticket.put("id", topicEntity.getId());
      ticket.put("assignee", topicEntity.getAssignee());
      ticket.put("environmentName", topicEntity.getEnvironment() != null ? topicEntity.getEnvironment().getName():"");
      ticket.put("infoType", topicEntity.getInfoType() != null ? topicEntity.getInfoType().name() : "");
      ticket.put("link", topicEntity.getLink());
      ticket.put("severity", topicEntity.getSeverity() != null ? topicEntity.getSeverity().name() : "");
      ticket.put("spaceGroupId", topicEntity.getSpace().getGroupId());
      ticket.put("title", topicEntity.getTitle());
      ticket.put("type", topicEntity.getType() != null ? topicEntity.getType().name() : "");
      ticket.put("status", topicEntity.getStatus());
      ticket.put("updateDate", sdf.format(topicEntity.getUpdateDate().getTime()));
    } catch (JSONException jsonException) {
      LOG.error("A problem when building JSON for ticket {}", topicEntity.getId());
    }
    return ticket;
  }

  private String buildJsonArray(List<TopicEntity> ticketDTOS) {
    JSONArray array = new JSONArray();
    for (TopicEntity ticketDTO : ticketDTOS) {
      JSONObject ticket = buildJsonObject(ticketDTO);
      array.put(ticket);
    }
    return array.toString();
  }

}
