/*
 *  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you under the Apache License, Version 2.0 (the
 *  "License"); you may not use this file except in compliance
 *  with the License.  You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 */
package org.apache.isis.extensions.secman.model.facets;

import java.util.stream.Collectors;

import javax.inject.Inject;
import javax.inject.Provider;

import org.springframework.stereotype.Component;

import org.apache.isis.applib.services.inject.ServiceInjector;
import org.apache.isis.applib.services.queryresultscache.QueryResultsCache;
import org.apache.isis.applib.services.registry.ServiceRegistry;
import org.apache.isis.applib.services.user.UserService;
import org.apache.isis.core.metamodel.facetapi.FacetHolder;
import org.apache.isis.core.metamodel.facetapi.FacetUtil;
import org.apache.isis.core.metamodel.facetapi.MetaModelRefiner;
import org.apache.isis.core.metamodel.postprocessors.ObjectSpecificationPostProcessorAbstract;
import org.apache.isis.core.metamodel.progmodel.ProgrammingModel;
import org.apache.isis.core.metamodel.spec.ObjectSpecification;
import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
import org.apache.isis.core.metamodel.spec.feature.ObjectActionParameter;
import org.apache.isis.core.metamodel.spec.feature.ObjectFeature;
import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
import org.apache.isis.extensions.secman.api.tenancy.ApplicationTenancyEvaluator;
import org.apache.isis.extensions.secman.api.user.ApplicationUserRepository;

import lombok.Getter;
import lombok.val;

public class TenantedAuthorizationPostProcessor
        extends ObjectSpecificationPostProcessorAbstract {

    @Component
    public static class Register implements MetaModelRefiner {
        @Override
        public void refineProgrammingModel(ProgrammingModel programmingModel) {
            programmingModel.addPostProcessor(
                    ProgrammingModel.PostProcessingOrder.A2_AFTER_BUILTIN,
                    TenantedAuthorizationPostProcessor.class);
        }
    }

    @Override
    public void doPostProcess(ObjectSpecification objectSpecification) {
        FacetUtil.addFacet(createFacet(objectSpecification.getCorrespondingClass(), objectSpecification));
    }

    @Override
    protected void doPostProcess(ObjectSpecification objectSpecification, ObjectAction act) {
        addFacetTo(objectSpecification, act);
    }

    @Override
    protected void doPostProcess(ObjectSpecification objectSpecification, ObjectAction objectAction, ObjectActionParameter param) {
        // no-op
    }

    @Override
    protected void doPostProcess(ObjectSpecification objectSpecification, OneToOneAssociation prop) {
        addFacetTo(objectSpecification, prop);
    }

    @Override
    protected void doPostProcess(ObjectSpecification objectSpecification, OneToManyAssociation coll) {
        addFacetTo(objectSpecification, coll);
    }

    private void addFacetTo(ObjectSpecification specification, ObjectFeature objectFeature) {
        FacetUtil.addFacet(createFacet(specification.getCorrespondingClass(), objectFeature));
    }


    public static class QueryResultsCacheProviderHolder {
        @Inject @Getter private Provider<QueryResultsCache> queryResultsCacheProvider;
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    private TenantedAuthorizationFacetDefault createFacet(
            final Class<?> cls,
            final FacetHolder holder) {

        val evaluators = serviceRegistry
                .select(ApplicationTenancyEvaluator.class)
                .stream()
                .filter(evaluator -> evaluator.handles(cls))
                .collect(Collectors.<ApplicationTenancyEvaluator>toList());

        if(evaluators.isEmpty()) {
            return null;
        }

        val queryResultsCacheProvider =
                serviceInjector
                .injectServicesInto(new QueryResultsCacheProviderHolder())
                .getQueryResultsCacheProvider();

        return new TenantedAuthorizationFacetDefault(
                        evaluators, applicationUserRepository,
                        queryResultsCacheProvider, userService,
                        holder);
    }

    @Inject ServiceRegistry serviceRegistry;
    @Inject ServiceInjector serviceInjector;
    @Inject UserService userService;
    @Inject ApplicationUserRepository applicationUserRepository;

}

@@ -4681,16 +4681,142 @@
  return 
+serviceRegistry.lookupService(ApplicationUserRepository.class)%0A                .map(userRepository -%3E%0A                        
 new Tena
@@ -4870,16 +4870,24 @@
         
+    
+    
 evaluato
@@ -4890,28 +4890,17 @@
 uators, 
-applicationU
+u
 serRepos
@@ -4906,16 +4906,24 @@
 sitory,%0A
+        
         
@@ -4978,16 +4978,24 @@
 ervice,%0A
+        
         
@@ -5017,16 +5017,47 @@
  holder)
+)%0A                .orElse(null)
 ;%0A    %7D%0A
@@ -5187,73 +5187,8 @@
 ice;
-%0A    @Inject ApplicationUserRepository applicationUserRepository;
 %0A%0A%7D%0A

Revert "ISIS-2619: simplifies implementation of TenantedAuthorizationPostProcessor by just injecting in ApplicationUserRepository."