/*
 * The Kuali Financial System, a comprehensive financial management system for higher education.
 *
 * Copyright 2005-2016 The Kuali Foundation
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.kuali.kfs.module.tem.businessobject.lookup;

import org.apache.commons.lang.StringUtils;
import org.kuali.kfs.kns.util.FieldUtils;
import org.kuali.kfs.kns.util.KNSGlobalVariables;
import org.kuali.kfs.kns.web.struts.form.KualiDocumentFormBase;
import org.kuali.kfs.kns.web.struts.form.KualiForm;
import org.kuali.kfs.kns.web.struts.form.LookupForm;
import org.kuali.kfs.krad.service.SessionDocumentService;
import org.kuali.kfs.krad.util.GlobalVariables;
import org.kuali.kfs.module.tem.TemConstants;
import org.kuali.kfs.module.tem.businessobject.TemProfile;
import org.kuali.kfs.module.tem.businessobject.TravelerProfileForLookup;
import org.kuali.kfs.module.tem.identity.TemKimAttributes;
import org.kuali.kfs.sys.KFSConstants;
import org.kuali.kfs.sys.context.SpringContext;
import org.kuali.kfs.sys.identity.KfsKimAttributes;
import org.kuali.rice.kew.api.WorkflowDocument;
import org.kuali.rice.kim.api.KimConstants;
import org.kuali.rice.kim.api.permission.PermissionService;
import org.kuali.rice.krad.bo.BusinessObject;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Overridden to filter results to only those available to the currently logged in user to use.  This will be used within TA, TR, ENT, and RELO
 * document traveler lookups - NOT the non-document TemProfile lookup
 */
public class TravelerProfileDocLookupableHelperServiceImpl extends TemProfileLookupableHelperServiceImpl {
    protected PermissionService permissionService;

    /**
     * Filters searched for profiles so they include only those the user should be able to access to use as a traveler on a travel, relocation, or entertainment document
     *
     * @see org.kuali.kfs.module.tem.businessobject.lookup.TemProfileLookupableHelperServiceImpl#getSearchResults(java.util.Map)
     */
    @Override
    public List<? extends BusinessObject> getSearchResults(Map<String, String> fieldValues) {
        final Map<String, String> viewRecordPermissionDetails = getPermissionDetailsForViewRecordsCheck();
        final String currentUserPrincipalId = GlobalVariables.getUserSession().getPrincipalId();
        final String documentTypeName = updateAuthorizationDocumentType(getCurrentDocumentTypeName());

        final List<TemProfile> allProfiles = (List<TemProfile>) super.getSearchResults(fieldValues);
        List<TemProfile> qualifyingProfiles = new ArrayList<TemProfile>();
        for (TemProfile profile : allProfiles) {
            final Map<String, String> roleQualifier = getRoleQualifierForViewRecordsCheck(profile, documentTypeName);
            if (getPermissionService().isAuthorizedByTemplate(currentUserPrincipalId, KFSConstants.PermissionTemplate.VIEW_RECORD.namespace, KFSConstants.PermissionTemplate.VIEW_RECORD.name, viewRecordPermissionDetails, roleQualifier)) {
                qualifyingProfiles.add(profile);
            }
        }
        return qualifyingProfiles;
    }

    /**
     * @return the PermissionDetails map to do permission checks on TravelerProfileForLookup records
     */
    protected Map<String, String> getPermissionDetailsForViewRecordsCheck() {
        Map<String, String> permissionDetails = new HashMap<String, String>();
        permissionDetails.put(KimConstants.AttributeConstants.COMPONENT_NAME, TravelerProfileForLookup.class.getSimpleName());
        return permissionDetails;
    }

    /**
     * Pulls the principal id, home chart, and home organization codes from the profile to build a role qualifier
     *
     * @param profile the profile to pull qualifications from
     * @return a role qualifier with attribute values from the profile
     */
    protected Map<String, String> getRoleQualifierForViewRecordsCheck(TemProfile profile, String documentTypeName) {
        Map<String, String> roleQualifier = new HashMap<String, String>();
        roleQualifier.put(KfsKimAttributes.CHART_OF_ACCOUNTS_CODE, profile.getHomeDeptChartOfAccountsCode());
        roleQualifier.put(KfsKimAttributes.ORGANIZATION_CODE, profile.getHomeDeptOrgCode());
        roleQualifier.put(KfsKimAttributes.PROFILE_PRINCIPAL_ID, profile.getPrincipalId());
        roleQualifier.put(TemKimAttributes.CUSTOMER_PROFILE_ID, profile.getCustomerNumber());
        roleQualifier.put(TemKimAttributes.PROFILE_ID, profile.getProfileId().toString());

        if (!StringUtils.isBlank(documentTypeName)) {
            roleQualifier.put(KimConstants.AttributeConstants.DOCUMENT_TYPE_NAME, documentTypeName);
        }

        return roleQualifier;
    }

    /**
     * Looks in the form from KNSGlobalVariables to try to figure out what the document type of the current document is
     *
     * @return
     */
    protected String getCurrentDocumentTypeName() {
        final KualiForm form = KNSGlobalVariables.getKualiForm();
        if (form != null) {
            if (form instanceof KualiDocumentFormBase) {
                return ((KualiDocumentFormBase) KNSGlobalVariables.getKualiForm()).getDocTypeName();
            } else if (form instanceof LookupForm) {
                final String docNum = ((LookupForm) KNSGlobalVariables.getKualiForm()).getDocNum();
                if (!StringUtils.isBlank(docNum)) {
                    WorkflowDocument workflowDocument = SpringContext.getBean(SessionDocumentService.class).getDocumentFromSession(GlobalVariables.getUserSession(), docNum);
                    return workflowDocument.getDocumentTypeName();
                }
            }
        }
        return null;
    }

    /**
     * If the passed in document type represents one of the travel authorization child documents, upgrade to travel authorization document; otherwise return the given document type unmodified
     *
     * @param documentType the document type name to filter
     * @return TA if TAC or TAA is passed in as the document type, otherwise the passed in document type
     */
    protected String updateAuthorizationDocumentType(String documentType) {
        if (!StringUtils.isBlank(documentType) && (TemConstants.TravelDocTypes.TRAVEL_AUTHORIZATION_CLOSE_DOCUMENT.equals(documentType) || TemConstants.TravelDocTypes.TRAVEL_AUTHORIZATION_AMEND_DOCUMENT.equals(documentType))) {
            return TemConstants.TravelDocTypes.TRAVEL_AUTHORIZATION_DOCUMENT;
        }
        return documentType;
    }

    /**
     * Overridden to always return TemProfile
     *
     * @see org.kuali.rice.kns.lookup.AbstractLookupableHelperServiceImpl#getBusinessObjectClass()
     */
    @Override
    public Class getBusinessObjectClass() {
        return TemProfile.class;
    }

    /**
     * This lookup only occurs within documents; it should never have a supplemental menu bar
     *
     * @see org.kuali.kfs.module.tem.businessobject.lookup.TemProfileLookupableHelperServiceImpl#getSupplementalMenuBar()
     */
    @Override
    public String getSupplementalMenuBar() {
        return KFSConstants.EMPTY_STRING;
    }

    /**
     * Overrides set rows so that the _rows_ always look at TravelerProfileForLookup as the business object class - we want that lookup,
     * but we want to search for TemProfiles....
     *
     * @see org.kuali.rice.kns.lookup.AbstractLookupableHelperServiceImpl#setRows()
     */
    @Override
    protected void setRows() {
        List<String> lookupFieldAttributeList = null;
        final Class<? extends BusinessObject> businessObjectClazz = TravelerProfileForLookup.class;
        if (getBusinessObjectMetaDataService().isLookupable(businessObjectClazz)) {
            lookupFieldAttributeList = getBusinessObjectMetaDataService().getLookupableFieldNames(
                businessObjectClazz);
        }
        if (lookupFieldAttributeList == null) {
            throw new RuntimeException("Lookup not defined for business object " + getBusinessObjectClass());
        }

        // construct field object for each search attribute
        List fields = new ArrayList();
        try {
            fields = FieldUtils.createAndPopulateFieldsForLookup(lookupFieldAttributeList, getReadOnlyFieldsList(),
                businessObjectClazz);
        } catch (InstantiationException e) {
            throw new RuntimeException("Unable to create instance of business object class" + e.getMessage());
        } catch (IllegalAccessException e) {
            throw new RuntimeException("Unable to create instance of business object class" + e.getMessage());
        }

        int numCols = getBusinessObjectDictionaryService().getLookupNumberOfColumns(businessObjectClazz);

        this.rows = FieldUtils.wrapFields(fields, numCols);
    }

    /**
     * @return the injected implementation KIM's PermissionService
     */
    public PermissionService getPermissionService() {
        return permissionService;
    }

    /**
     * Injects an implementation of KIM's PermissionService
     *
     * @param permissionService an implementation of KIM's PermissionService
     */
    public void setPermissionService(PermissionService permissionService) {
        this.permissionService = permissionService;
    }

}
