package riomhaire.lti.core.business.commands;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.jose4j.jwk.JsonWebKey;
import org.jose4j.jwk.JsonWebKeySet;
import org.jose4j.jwk.PublicJsonWebKey;
import org.jose4j.jwk.RsaJsonWebKey;
import org.jose4j.keys.RsaKeyUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;
import riomhaire.lti.core.model.LTIMessage;
import riomhaire.lti.core.model.configuration.JWKKey;
import riomhaire.lti.core.model.interfaces.ApplicationRegistry;
import riomhaire.lti.core.model.interfaces.Command;

import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;

/**
 * The type Build jwks response command.
 */
public class BuildJWKSResponseCommand  implements Command<ModelAndView, ApplicationRegistry<ModelAndView, LTIMessage>, LTIMessage> {
    // Logger
    private Logger log = LoggerFactory.getLogger(this.getClass());

    /**
     * This is the signature a message processing command can implement when invoked by the dispatcher
     *
     * @param environment - usually some form of application registry
     * @param message     - what message format the command will get
     * @return some response
     */
    @Override
    public ModelAndView execute(ApplicationRegistry<ModelAndView, LTIMessage> environment, LTIMessage message) {
        ModelAndView mav;
        var objectMapper = new ObjectMapper();
        try {
            // Step 1 - retrieve config
            var optionalConfiguration =  environment.toolConfigurationResolver().lookup();
            if( optionalConfiguration.isEmpty() ) throw new FileNotFoundException("No Tool Configuration has been set");


            // Step 2 - build jwks compliant data structure
            var webKeys = new ArrayList<RsaJsonWebKey>();
            var toolConfiguration = optionalConfiguration.get();
            var jwkKeys = toolConfiguration.getJwkKeys();
            // Step 2.1 - Convert public key from base64 String and convert to PEM
            // For each known key
            for( JWKKey configKeyEntry : jwkKeys ){
                var pemString =  new String(Base64.getDecoder().decode(configKeyEntry.getPublicKey()));
                var rsaJsonWebKey = (RsaJsonWebKey) PublicJsonWebKey.Factory.newPublicJwk(new RsaKeyUtil().fromPemEncoded(pemString));
                rsaJsonWebKey.setKeyId(configKeyEntry.getKeyId());
                webKeys.add(rsaJsonWebKey);
            }
            // Conveluted  hack
            var jwks = new JsonWebKeySet(webKeys);
            var raw = objectMapper.readValue(jwks.toJson(JsonWebKey.OutputControlLevel.PUBLIC_ONLY), HashMap.class);
            // Step 3 - render as json response

            var view = new MappingJackson2JsonView();
            view.setPrettyPrint(true);
            mav = new ModelAndView(view);
            mav.addAllObjects(raw);

        } catch (Exception ex) {
            log.error(ex.getMessage());
            mav = new ModelAndView("error");
            mav.addObject("timestamp", new Date());
            mav.addObject("error", ex.getMessage());
            mav.addObject("path", "/jwks");
            mav.addObject("queryParameters", message.getQueryParams());

        }

        return mav;
    }
}
