/********************************************************************
 * Copyright © 2004 VHA. All rights reserved
 ********************************************************************/
package gov.va.med.esr.ui.admin.action;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.Validate;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionMessage;

import gov.va.med.fw.model.UserPrincipalImpl;
import gov.va.med.fw.persistent.MaxRecordsExceededException;
import gov.va.med.fw.security.SecurityContext;
import gov.va.med.fw.security.SecurityContextHelper;
import gov.va.med.fw.security.UserPrincipal;
import gov.va.med.fw.service.ServiceOptimisticLockException;
import gov.va.med.fw.ui.struts.AbstractForm;
import gov.va.med.fw.util.StringUtils;

import gov.va.med.esr.common.model.lookup.Capability;
import gov.va.med.esr.common.model.lookup.FunctionalGroup;
import gov.va.med.esr.common.model.lookup.VAFacility;
import gov.va.med.esr.common.model.security.CapabilitySet;
import gov.va.med.esr.common.model.security.ESRRolePrincipalImpl;
import gov.va.med.esr.common.model.security.ESRUserPrincipalImpl;
import gov.va.med.esr.common.model.security.UserCapability;

import gov.va.med.esr.service.UserAdminService;

import gov.va.med.esr.ui.common.beans.EntityHistory;
import gov.va.med.esr.ui.common.beans.FieldHistoryInfo;
import gov.va.med.esr.ui.common.beans.FormattingTableRow;
import gov.va.med.esr.ui.common.util.DateUtils;

public class UserProfileAction extends AbstractUserProfileAction {

    public static final String EDIT_USER_PROFILE = "editUserProfile";
    public static final String SEARCH_USER_PROFILE = "searchUserProfile";
    public static final String DISPLAY_ALL_USERS = "displayAll"; 
    public static final String ATT_USER_PROFILE = "USER_PROFILE";
    public static final String MSG_FG_ARE_DIFFERENT ="error.functionalGroupisDifferent";
    public static final String MSG_EMPTY_RESULT_SET = "error.emptyResultSet";
    public static final String MSG_MULTIPLE_RESULTS = "error.multipleResults";
    public static final String ATT_USER_LIST = "USER_LIST";
    public static final String PAR_USER_NAME = "userName";
    public static final String MSG_CAPABILITY_REQUIRES_ROLE = "error.capabilityRequiresRole";

    private UserAdminService userAdminService = null;
    
    public UserProfileAction() {
        super();
    }
    
    public UserAdminService getUserAdminService() {
        return userAdminService;
    }

    public void setUserAdminService(UserAdminService userAdminService) {
        this.userAdminService = userAdminService;
    }

    /**
     * display search screen
     * @param mapping
     * @param form
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    public ActionForward display(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response) throws Exception
    {
        //clear previous user
        clearSessionData(request);
        return mapping.findForward(SEARCH_USER_PROFILE);
        
    }
    public ActionForward displayAll(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response) throws Exception
    {
        //clear previous user
        clearSessionData(request);
        //get list of all users (userid, display name only)
        List list = getUserAdminService().findAllUsers();
        request.setAttribute(ATT_USER_LIST,list);
        
        return mapping.findForward(DISPLAY_ALL_USERS);
        
    }
    public ActionForward view(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response) throws Exception
    {
       //clear previous user
        clearSessionData(request);
        //get list of all users (userid, display name only)
        String userName = request.getParameter(PAR_USER_NAME);
        if (StringUtils.isNotEmpty(userName)) {
            //get user and forward to edit
            ESRUserPrincipalImpl esrUser = getUserAdminService().getUserByName(userName);
            if (esrUser != null) {            	
                //place the pristine entity in the session
                setPristineEntity(request,esrUser);
                return mapping.findForward(EDIT_USER_PROFILE);
            }
        }
        return displayAll(mapping,form,request,response);       
    }        
    /**
     * search and forward to edit screen if user is found
     * @param mapping
     * @param form
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    public ActionForward search(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response) throws Exception
    {
        UserForm userForm = (form instanceof UserForm) ? (UserForm)form : null; 
        Validate.notNull(userForm,"form is not configured for User Profile Search Action");
        
        ActionForward forward = null;
        clearSessionData(request); 
        //find the user and forward to edit screen
        UserPrincipalImpl user = new UserPrincipalImpl();
        convert(userForm,user);
        
        //getAdmin Service and search for user
        try {
            ESRUserPrincipalImpl esrUser = getUserAdminService().findUser(user);
            if (esrUser == null) {
                //set error message no matching records found
                addActionMessage(request,MSG_EMPTY_RESULT_SET);
                forward = mapping.getInputForward();
            }
            else {
                //Check whether the current user has permissions to update the user
                setPristineEntity(request,esrUser);
                forward = mapping.findForward(EDIT_USER_PROFILE);
            }
            
        }catch (MaxRecordsExceededException mre) {
            
            //set action error message
            addActionMessage(request,MSG_MULTIPLE_RESULTS);
            forward = mapping.getInputForward();
        }        
        return forward;
    }

    /** convert and forward to edit screen
     * 
     * @param mapping
     * @param form
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    public ActionForward edit(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response) throws Exception
    {
        //Retrieve the user id or the User object from the Request
        ESRUserPrincipalImpl esrUser = (ESRUserPrincipalImpl) getPristineEntity(request);
        setUpdatedEntity(request,null);
        
        if (esrUser == null) {
            //forward back to search page
            return mapping.findForward(SEARCH_USER_PROFILE);
        }

        //Convert the Role to the form
        conversionService.convert(esrUser,form);
                
        populateReferenceData(request); 
        AbstractForm absForm = (AbstractForm)form;
        absForm.setReadOnly(!isAllowedtoEdit(esrUser));
        return mapping.findForward(EDIT_USER_PROFILE);        
    }
    /**
     * convert and forward to confirmation action
     * @param mapping
     * @param form
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    public ActionForward update(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response) throws Exception
    {
        //validate from
        UserProfileForm userProfileForm = (UserProfileForm) form;
        
        ESRUserPrincipalImpl pristine = (ESRUserPrincipalImpl) getPristineEntity(request);
        ESRUserPrincipalImpl updated = (ESRUserPrincipalImpl) pristine.clone();
         
        //convert
        conversionService.convert(userProfileForm, updated);
        setUpdatedEntity(request,updated);
        
        //Check for required roles for assiging capabilities 
        if (checkRequiredRoles(request, updated)) {
            //Forward to confirmation 
            return confirmation(mapping,form,request,response); 
        }else
        {
            return revise(mapping,form,request,response);
        }
    }

    /**
     * update user profile
     * @param mapping
     * @param form
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    public ActionForward accept(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response) throws Exception
    {
        //Get the updated capability set from the session
        ESRUserPrincipalImpl user = (ESRUserPrincipalImpl)getUpdatedEntity(request);
        
        //update
        getUserAdminService().updateUserProfile(user);
        //clear session data
        clearSessionData(request);
        return mapping.findForward(FWD_DISPLAY_UPDATE_MSG);
    }
    /**
     * forward back to edit screen to revise
     * @param mapping
     * @param form
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    public ActionForward revise(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response) throws Exception
    {
        //Retrive the capability set from the session and forward to edit screen
        ESRUserPrincipalImpl user = (ESRUserPrincipalImpl)getUpdatedEntity(request);        
        conversionService.convert(user,form);
        populateReferenceData(request); 
        return mapping.findForward(EDIT_USER_PROFILE);
    }

    /**
     * discard the changes and forward to search screen
     * @param mapping
     * @param form
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    public ActionForward discard(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response) throws Exception
    {
        //return the display list discarding all changes
        clearSessionData(request);
        return mapping.findForward(SEARCH_USER_PROFILE);
    }
    
    /**
     * discard the changes and forward back to edit screen
     */
    public ActionForward clear(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response) throws Exception
    {
        //clear from search action
        if (form instanceof UserForm) {
            return super.clear(mapping,form,request,response);
        }
        //clear from edit screen
        else {
            return edit(mapping, form, request, response);
        }
    }
    
    public void afterPropertiesSet() throws Exception {
        // TODO Auto-generated method stub
    }
    
    private boolean isAllowedtoEdit(ESRUserPrincipalImpl userProfile) {
        SecurityContext securityContext = SecurityContextHelper.getSecurityContext();
        ESRUserPrincipalImpl user = (ESRUserPrincipalImpl)securityContext.getUserPrincipal();
        FunctionalGroup userProfileFG = userProfile.getFunctionalGroup();
        FunctionalGroup loggedinUserFG = user.getFunctionalGroup();
        VAFacility userProfileFacility = userProfile.getFacility();
        VAFacility loggedinUserFacility = user.getFacility();
        
        if (isPermissionGranted(Capability.EDIT_USER_PROFILE_INFO.getName())) {
        	
	        //Administrator can edit all profiles
	        if (isPermissionGranted(Capability.ADMINISTRATOR.getName())) {
	        	return true;
	        }
	        
	        //Local Administrator can edit all users in the same facility
	        if (isPermissionGranted(Capability.LOCAL_ADMINISTRATOR.getName())){
	        	if (userProfileFacility != null && loggedinUserFacility != null) {
	            	if (userProfileFacility.getCode().equals(loggedinUserFacility.getCode())){
	            		return true;
	            	}
	        	}
	        }
	        
	        //Same functional group (HEC Users)
	        if (userProfileFG != null && loggedinUserFG != null) {
	            if (loggedinUserFG.getCode().equals(userProfileFG.getCode())) {
	            	return true;
	            }
	        }
        }
        return false;
    }
    private void convert(UserForm form, UserPrincipalImpl user) {
        user.setGivenName(form.getGivenName());
        user.setFamilyName(form.getFamilyName());
        
        //Optional Fields
        if (StringUtils.isNotEmpty(form.getMiddleName()))
            user.setMiddleName(form.getMiddleName());
        
        if (StringUtils.isNotEmpty(form.getName()))
        user.setName(form.getName());
    }
        
    /**
     * populate form with confirmation data
     * @param mapping
     * @param form
     * @param request
     * @param response
     * @return
     */
    protected ActionForward confirmation(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response) {
        //convert to confirmation properties
        ESRUserPrincipalImpl pristine = 
            (ESRUserPrincipalImpl) getPristineEntity(request);
        ESRUserPrincipalImpl current = 
            (ESRUserPrincipalImpl) getUpdatedEntity(request);
        
        List propList = new ArrayList ();
        
        //User Attributes (name) and Functional Group
        propList.add(new FieldHistoryInfo(
            "label.userName",current.getFullName(),pristine.getFullName()));
        propList.add(new FieldHistoryInfo(
            "label.functionalGroup", 
            current.getFunctionalGroup() == null ? "" : current.getFunctionalGroup().getDescription(),
            pristine.getFunctionalGroup() == null ? "" : pristine.getFunctionalGroup().getDescription()));
        //roles        
        List list = matchSets(current.getUserRoles(),pristine.getUserRoles());
        propList.addAll(processRoles(list));
        //Capability Sets
        list = matchSets(current.getCapabilitySets(),pristine.getCapabilitySets());
        propList.addAll(processCapabilitySets(list));
        //Capabilities
        list = matchSets(current.getUserCapabilities(),pristine.getUserCapabilities());
        propList.addAll(processCapabilities(list));
        
        request.setAttribute("changeList",propList);
        request.setAttribute("confirmationActionName","editUserProfile");
        request.setAttribute("confirmationEntityId",current.getEntityKey().getKeyValueAsString());        
        request.setAttribute("confirmationTitle","label.editUserProfile");

        return mapping.findForward(FWD_CONFIRM);
    }
    
    /**
     * process capabilities for display in the confirmation screen
     */
    protected  List processCapabilities(List capabilityList){
        List filedHistoryList = new ArrayList ();
        if (capabilityList != null && capabilityList.size() > 0) {
            FormattingTableRow infoBean = new FormattingTableRow("label.assignedCapabilities");
            filedHistoryList.add(infoBean);            
            
            for (Iterator i=capabilityList.iterator(); i.hasNext();) {
                EntityHistory entityHistory = (EntityHistory)i.next();
                UserCapability current = (UserCapability) entityHistory.getCurrent();
                UserCapability previous = (UserCapability) entityHistory.getPrevious();
                String curValue = (current == null ? "" : current.getCapability().getName());
                String oldValue = (previous == null ? "" : previous.getCapability().getName());
                filedHistoryList.add(new FieldHistoryInfo("label.name",curValue,oldValue));
                filedHistoryList.add(new FieldHistoryInfo("label.activeDate",
                    (current == null ? "" : DateUtils.format(current.getActiveDate(),null)),
                    (previous == null ? "" : DateUtils.format(previous.getActiveDate(),null))));
                    
                filedHistoryList.add(new FieldHistoryInfo("label.inactivedate",
                   (current == null ? "" : DateUtils.format(current.getInactiveDate(),null)),
                   (previous == null ? "" : DateUtils.format(previous.getInactiveDate(),null)))); 
            }
        }
        return filedHistoryList;
    }
    
    protected Map getKeyMethodMap()
    {
        Map map = new HashMap();
        map.put("button.display", "display");
        map.put("button.displayAll", "displayAll");
        map.put("button.view", "view");
        map.put("button.update", "update");
        map.put("button.search", "search");
        map.put("button.cancel", "cancel");
        map.put("button.clear", "clear");
        map.put("button.edit", "edit");
        map.put("button.revise", "revise");
        map.put("button.discardChanges", "discard");
        map.put("button.acceptChanges", "accept");                    
        return map;
    }
    
    private boolean checkRequiredRoles(HttpServletRequest request, ESRUserPrincipalImpl user) throws Exception{
        boolean valid = true;
        //get assigned Capabilities, CapabilitySets and Roles for filtering the available list
        Set assignedCapabilities = user.getCapabilities();
        Set assignedCapabilitySets = user.getCapabilitySets();
        Set assignedRoles = user.getUserRoles();
        Set assignedRoleNames = new HashSet();
        //assigned sets and capabilities of roles
        for (Iterator r=assignedRoles.iterator(); r.hasNext();){
            ESRRolePrincipalImpl role = (ESRRolePrincipalImpl)r.next();
            assignedCapabilitySets.addAll(role.getCapabilitySets());
            assignedCapabilities.addAll(role.getCapabilities());
            assignedRoleNames.add(role.getName());
        }
        //assigned capabilities of sets
        for (Iterator c=assignedCapabilitySets.iterator(); c.hasNext();){
            CapabilitySet cset = (CapabilitySet)c.next();
            assignedCapabilities.addAll(cset.getCapabilities());
        }
        //check for required roles for the assigned Capability
        Map capRoleMap = getUserAdminService().getCapabilityPermissionMap();
        for (Iterator c=assignedCapabilities.iterator(); c.hasNext();){
            Capability capbility = (Capability)c.next();
            String requiredRoleName = (String) capRoleMap.get(capbility.getCode());
            if (requiredRoleName == null || assignedRoleNames.contains(requiredRoleName)){
                continue;
            }else {
            valid = false;
            addActionMessage(request, new ActionMessage(
                MSG_CAPABILITY_REQUIRES_ROLE, capbility.getDescription(), requiredRoleName));
            }
        }        
        return valid;
    }
    protected void processOptimisticLockException(HttpServletRequest request,
            ServiceOptimisticLockException ex) throws Exception
    {
            // Get the updated Person since the original one is out-dated.
            addActionMessage(request, OPTIMISTC_LOCK_ERROR_KEY);
    }

    protected ActionForward handleOptimisticLockException(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
        ESRUserPrincipalImpl pristine = (ESRUserPrincipalImpl) getPristineEntity(request);
        if (pristine != null){
            ESRUserPrincipalImpl esrUser = getUserAdminService().getUserByName(pristine.getName());
            if (esrUser != null) {              
                //place the pristine entity in the session
                setPristineEntity(request,esrUser);
                return edit(mapping, form, request, response);
            }
        }
        return display(mapping, form, request, response);
    }     
}
