package com.k_int.ciim;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
import org.springframework.stereotype.Component;

import com.k_int.ciim.json.CIIMUserJSON;
import com.k_int.ciim.kernel.CIIMDataManager;
import com.k_int.ciim.ref.Constants;
import com.k_int.ciim.utils.IdentityServiceWrapper;
import com.k_int.svc.identity.datamodel.AuthenticationDetailsHDO;
import com.k_int.svc.identity.datamodel.RegisteredUserHDO;
import com.k_int.svc.identity.datamodel.RoleHDO;
import com.k_int.svc.identity.service.IdentityService;
import com.k_int.svc.identity.service.IdentityServiceException;
import com.k_int.svc.identity.service.SystemUserDTO;
import com.k_int.svc.identity.service.TargetObjectIdentifier;
import com.sun.jersey.api.view.ImplicitProduces;

/**
 * REST Web Service
 *
 * @author mark
 * curl -s -H 'Accept: application/json' http://localhost:8080/Template/CIIM/Admin
 * ImplicitProduces("text/html;qs=5")   
 */

@XmlRootElement
@ImplicitProduces("application/json;qs=5")
@XmlAccessorType(XmlAccessType.FIELD)
@Path("/CIIM/Admin")
@Component
public class Administration implements ApplicationContextAware
{
	  @XmlTransient
	  private ApplicationContext ctx;
	  @XmlTransient
	  private IdentityService ident_srv;
	  @XmlTransient
	  private SessionFactory factory = null;
	  
	  private List<CIIMUserJSON> users = new ArrayList<CIIMUserJSON>(); 
	  
	  @XmlTransient
	  private static Log log = LogFactory.getLog(Administration.class);
	  	  
	  public void setApplicationContext(ApplicationContext ctx) 
	  {
	    this.ctx = ctx;
	  }
	  
	  public void setIdentityService(IdentityService ident_srv) 
	  {
	    this.ident_srv = ident_srv;
	  }
	  
	  public void setSessionFactory(SessionFactory factory)
	  {
	    this.factory = factory;
	  }
	  
	  @javax.annotation.PreDestroy
	  public void destroy() {
	  }
	   
	  /** Creates a new instance */
	  public Administration() 
	  {  
		  
	  }	  

	  @javax.annotation.PostConstruct
	  public void init() 
	  {
		  
	  }
	  
	  @Path("/")
	  @GET
	  @Produces({MediaType.APPLICATION_JSON})
	  public Administration getAdminJSON()
	  {		
		  Session session = null;
		  
		  users = new ArrayList<CIIMUserJSON>();
		  
		  try 
		  {
			  session = SessionFactoryUtils.getSession(factory, true);
			  
			  Query q = session.createQuery("Select x from com.k_int.svc.identity.datamodel.AuthenticationDetailsHDO x");
			  
			  List<AuthenticationDetailsHDO> lResults = (List<AuthenticationDetailsHDO>) q.list();
			  
			  //now iterate through results and retrieve the roles as they are set as lazy load
			  if(lResults != null && lResults.size() > 0)
			  {
				  for(AuthenticationDetailsHDO lAuthDetail : lResults)
				  {
					  if(lAuthDetail != null && lAuthDetail.getUser() != null)
					  {
						  CIIMUserJSON lUser = CIIMUserJSON.userHDOtoDTO(lAuthDetail.getUser(), lAuthDetail.getName(), lAuthDetail.getPassword());
						  	
						  // Now that role has been loaded on the HDO add it to the users list
						  users.add(lUser);
					  }
				  }
			  }			   
		  }
		  catch (HibernateException he) 
		  {
			  log.error("[firstrun:1] Problem returning users from db", he);
		  } 
		  catch (Exception e) 
		  {
			  log.error("[firstrun:1] Problem returning users", e);
		  } 
		
		  return this;
	  }
	  
	  public CIIMUserJSON getUser(String username)
	  {
		  Session session = null;
		  
		  CIIMUserJSON lReturn = null;
		  
		  try 
		  {
			  session = SessionFactoryUtils.getSession(factory, true);
			  
			  Query q = session.createQuery("Select x from com.k_int.svc.identity.datamodel.AuthenticationDetailsHDO x where x.username = ?");
			  q.setParameter(0, username);
			  
			  AuthenticationDetailsHDO lResult = (AuthenticationDetailsHDO) q.uniqueResult();
			  
			  //now iterate through results and retrieve the roles as they are set as lazy load
			  if(lResult != null)
			  {
					lReturn = CIIMUserJSON.userHDOtoDTO(lResult.getUser(), lResult.getName(), lResult.getPassword());
			  }			   
		  }
		  catch (HibernateException he) 
		  {
			  log.error("[firstrun:1] Problem returning users from db", he);
		  } 
		  catch (Exception e) 
		  {
			  log.error("[firstrun:1] Problem returning users", e);
		  } 
		
		  return lReturn;
	  }
	  
	  @Path("/GetUsersInRole")
	  @POST
	  @Produces({MediaType.APPLICATION_JSON})
	  public  List<CIIMUserJSON> getUsersInRole(@FormParam("role") String role)
	  {
		  Session session = null;
		  
		  List<CIIMUserJSON> lReturn = new ArrayList<CIIMUserJSON>();
		  
		  try 
		  {
			  session = SessionFactoryUtils.getSession(factory, true);
			  
			  Query q = session.createQuery("Select x from com.k_int.svc.identity.datamodel.AuthenticationDetailsHDO x");
			  
			  List<AuthenticationDetailsHDO> lResults = (List<AuthenticationDetailsHDO>) q.list();
	  
			  if(lResults != null && lResults.size() > 0)
			  {
				  for(AuthenticationDetailsHDO lAuthDetail : lResults)
				  {
					  if(lAuthDetail != null)
					  {
						  RegisteredUserHDO lUser = lAuthDetail.getUser();
						  
						  if(lUser != null)
						  {
							  Set<RoleHDO> lRoles = lUser.getRoles();
							  
							  for(RoleHDO lRole : lRoles)
							  {
								  if(lRole != null && lRole.getName().equals(Constants.CIIM_ROLE_PREFIX + role))
								  {
									  CIIMUserJSON lUserJSON = CIIMUserJSON.userHDOtoDTO(lUser,lAuthDetail.getName(),lAuthDetail.getPassword());
									  lReturn.add(lUserJSON);
								  }
							  }
						  }
					  }			   
				  }
			  }
		  }
		  catch (HibernateException he) 
		  {
			  log.error("[firstrun:1] Problem returning users from db", he);
		  } 
		  catch (Exception e) 
		  {
			  log.error("[firstrun:1] Problem returning users", e);
		  } 
		
		  return lReturn;
	  }
	    
	  @Path("/ChangePassword")
	  @POST
	  @Produces({MediaType.TEXT_PLAIN})
	  public String changePassword( 	@FormParam("username") String username,
			  							@FormParam("password_old") String pwd_old,
			  							@FormParam("password_new") String pwd_new,
			  							@FormParam("password_confirm") String pwd_confirm,
			  							@Context HttpServletRequest request)
	  {
		  String lReturn = "Failed to update Password.";
		  
		  if(request != null)
		  {			  
			  try
			  {
				  if(ident_srv.getPassword(username).equalsIgnoreCase(pwd_old))
				  {
					  if(pwd_new.equals(pwd_confirm))
					  {
						  ident_srv.setPasswordAuthentication(username, pwd_new);  
						  lReturn = "Password changed successfully";
					  }
					  else
					  {
						  lReturn += " New passwords entered do not match";
					  }
				  }
				  else
				  {
					  lReturn += " Incorrect current password entered.";
				  }
			  }
			  catch(IdentityServiceException ise)
			  {
				  log.error("Error trying to update password :" + ise);
			  }
		  } 
		  return lReturn;
	  }
	  
	  @Path("/CreateUser")
	  @POST
	  @Produces({MediaType.TEXT_PLAIN})
	  public String createUser( @FormParam("username") String username,
			  					@FormParam("name") String name,
			  					@FormParam("email") String email,
			  					@FormParam("password") String pwd,
			  					@FormParam("password_confirm") String pwd_confirm,
	  							@Context HttpServletRequest request)
	  {
		  String lReturn = "Failed to create user.";
		  
		  if(request != null && (request.isUserInRole(Constants.CIIM_ADMIN_ROLE) || request.isUserInRole("GLOBAL.admin")))
		  {	
			  try
			  {		
				  SystemUserDTO user = ident_srv.findUsersByUsername(username);
	 
				  if(username == null || username.trim().length() == 0)
				  {
					  lReturn += " Username is required.";
				  }
				  else if (user != null) 
				  {
					  lReturn += " Username already in use.";
		          } 
				  else 
				  {
		              // The user identity
		              log.debug("Calling identity service create user...");
		          
		              user = new SystemUserDTO(username, username, name, email, null);
		              
		              //sanity check on passwords entered
		              if(pwd.equals(pwd_confirm))
		              {
		            	  Long lUserId = ident_srv.registerUser(user, pwd);
		              
		            	  ident_srv.addRole(username, Constants.CIIM_DEFAULT_ROLE , false);
			              
		            	  if(lUserId != null)
		            	  {
		            		  lReturn = "Successfully registered new user.";
		            	  }
		              }
		              else
		              {
		            	  lReturn += " Passwords entered do not match.";
		              }
		          }		
			  }
			  catch(IdentityServiceException ise)
			  {
				  log.error("Error trying to create user :" + ise);
			  }
		  }
		  return lReturn;
	  }
	  
	  @Path("/UpdateUser")
	  @POST
	  @Produces({MediaType.TEXT_PLAIN})
	  public String updateUser( @FormParam("username") String username,
			  					@FormParam("name") String name,
			  					@FormParam("email") String email,
			  					@FormParam("password") String pwd,
			  					@FormParam("password_confirm") String pwd_confirm,
	  							@Context HttpServletRequest request)
	  {
		  String lReturn = "Failed to update user.";
		  
		  if(request != null && (request.isUserInRole(Constants.CIIM_ADMIN_ROLE) || request.isUserInRole("GLOBAL.admin")))
		  {	  
			  try
			  {		
				  SystemUserDTO user = ident_srv.findUsersByUsername(username);
	 
				  if (user == null) 
				  {
					  lReturn += " User (" + username + ") does not exist.";
		          } 
				  else 
				  {				  
					  if(pwd.equals(pwd_confirm))
					  {
						  ident_srv.setPasswordAuthentication(username, pwd);
						  
						  user.setEmail(email);
						  user.setName(name);
						  
						  ident_srv.update(user);
						  
						  lReturn = "Successfully updated user.";
					  }
					  else
					  {
						  lReturn += " Passwords entered do not match.";
					  }
		          }		
			  }
			  catch(IdentityServiceException ise)
			  {
				  log.error("Error trying to update user :" + ise);
			  }
		  }
		  return lReturn;
	  }
	  
	  @Path("/AddRole")
	  @POST
	  @Produces({MediaType.APPLICATION_JSON})
	  public CIIMUserJSON addRole( 	@FormParam("username") String username,
			  				 		@FormParam("role") String role,
		  							@Context HttpServletRequest request)
	  {
		  CIIMUserJSON lReturn = null;
		  
		  if(request != null && (request.isUserInRole(Constants.CIIM_ADMIN_ROLE) || request.isUserInRole("GLOBAL.admin")))
		  {				  
			  try
			  {		
				  SystemUserDTO user = ident_srv.findUsersByUsername(username);
	 
				  if (user != null) 
				  {	
					  if(role != null && role.trim().length() > 0)
					  {
						  boolean lSuccess = ident_srv.addRole(username, Constants.CIIM_ROLE_PREFIX + role , false);
							  
						  if(lSuccess)
						  {
							  lReturn = this.getUser(username);
						  }
					  }
		          }		
			  }
			  catch(IdentityServiceException ise)
			  {
				  log.error("Error trying to add role :" + ise);
			  }
		  }
		  return lReturn;
	  }
	  
	  @Path("/RemoveRole")
	  @POST
	  @Produces({MediaType.APPLICATION_JSON})
	  public CIIMUserJSON removeRole( 	@FormParam("username") String username,
			  							@FormParam("role") String role,
			  							@Context HttpServletRequest request)
	  {
		  CIIMUserJSON lReturn = null;
		  
		  if(request != null && (request.isUserInRole(Constants.CIIM_ADMIN_ROLE) || request.isUserInRole("GLOBAL.admin")))
		  {	
			  try
			  {		
				  SystemUserDTO user = ident_srv.findUsersByUsername(username);
	 
				  if (user != null) 
				  {	
					  if(role != null && role.trim().length() > 0)
					  {
						  boolean lSuccess = ident_srv.removeRole(username, Constants.CIIM_ROLE_PREFIX + role);
						  
						  if(lSuccess)
						  {
							  lReturn = this.getUser(username);
						  }
					  }
		          }		
			  }
			  catch(IdentityServiceException ise)
			  {
				  log.error("Error trying to add role :" + ise);
			  }
		  }
		  return lReturn;
	  }
	  
	  @Path("/AddEditor")
	  @POST
	  @Produces({MediaType.APPLICATION_JSON})
	  public List<CIIMUserJSON> addEditor( 	@FormParam("gid") String gid,
						  					@FormParam("editors") String editor,
						  					@Context HttpServletRequest request)
	  {
		  List<CIIMUserJSON> lReturn = new ArrayList<CIIMUserJSON>();
		  
		  if(request != null && (request.isUserInRole(Constants.CIIM_ADMIN_ROLE) || request.isUserInRole("GLOBAL.admin")))
		  {	
			  try
			  {	
				  //get ID
				  Long user_id = ident_srv.usernameToId(editor);
				    
				  if(user_id != null)
				  {
					  boolean lSuccess = ident_srv.grant(user_id, Constants.CIIM_EDIT_GROUP_PERMISSION, new TargetObjectIdentifier(Constants.TCN_CIIM_CURATED_GROUP, gid));
					  
					  if(lSuccess)
					  {
						  lReturn = this.getAssignedEditors(gid);
					  }
				  }
			  }
			  catch(IdentityServiceException ise)
			  {
				  log.error("Error trying to add role :" + ise);
			  }	  
		  }
		  
		  return lReturn;
	  }
	  
	  @Path("/RemoveEditor")
	  @POST
	  @Produces({MediaType.APPLICATION_JSON})
	  public List<CIIMUserJSON> removeEditor( 	@FormParam("gid") String gid,
						  						@FormParam("editor") String editor,
						  						@Context HttpServletRequest request)
	  {
		  List<CIIMUserJSON> lReturn = new ArrayList<CIIMUserJSON>();
		  
		  if(request != null && (request.isUserInRole(Constants.CIIM_ADMIN_ROLE) || request.isUserInRole("GLOBAL.admin")))
		  {	
			  try
			  {	
				  //get ID
				  Long user_id = ident_srv.usernameToId(editor);
				  
				  if(user_id != null)
				  {
					  boolean lSuccess = ident_srv.revoke(user_id, Constants.CIIM_EDIT_GROUP_PERMISSION, new TargetObjectIdentifier(Constants.TCN_CIIM_CURATED_GROUP, gid));
					  
					  if(lSuccess)
					  {
						  lReturn = this.getAssignedEditors(gid);
					  }
				  } 
			  }
			  catch(IdentityServiceException ise)
			  {
				  log.error("Error trying to add role :" + ise);
			  }	  
		  }
		  
		  return lReturn;
	  }
	  
	  @Path("/GetAssignedEditors")
	  @POST
	  @Produces({MediaType.APPLICATION_JSON})
	  public List<CIIMUserJSON> getAssignedEditors( 	@FormParam("gid") String gid	)
	  {
		  Session session = null;
		  
		  List<CIIMUserJSON> lReturn = new ArrayList<CIIMUserJSON>();
		  
		  try 
		  {
			  session = SessionFactoryUtils.getSession(factory, true);
			  
			  Query q = session.createQuery("Select y from com.k_int.svc.identity.datamodel.AuthenticationDetailsHDO y, com.k_int.svc.identity.datamodel.GrantHDO x where y.user.id = x.party.id and x.keys[0] = ?");
			  q.setParameter(0, gid);
			  
			  List<AuthenticationDetailsHDO> lResults = (List<AuthenticationDetailsHDO>) q.list();
			    
			  if(lResults != null)
			  {			  
				  for(AuthenticationDetailsHDO lUser : lResults)
				  {
					  lReturn.add(CIIMUserJSON.userHDOtoDTO(lUser.getUser(), lUser.getUsername() , lUser.getPassword()));
				  }		  
			  }
		  }
		  catch (HibernateException he) 
		  {
			  log.error("[firstrun:1] Problem returning users from db", he);
		  } 
		  catch (Exception e) 
		  {
			  log.error("[firstrun:1] Problem returning users", e);
		  } 
		
		  return lReturn;
	  }
	  
	  @Path("/CheckPermissions")
	  @POST
	  @Produces({MediaType.TEXT_PLAIN})
	  public String checkPermissions(	@FormParam("gid") String gid,
										@Context HttpServletRequest request)
	  {
		  String lReturn = null;
		  
		  CIIMDataManager cdm = (CIIMDataManager) ctx.getBean("CIIMDataManager");
		  
		  boolean hasPermissions = cdm.userHasPermissions(request, gid);
		  
		  if(hasPermissions == false)
		  {
			  lReturn = "You do not have the right user permissions to modify this group.";
		  }
		  
		  return lReturn;
	  }
	  
	  @Path("/disableUser")
	  @POST
	  public void disableUser(@FormParam("id") Long id)
	  {
		  IdentityServiceWrapper.disableUser(id, factory);
	  }
	  
	  @Path("/enableUser")
	  @POST
	  public void enableUser(@FormParam("id") Long id)
	  {
		  IdentityServiceWrapper.enableUser(id, factory);
	  }
}
