package org.exoplatform.poll.service.ws;

import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
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.Response.Status;
import javax.ws.rs.core.SecurityContext;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.ext.RuntimeDelegate;

import org.apache.commons.lang.StringUtils;
import org.exoplatform.container.ExoContainerContext;
import org.exoplatform.poll.service.Poll;
import org.exoplatform.poll.service.PollNodeTypes;
import org.exoplatform.poll.service.PollService;
import org.exoplatform.poll.service.PollSummary;
import org.exoplatform.poll.service.Utils;
import org.exoplatform.portal.config.UserACL;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import org.exoplatform.services.organization.Group;
import org.exoplatform.services.organization.Membership;
import org.exoplatform.services.organization.OrganizationService;
import org.exoplatform.services.rest.impl.RuntimeDelegateImpl;
import org.exoplatform.services.rest.resource.ResourceContainer;
import org.exoplatform.services.security.ConversationState;
import org.exoplatform.services.security.IdentityConstants;

/**
 * Handles REST request to process Poll.
 * 
 * @anchor PollWebservice
 * 
 */
@Path("ks/poll")
public class PollWebservice implements ResourceContainer {
  private static final Log   log      = ExoLogger.getLogger(PollWebservice.class);

  final public static String APP_TYPE = "poll".intern();
  private OrganizationService organizationService = null;
  
  public PollWebservice() {
    getOrganizationService();
  }
  
  private static final CacheControl         cc;
  static {
    RuntimeDelegate.setInstance(new RuntimeDelegateImpl());
    cc = new CacheControl();
    cc.setNoCache(true);
    cc.setNoStore(true);
  }
  
  /**
   * Views a poll by its Id.
   * 
   * @param pollId The poll Id to view.
   * @param sc The security context which gets the information of the current user.
   * @param uriInfo The URI information which gets the information of the current user.
   * 
   * @anchor PollWebservice.viewPoll
   * 
   * @return The response is JSON data.
   * 
   * @throws Exception
   * 
   * @LevelAPI Platform
   */
  @GET
  @Path("/viewpoll/{resourceid}")
  @Produces(MediaType.APPLICATION_JSON)
  public Response viewPoll(@PathParam("resourceid") String pollId,
                           @Context SecurityContext sc,
                           @Context UriInfo uriInfo) throws Exception {
    PollService pollService = (PollService) ExoContainerContext.getCurrentContainer().getComponentInstanceOfType(PollService.class);
    String username = getUserId(sc, uriInfo);
    if (!Utils.isEmpty(pollId)) {
      try {
        Poll poll = pollService.getPoll(pollId);
        if (poll != null) {
          // poll.setIsAdmin(String.valueOf(hasGroupAdminOfGatein()));
          poll.setIsAdmin("true");
          String group = poll.getParentPath();
          boolean hasPerminsion = false;
          if (group.indexOf(PollNodeTypes.APPLICATION_DATA) > 0 && poll.getIsAdmin().equals("false")) {
            group = group.substring(group.indexOf(PollNodeTypes.GROUPS + "/") + PollNodeTypes.GROUPS.length(), group.indexOf("/" + PollNodeTypes.APPLICATION_DATA));
            for (String group_ : getGroupsOfUser(username)) {
              if (group_.indexOf(group) >= 0) {
                hasPerminsion = true;
                break;
              }
            }
          } else if (group.indexOf(PollNodeTypes.POLLS) < 0) {
            hasPerminsion = pollService.hasPermissionInForum(group + "/" + poll.getId(), getAllGroupAndMembershipOfUser(username));
          } else {
            hasPerminsion = true;
          }
          if (!hasPerminsion) {
            poll = new Poll();
            poll.setId("DoNotPermission");
            return Response.ok(poll, MediaType.APPLICATION_JSON).cacheControl(cc).build();
          }
          poll.setVotes();
          poll.setInfoVote();
          poll.setShowVote(isGuestPermission(poll, username));
          return Response.ok(poll, MediaType.APPLICATION_JSON).cacheControl(cc).build();
        }
      } catch (Exception e) {
        log.error("Can not get poll by id: " + pollId, e);
      }
    }
    PollSummary pollSummary = new PollSummary();
    pollSummary = pollService.getPollSummary(getAllGroupAndMembershipOfUser(username));
    pollSummary.setIsAdmin("true");
    return Response.ok(pollSummary, MediaType.APPLICATION_JSON).cacheControl(cc).build();
  }

  /**
   * Votes for a poll by its Id.
   * 
   * @param pollId The poll Id to vote.
   * @param indexVote The vote indexes selected by the current user.
   * @param sc The security context which gets the information of the current user.
   * @param uriInfo The URI information which gets the information of the current user.
   * 
   * @anchor PollWebservice.votePoll
   * 
   * @return The response is JSON data.
   * 
   * @throws Exception
   * 
   * @LevelAPI Platform
   */
  @GET
  @Path("/votepoll/{pollId}/{indexVote}")
  @Produces(MediaType.APPLICATION_JSON)
  public Response votePoll(@PathParam("pollId") String pollId,
                           @PathParam("indexVote") String indexVote,
                           @Context SecurityContext sc,
                           @Context UriInfo uriInfo) throws Exception {
    if (!Utils.isEmpty(pollId) && !Utils.isEmpty(indexVote)) {
      try {
        PollService pollService = (PollService) ExoContainerContext.getCurrentContainer().getComponentInstanceOfType(PollService.class);
        Poll poll = pollService.getPoll(pollId.trim());
        String username = getUserId(sc, uriInfo);
        if (poll != null && !IdentityConstants.ANONIM.equals(username) && 
            validateIndexVote(indexVote, poll.getOption().length)) {
          poll = Utils.calculateVote(poll, username, indexVote);
          pollService.savePoll(poll, false, true);
          poll.setVotes();
          poll.setInfoVote();
          poll.setShowVote(isGuestPermission(poll, username));
          poll.setIsAdmin(String.valueOf(hasGroupAdminOfGatein(username)));
          return Response.ok(poll, MediaType.APPLICATION_JSON).cacheControl(cc).build();
        }
      } catch (Exception e) {
        log.debug("Failed to vote poll.", e);
      }
      return Response.ok("You do not have permission to vote this poll; or some options have been removed from the poll.", MediaType.TEXT_PLAIN).cacheControl(cc).build();
    }
    return Response.status(Status.INTERNAL_SERVER_ERROR).build();
  }

  private OrganizationService getOrganizationService() {
    if (organizationService == null) {
      organizationService = (OrganizationService) ExoContainerContext.getCurrentContainer()
                                .getComponentInstance(OrganizationService.class);
    }
    return organizationService;
  }
  
  private String getUserId(SecurityContext sc, UriInfo uriInfo) {
    ConversationState state = ConversationState.getCurrent();
    if(state != null) {
      return state.getIdentity().getUserId();
    }
    if (sc != null && sc.getUserPrincipal() != null) {
      return sc.getUserPrincipal().getName();
    } else if (uriInfo != null) {
      return getViewerId(uriInfo);
    }
    return StringUtils.EMPTY;
  }
  
  private String getViewerId(UriInfo uriInfo) {
    URI uri = uriInfo.getRequestUri();
    String requestString = uri.getQuery();
    if (requestString == null) return null;
    String[] queryParts = requestString.split("&");
    for (String queryPart : queryParts) {
      if (queryPart.startsWith("opensocial_viewer_id")) {
        return queryPart.substring(queryPart.indexOf("=") + 1, queryPart.length());
      }
    }
    return null;
  }
  
  private boolean validateIndexVote(String indexVote, int max) {
    String[] ivArr = indexVote.split(Utils.COLON);
    for (int i = 1; i < ivArr.length; i++) {
      try {
        int t = Integer.parseInt(ivArr[i]);
        if (t >= max) {
          return false;
        }
      } catch (Exception e) {
        return false;
      }
    }
    return true;
  }

  private boolean isGuestPermission(Poll poll_, String username) throws Exception {
    if (poll_.getIsClosed())
      return true;
    if (poll_.getTimeOut() > 0) {
      Date today = Utils.getGreenwichMeanTime().getTime();
      if ((today.getTime() - poll_.getCreatedDate().getTime()) >= poll_.getTimeOut() * 86400000)
        return true;
    }
    if (Utils.isEmpty(username) || IdentityConstants.ANONIM.equals(username))
      return true;
    String[] userVotes = poll_.getUserVote();
    for (String string : userVotes) {
      string = string.substring(0, string.indexOf(":"));
      if (string.equalsIgnoreCase(username))
        return true;
    }
    return false;
  }

  private boolean hasGroupAdminOfGatein(String username) {
    try {
      UserACL userACL = (UserACL) ExoContainerContext.getCurrentContainer().getComponentInstanceOfType(UserACL.class);
      List<String> list = new ArrayList<String>();
      list.add(username);
      list.addAll(getGroupsOfUser(username));
      for (String str : list) {
        if (str.equals(userACL.getSuperUser()) || str.equals(userACL.getAdminGroups()))
          return true;
      }
    } catch (Exception e) {
      log.debug("Failed to check has group admin of gatein." + e.getCause());
    }
    return false;
  }

  @SuppressWarnings("unchecked")
  private List<String> getAllGroupAndMembershipOfUser(String username) {
    List<String> listOfUser = new ArrayList<String>();
    try {
      listOfUser.add(username);// himself
      Set<String> list = new HashSet<String>();
      Collection<Membership> memberships = organizationService.getMembershipHandler().findMembershipsByUser(username);
      for (Membership membership : memberships) {
        listOfUser.add(membership.getGroupId()); // its groups
        listOfUser.add(membership.getMembershipType() + ":" + membership.getGroupId()); // its memberships
      }
      listOfUser.addAll(list);
    } catch (Exception e) {
      log.warn("Failed to get all info of user.");
    }
    return listOfUser;
  }

  private List<String> getGroupsOfUser(String username) {
    try {
      List<String> grIds = new ArrayList<String>();
      ConversationState state = ConversationState.getCurrent();
      if(state != null && username.equals(state.getIdentity().getUserId())) {
        Set<String> groupsSet = state.getIdentity().getGroups();
        grIds.addAll(groupsSet);
      } else {
        for (Object gr : getOrganizationService().getGroupHandler().findGroupsOfUser(username)) {
          grIds.add(((Group) gr).getId());
        }
      }
      return grIds;
    } catch (Exception e) {
      log.warn("Failed to get groupId of user.");
      return new ArrayList<String>();
    }
  }
}
