package riomhaire.lti;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.lang.NonNull;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import riomhaire.lti.core.business.LTIMessageDispatcher;
import riomhaire.lti.core.business.commands.*;
import riomhaire.lti.core.infrastructure.facades.configuration.clientregistration.RedisBasedClientRegistrationResolver;
import riomhaire.lti.core.infrastructure.facades.configuration.tool.RedisBasedToolConfigurationResolver;
import riomhaire.lti.core.model.LTIMessage;
import riomhaire.lti.core.model.LTIMessageType;
import riomhaire.lti.core.model.configuration.ClientConfiguration;
import riomhaire.lti.core.model.configuration.ToolConfiguration;
import riomhaire.lti.core.model.interfaces.ApplicationRegistry;
import riomhaire.lti.core.model.interfaces.CommandDispatcher;
import riomhaire.lti.core.model.interfaces.NoKeyResolver;
import riomhaire.lti.core.model.interfaces.UniKeyResolver;

import java.util.Optional;

/**
 * This is the main entry point for the LTI Tool and well as salient configuration of core important items.
 *
 * In this simplified implementation we have:
 *
 *  [Application Registry] Which contains information about environment and services the commands can execute - everything is in one place.
 *       -
 */
@SpringBootApplication
@EnableWebMvc
@Configuration
public class JavaLti13ToolApplication extends WebMvcConfigurerAdapter {

    /**
     * This is the entry point - eveything starts here
     * @param args command line arguments
     */
    public static void main(String[] args) {
        SpringApplication.run(JavaLti13ToolApplication.class, args);
    }

    /**
     * This builds the implementation of the service which allows us to look up a deployed and registered tool within
     * our system by default we store a configuration within redis ... see src/redis folder for an example
     * @param template this is the template engine which allows us to interact with redis - provided by spring
     * @return ClientRegistrationResolver which allows us to look up a client registration based on certain parameters
     */
    @Bean
    UniKeyResolver<Optional<ClientConfiguration>, String> buildClientRegistrationResolver(RedisTemplate<String, String> template) {
        return new RedisBasedClientRegistrationResolver(template);
    }

    /**
     *
     */
    @Bean
    NoKeyResolver<Optional<ToolConfiguration>> buildToolConfigurationResolver(RedisTemplate<String, String> template) {
        return new RedisBasedToolConfigurationResolver(template,"lti-tool-configuration");
    }

    /**
     * This Spring bean creates the command dispatcher and populates it for the current tool .
     * It is here where you map messageType to command which handles it
     *
     * Currently defined messages are:
     *
     *   - "ProcessOidcRequest"      : This is the command launched when the 'initiate' oidc launch command
     *   - "ProcessLTIMessage"       : LTI Launch-content/deep-link are contained within a JWT.. this command
     *                                 decodes the JWT and delegates to 'LaunchContent', 'SelectDeepLinkContent' etc
     *   - "LaunchContent"           : This is the message associated with launch a basic resource
     *   - "SelectDeepLinkContent"   : This is the message sent to handle 'we need to select content for a deep link'
     *
     * @return the Spring View-Model result of applyings a command to a message
     */
    @Bean
    CommandDispatcher<ModelAndView, ApplicationRegistry<ModelAndView,LTIMessage>, LTIMessage> buildCommandDispatcher() {
        return new LTIMessageDispatcher()
                .addCommand(LTIMessageType.ProcessOidcRequest.name(), new InitiateOidcCommand())
                .addCommand(LTIMessageType.ProcessLTIMessage.name(), new LTILaunchCommand())
                .addCommand(LTIMessageType.LaunchContent.name(), new LaunchContentCommand())
                .addCommand(LTIMessageType.BuildJWKSResponse.name(), new BuildJWKSResponseCommand())
                .addCommand(LTIMessageType.SelectDeepLinkContent.name(), new LaunchCreateDeepLink());

    }

    /**
     * This builds the registry for this tool/application
     *
     * @param commandDispatcher            Dispatches a message to an appropriate command
     * @param clientRegistrationResolver   Look up the configuration for a particular client
     * @param toolConfigurationResolver    Where we can find the config
     * @return Registry            environment links  we execute within
     */
    @Bean
    public Registry buildRegistry(@NonNull CommandDispatcher<ModelAndView, ApplicationRegistry<ModelAndView, LTIMessage>, LTIMessage> commandDispatcher, @NonNull UniKeyResolver<Optional<ClientConfiguration>, String> clientRegistrationResolver, NoKeyResolver<Optional<ToolConfiguration>> toolConfigurationResolver) {
        return new Registry(commandDispatcher, clientRegistrationResolver, toolConfigurationResolver);
    }

    @Override
    /**
     * This adds the locations/handlers for the static application content
     */
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler(
                "/webjars/**",
                "/img/**",
                "/css/**",
                "/js/**")
                .addResourceLocations(
                        "classpath:/META-INF/resources/webjars/",
                        "classpath:/static/img/",
                        "classpath:/static/css/",
                        "classpath:/static/js/");
    }


}
