package riomhaire.lti.core.business.commands;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.RedirectView;
import riomhaire.lti.core.model.LTIMessage;
import riomhaire.lti.core.model.interfaces.ApplicationRegistry;
import riomhaire.lti.core.model.interfaces.Command;

import java.util.Date;
import java.util.UUID;

/**
 * Defines  execute command on message P within environment E returning T
 *
 *
 * This implementation is for the OIDC Initiation call made by an issuer as part of the security specification.
 */
public class InitiateOidcCommand implements Command<ModelAndView, ApplicationRegistry<ModelAndView,LTIMessage>, LTIMessage> {
    // Logger
    private Logger log = LoggerFactory.getLogger(this.getClass());


    /**
     * THis command handles the initiate OIDC launch sequence
     *
     * @param registry the environment
     * @param message - what message format the command will get
     * @return
     *
     */
    public ModelAndView execute(ApplicationRegistry<ModelAndView,LTIMessage> registry, LTIMessage message) {
        final ModelAndView mav;
        var queryParams = message.getQueryParams();
        String iss = queryParams.get("iss");
        String clientId = queryParams.get("client_id");
        var clientRegistration = registry.clientRegistrationResolver().lookup(String.format("%s%s",iss, clientId));

        if (clientRegistration.isPresent()) {
            // Build Location
            StringBuilder location = new StringBuilder();
            var clientConfiguration = clientRegistration.get();
            var loginHint = queryParams.get("login_hint");
            var ltiMessageHint = queryParams.get("lti_message_hint");
            var targetLinkUri = queryParams.get("target_link_uri");


            location.append(clientConfiguration.getAuthenticateUrl());
            location.append("?");
            location.append("scope=openid&response_type=id_token&client_id=");
            location.append(clientConfiguration.getClientId());
            location.append("&login_hint=");
            location.append(loginHint);

            // This should be used to map to some backend thing we can verify ... jwt etc
            location.append("&state=");
            final String state = UUID.randomUUID().toString();
            location.append(state);

            // This should be used to map to some backend thing we can verify ... as once only
            location.append("&response_mode=form_post&prompt=none&nonce=");
            location.append(UUID.randomUUID().toString());
            location.append("&lti_message_hint=");
            location.append(ltiMessageHint);
            location.append("&redirect_uri=");
            location.append(targetLinkUri);

            log.info("OIDC Redirecting to " + location.toString());
            mav = new ModelAndView(new RedirectView(location.toString()));

        } else {
            String msg = "Unknown Issuer '" + iss + "' and client '" + clientId + "'";
            log.error(msg);
            mav = new ModelAndView("error");
            mav.addObject("timestamp", new Date());
            mav.addObject("error", msg);
            mav.addObject("path", "/oidc");
            mav.addObject("queryParameters", message.getQueryParams());

        }
        return mav;

    }


    /**
     * Default constructor
     */
    public InitiateOidcCommand() {
    }
}
