package org.apereo.cas.web.consent;

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apereo.cas.configuration.CasConfigurationProperties;
import org.apereo.cas.configuration.support.Beans;
import org.apereo.cas.consent.ConsentDecision;
import org.apereo.cas.consent.ConsentEngine;
import org.apereo.cas.consent.ConsentRepository;
import org.apereo.cas.util.Pac4jUtils;
import org.apereo.inspektr.common.spi.PrincipalResolver;
import org.pac4j.core.config.Config;
import org.pac4j.core.context.J2EContext;
import org.pac4j.core.engine.CallbackLogic;
import org.pac4j.core.http.J2ENopHttpActionAdapter;
import org.pac4j.core.profile.ProfileManager;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.async.WebAsyncTask;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.concurrent.Callable;

/**
 * This is {@link CasConsentReviewController}.
 *
 * @author Arnold Bergner
 * @since 5.2.0
 */
@Controller("casConsentReviewController")
@RequestMapping("/consentReview")
@Slf4j
@AllArgsConstructor
public class CasConsentReviewController {

    private static final String CONSENT_REVIEW_VIEW = "casConsentReviewView";
    private static final String CONSENT_LOGOUT_VIEW = "casConsentLogoutView";

    /**
     * The consent repository.
     */
    private final ConsentRepository consentRepository;
    /**
     * The consent engine.
     */
    private final ConsentEngine consentEngine;
    private final Config pac4jConfig;
    private final CasConfigurationProperties casProperties;

    /**
     * Show consent decisions.
     *
     * @param request  the request
     * @param response the response
     * @return the view where json data will be rendered
     */
    @GetMapping
    public String showConsent(final HttpServletRequest request,
                                        final HttpServletResponse response) {
        return CONSENT_REVIEW_VIEW;
    }

    /**
     * Endpoint for getting consent decisions in JSON format.
     *
     * @param request  the request
     * @param response the response
     * @return the consent decisions
     */
    @GetMapping("/getConsentDecisions")
    @ResponseBody
    public WebAsyncTask<Collection<Map<String, Object>>> getConsentDecisions(final HttpServletRequest request,
                                                            final HttpServletResponse response) {
        final Callable<Collection<Map<String, Object>>> asyncTask = () -> {
            final String principal = Pac4jUtils.getPac4jAuthenticatedUsername();
            if (!PrincipalResolver.UNKNOWN_USER.equals(principal)) {
                LOGGER.debug("Fetching consent decisions for principal [{}]", principal);
                final Collection<ConsentDecision> consentDecisions = this.consentRepository.findConsentDecisions(principal);
                LOGGER.debug("Resolved consent decisions for principal [{}]: {}", principal, consentDecisions);
                final Collection<Map<String, Object>> result = new HashSet<>();
                consentDecisions.stream().forEach(d -> {
                    final Map<String, Object> map = new HashMap<>();
                    map.put("decision", d);
                    map.put("attributes", this.consentEngine.resolveConsentableAttributesFrom(d));
                    result.add(map);
                });
                return result;
            }
            return null;
        };
        final long timeout = Beans.newDuration(casProperties.getHttpClient().getAsyncTimeout()).toMillis();
        return new WebAsyncTask<>(timeout, asyncTask);
    }
    
    /**
     * Endpoint for deleting single consent decisions.
     * 
     * @param decisionId the decision id
     * @return true / false
     */
    @PostMapping("/deleteConsentDecision")
    @ResponseBody
    public boolean deleteConsentDecision(@RequestParam final Long decisionId) {
        final String principal = Pac4jUtils.getPac4jAuthenticatedUsername();
        LOGGER.debug("Deleting consent decision with id [{}] for principal [{}].", decisionId, principal);        
        return this.consentRepository.deleteConsentDecision(decisionId, principal);
    }
    
    /**
     * Endpoint for local logout, no SLO.
     * 
     * @param request the request
     * @param response the response
     * @return the logout view
     */
    @GetMapping("/logout")
    public String logout(final HttpServletRequest request, final HttpServletResponse response) {
        LOGGER.debug("Performing Pac4j logout...");
        final ProfileManager manager = Pac4jUtils.getPac4jProfileManager(request, response);
        manager.logout();
        return CONSENT_LOGOUT_VIEW;
    }
    
    /**
     * Endpoint for Cas Client Callback.
     * 
     * @param request the request
     * @param response the response
     */
    @GetMapping("/callback")
    public void callback(final HttpServletRequest request, final HttpServletResponse response) {
        LOGGER.debug("Callback endpoint hit...");
        
        final CallbackLogic logic = this.pac4jConfig.getCallbackLogic();
        final J2EContext context = Pac4jUtils.getPac4jJ2EContext(request, response);
        logic.perform(context, this.pac4jConfig, J2ENopHttpActionAdapter.INSTANCE,
                this.casProperties.getServer().getPrefix().concat("/consentReview"), Boolean.FALSE, Boolean.FALSE);
    }
}

@@ -1091,16 +1091,69 @@
 yncTask;
+%0Aimport org.springframework.web.servlet.ModelAndView;
 %0A%0Aimport
@@ -2284,22 +2284,28 @@
  public 
-String
+ModelAndView
  showCon
@@ -2433,34 +2433,178 @@
     
-return CONSENT_REVIEW_VIEW
+final ModelAndView view = new ModelAndView(CONSENT_REVIEW_VIEW);%0A        view.getModel().put(%22principal%22, Pac4jUtils.getPac4jAuthenticatedUsername());%0A        return view
 ;%0A  

return principal in model (#3158)