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

// Java Classes
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

// Library Classes
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

// Framework Classes
import gov.va.med.fw.util.StringUtils;
import gov.va.med.fw.service.MultipleRecordsFoundException;
import gov.va.med.fw.service.ServiceOptimisticLockException;
import gov.va.med.fw.security.InvalidPasswordException;
import gov.va.med.fw.security.PasswordChangeNotAllowedException;
import gov.va.med.fw.security.SecurityContext;
import gov.va.med.fw.security.SecurityContextHelper;

// ESR Classes
import gov.va.med.esr.common.model.lookup.Capability;
import gov.va.med.esr.common.model.lookup.VAFacility;
import gov.va.med.esr.common.model.security.ESRUserPrincipalImpl;
import gov.va.med.esr.ui.ApplicationConstants;
import gov.va.med.esr.ui.common.action.AbstractAction;
import gov.va.med.esr.ui.common.action.AbstractLogonAction;

public class UserAdminAction extends AbstractAction {

    // Constants
    public static final String ADD_USER_ACCOUNT = "addUserAccount";
    public static final String EDIT_USER_ACCOUNT = "editUserAccount";
    public static final String VIEW_USER_ACCOUNT = "viewUserAccount";
    public static final String SEARCH_USER_ACCOUNT = "searchUserAccount";
    public static final String USER_ACCOUNT = "userAccount";

    public static final String MSG_NO_PERMISSION_TO_ADD_USER = "error.noPermissionAddUser";
    public static final String MSG_NO_PERMISSION_TO_EDIT_USER = "error.noPermissionEditUser";
    public static final String MSG_USER_ACCOUNT_ADDED = "message.userAccountAdded";
    public static final String MSG_CONST_INAVLID_USERID = "error.invalidUserId";
    public static final String MSG_CONST_MULTIPLE_LDAP_RECORDS_FOUND = "error.multipleRecordsFound";
    public static final String MSG_ACCT_DISABLED = "error.acctDisabled";
    public static final String FLD_CONST_USERID = "name";
    public static final String LABEL_KEY_USERID = "label.userid";
    public static final String MSG_ACCT_NOT_DISABLED = "error.acctNotDisabled";

    // Flag that determines if the data is clean
    private boolean isDataClean = true;

    public UserAdminAction() {
        super();
    }

    /**
     * Displays the search screen without a user having been selected yet.
     *
     * @param mapping An action mapping
     * @param form A form bean
     * @param request A http request from an search.jsp page
     * @param response A http response to stream data to a next page
     *
     * @return An action forward class encapsulating information about a next page
     * @throws Exception if any errors occurred while trying to select the person.
     */
    public ActionForward display(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response) throws Exception
    {
        return mapping.findForward(SEARCH_USER_ACCOUNT);
    }

    /**
     * Performs a search for the user information without an LDAP check.
     *
     * @param mapping An action mapping
     * @param form A form bean
     * @param request A http request from an search.jsp page
     * @param response A http response to stream data to a next page
     *
     * @return An action forward class encapsulating information about a next page
     * @throws Exception if any errors occurred while trying to select the person.
     */
    public ActionForward selectUser(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response) throws Exception
    {
        return searchImpl(mapping, form, request, response, false);
    }

    /**
     * Performs a search for the user information with an LDAP check.
     *
     * @param mapping An action mapping
     * @param form A form bean
     * @param request A http request from an search.jsp page
     * @param response A http response to stream data to a next page
     *
     * @return An action forward class encapsulating information about a next page
     * @throws Exception if any errors occurred while trying to select the person.
     */
    public ActionForward search(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response) throws Exception
    {
        return searchImpl(mapping, form, request, response, true);
    }

    /**
     * Performs a search for the user information with an optional LDAP check.
     *
     * @param mapping An action mapping
     * @param form A form bean
     * @param request A http request from an search.jsp page
     * @param response A http response to stream data to a next page
     * @param performLdapCheck If true, an LDAP check will always be performed.  If false,
     * an LDAP check will not be performed.
     *
     * @return An action forward class encapsulating information about a next page
     * @throws Exception if any errors occurred while trying to select the person.
     */
    protected ActionForward searchImpl(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response, boolean performLdapCheck) throws Exception
    {
        ActionForward forward = null;
        UserForm userForm = (UserForm) form;

        //Reset/clear session data if any
        setPristineEntity(request,null);
        setUpdatedEntity(request,null);

        String userName = userForm.getName();
        if (StringUtils.isEmpty(userName)) {
            //Add Validation erros in the request
            addActionMessageForField(request,
                ApplicationConstants.MessageKeys.ERRORS_REQUIRED,
                    getMessage(request,LABEL_KEY_USERID), FLD_CONST_USERID);
            return mapping.getInputForward();
        }

        //Find if the user already exists
        ESRUserPrincipalImpl userPrincipal = null;

        userPrincipal = getUserAdminService().getUserByName(userName);

        String[] viewPermissions = {
        	Capability.ADD_NEW_USER.getName(),
			Capability.EDIT_USER_ACCOUNT.getName(),
			Capability.ADD_LOCAL_USER.getName(),
			Capability.EDIT_LOCAL_USER.getName(),
			Capability.VIEW_USER_ACCOUNTS.getName()};

    	String[] addPermissions = {Capability.ADD_NEW_USER.getName(),
			Capability.EDIT_USER_ACCOUNT.getName()};

    	String[] addLocalUserPermissions = {Capability.ADD_LOCAL_USER.getName(),
    			Capability.EDIT_LOCAL_USER.getName()};

    	String[] administratorPermissions = {Capability.LOCAL_ADMINISTRATOR.getName(),
    	    Capability.ADMINISTRATOR.getName()};

        try {
            //Modify User
        	if (userPrincipal != null) {

                boolean validNetworkId = true;
                //this method is being reused by two different actions
                if (performLdapCheck)
                {
                    // Perform the LDAP check to determine if the username is a valid network Id
                    validNetworkId = getUserAdminService().isValidNetworkId(userName);
                }

                if (validNetworkId) {
                	//verify whether the user has privileges to edit/view
            		//local administrator facility check
            		if (isPermissionGranted(Capability.EDIT_USER_ACCOUNT.getName()) ||
            				(isPermissionGranted(Capability.EDIT_LOCAL_USER.getName()) &&
            						isAllowedtoEdit(userPrincipal))) {
                        // convert user to form and save the pristine object in session
	                    conversionService.convert(userPrincipal,form);
	                    setPristineEntity(request,userPrincipal);
	                    setUpdatedEntity(request,null);
        				return mapping.findForward(EDIT_USER_ACCOUNT);
            		}

                	//Forward to view screen if user has view capabilities
                	if (isAnyPermissionGranted(viewPermissions)) {
	                    conversionService.convert(userPrincipal,form);
	                    return mapping.findForward(VIEW_USER_ACCOUNT);
                	}
                	//no administration permissions
                	addActionMessage(request,MSG_NO_PERMISSION_TO_EDIT_USER);
    	            return mapping.getInputForward();
                }
                else
                {
                    //disable the userid if the logged in user has privileges
                	if (isPermissionGranted(Capability.DISABLE_USER_ACCOUNT.getName())) {
	                    userPrincipal.setInActiveDate(new Date());
	                    getUserAdminService().updateUserAccount(userPrincipal);
	                    addActionMessageForField(request,MSG_ACCT_DISABLED,FLD_CONST_USERID);
                	}
                	else {
                		addActionMessageForField(request,MSG_ACCT_NOT_DISABLED,FLD_CONST_USERID);
                	}
                	forward = mapping.getInputForward();
                }
	        }
	        //Add User if the userid is a valid network id
	        else {
	            //Use windows ldap service to validate the userid
	            userPrincipal = getUserAdminService().getLDAPUserInfo(userName);

	            if (userPrincipal != null) {

	                //check whether user has permission to add new users
	                if (isAnyPermissionGranted(addPermissions) ||
	                		isAnyPermissionGranted(addLocalUserPermissions)) {
	                    //Pre-Populate the information if required from LDAP info
	                    conversionService.convert(userPrincipal,form);
	                    //If only local user admin permissions are granted
	                    if (!isAnyPermissionGranted(addPermissions))
	                    {
	                    	//local administrator - Get administrator facility
	                    	ESRUserPrincipalImpl currentUser = (ESRUserPrincipalImpl)getLoggedInUser();
	                    	VAFacility facility = currentUser.getFacility();
	                    	if (facility != null)
	                    		((UserForm)form).setFacility(facility.getCode());
	                    }
	                    forward = mapping.findForward(ADD_USER_ACCOUNT);
	                }
	                else
	                {
	                    //return an error message
	                    addActionMessage(request,MSG_NO_PERMISSION_TO_ADD_USER);
	                    forward = mapping.getInputForward();
	                }
	            }
	            //return with validation error message
	            else {
	                //Add Validation erros in the request
	                addActionMessageForField(request,MSG_CONST_INAVLID_USERID,FLD_CONST_USERID);
	                forward = mapping.getInputForward();
	            }
	        }
        }catch (MultipleRecordsFoundException mrfe) {
        	addActionMessageForField(
        	    request,MSG_CONST_MULTIPLE_LDAP_RECORDS_FOUND,FLD_CONST_USERID);
        	forward = mapping.getInputForward();
        }
        return forward;
    }

    /**
     * Update User account, if password has been updated reset password change date
     * @param mapping
     * @param form
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    public ActionForward updateUser(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response) throws Exception
    {
        //Get the user from the session
        ESRUserPrincipalImpl userPrincipal= (ESRUserPrincipalImpl)getPristineEntity(request);
        ESRUserPrincipalImpl updatedUser = (ESRUserPrincipalImpl) userPrincipal.clone();
        conversionService.convert(form,updatedUser);

        UserForm userForm = (UserForm) form;
        try {
            if (StringUtils.isNotEmpty(userForm.getPassword())) {
                getUserAdminService().validatePasswordReuse(userPrincipal, userForm.getPassword());
            }
        } catch (PasswordChangeNotAllowedException pe){
            addActionMessage(request,AbstractLogonAction.MESSAGE_PASSWORD_CHANGE_NOTALLOWED);
            userForm.setPassword(null);
            userForm.setConfirmationPassword(null);
            return mapping.getInputForward();
        } catch (InvalidPasswordException ipe){
            addActionMessage(request,AbstractLogonAction.MESSAGE_PASSWORD_REUSE);
            return mapping.getInputForward();
        }

        //if the password has been modified encrypt and set the new value
        if (StringUtils.isNotEmpty(userForm.getPassword())) {
            updatedUser.setPassword(
                getUserAdminService().encryptPassword(userForm.getPassword()));
            //reset date and count
            updatedUser.setPasswordChangeDate(new Date());
            updatedUser.setFailAttemptCount(0);
        }
        getUserAdminService().updateUserAccount(updatedUser);

        //clear the session data
        clearSessionData(request);

        //forwad back to the search page with message
        return mapping.findForward(FWD_DISPLAY_UPDATE_MSG);
    }

    public ActionForward addUser(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response) throws Exception
    {
        UserForm userForm = (UserForm)form;
        ActionForward forward = mapping.findForward(USER_ACCOUNT);

        ESRUserPrincipalImpl userPrincipal =
            new ESRUserPrincipalImpl(userForm.getName(),ApplicationConstants.SessionData.SM_PASSWORD);
        conversionService.convert(form,userPrincipal);

        //encrypt the password
        userPrincipal.setPassword(
                getUserAdminService().encryptPassword(userPrincipal.getPassword()));

        getUserAdminService().addUserAccount(userPrincipal);
        //Reretrive the user account info for display
        userPrincipal = getUserAdminService().getUserByName(userForm.getName());
        //reset the form and convert again with the new user account object
        userForm.reset(mapping,request);
        conversionService.convert(userPrincipal,userForm);
        addInformationMessage(request,MSG_USER_ACCOUNT_ADDED);
        return forward;
    }

    /**
     * User with edit local users permission can only edit user accounts in that facility
     * @param userProfile
     * @return
     */
    private boolean isAllowedtoEdit(ESRUserPrincipalImpl userProfile) {
        SecurityContext securityContext = SecurityContextHelper.getSecurityContext();
        ESRUserPrincipalImpl user = (ESRUserPrincipalImpl)securityContext.getUserPrincipal();
        VAFacility userProfileSite= userProfile.getFacility();
        VAFacility userLoggedInSite= user.getFacility();
        if (userProfileSite != null && userLoggedInSite != null) {
            return userProfileSite.getCode().equals(userLoggedInSite.getCode());
        }/*else if (userProfileSite == null && userLoggedInSite == null) {
        	return true;
        }*/
        return false;
    }

    /**
     * Cancel returns to Home Page or search User based on the configuration
     * in struts-config.
     *
     * Add line 363 to fix INC000001009151 Phil Axworthy
     *
     * @param mapping
     * @param form
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    public ActionForward cancel(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response) throws Exception
    {
    	clearSessionData(request);
// fix INC000001009151  ESR_3_12_CodeCR12950
    	((UserForm)form).initialize();
        return mapping.findForward(FWD_CANCEL);
    }

    public void afterPropertiesSet() throws Exception {
    }

    public boolean isDataClean()
    {
        return isDataClean;
    }

    public void setIsDataClean(boolean isDataClean)
    {
        this.isDataClean = isDataClean;
    }

    /**
     * Clear the temporary session data of user account
     * @param request
     */
    private void clearSessionData(HttpServletRequest request) {
        //Reset/clear session data if any
        setPristineEntity(request,null);
        setUpdatedEntity(request,null);
    }
    protected Map getKeyMethodMap()
    {
        Map map = new HashMap();
        map.put("button.update", "updateUser");
        map.put("button.add", "addUser");
        map.put("button.search", "search");
        map.put("button.cancel", "cancel");
        map.put("button.display", "display");
        map.put("button.selectPerson", "selectUser");
        return map;
    }

    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 {
        return search(mapping, form, request, response);
    }
}
