package ${vhGrpcBuilder_packageName}.${vhGrpcBuilder_sprojectName}.config;

import java.io.IOException;
import java.util.List;

import javax.servlet.DispatcherType;
import javax.servlet.Filter;

import ${vhGrpcBuilder_packageName}.${vhGrpcBuilder_sprojectName}.api.auth.TokenFilter;
import ${vhGrpcBuilder_packageName}.${vhGrpcBuilder_sprojectName}.resolver.CurrentUserArgumentResolver;
import ${vhGrpcBuilder_packageName}.${vhGrpcBuilder_sprojectName}.util.AuthorizationInterceptor;
import ${vhGrpcBuilder_packageName}.${vhGrpcBuilder_sprojectName}.util.CorsFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.core.env.Environment;
import org.springframework.core.io.FileSystemResource;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import org.springframework.web.multipart.support.MultipartFilter;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

import com.google.common.collect.Sets;
import com.viewhigh.vhsc.support.config.ProfileNames;
import com.viewhigh.vhsc.support.spring.ApiResultResponseAdvice;
import com.viewhigh.vhsc.support.spring.RequestLoggingFilter;
import com.viewhigh.vhsc.support.tracer.TraceFilter;

@EnableWebMvc
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {

    @Value("uploadTmpPath")
    private String uploadTmpPath;

    @Value("${jwt.validate.path}")
    private String validatePath;

    @Autowired
    private Environment env;

    @RestControllerAdvice("com.viewhigh.vhsc.api")
    static class ResponseAdvice extends ApiResultResponseAdvice {
    }


    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        // 只有非staging和非prod环境才启用swagger
        if (env.acceptsProfiles(ProfileNames.DEFAULT, ProfileNames.DEV)) {
            registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
            registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
        }
    }

    @Profile({ProfileNames.DEFAULT, ProfileNames.DEV})
    @Bean
    public FilterRegistrationBean corsFilterRegistrationBean() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        registrationBean.setFilter(new CorsFilter());
        registrationBean.setDispatcherTypes(DispatcherType.REQUEST);
        registrationBean.setOrder(1);
        return registrationBean;
    }

    @Bean
    public FilterRegistrationBean traceFilterRegistrationBean() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        registrationBean.setFilter(new TraceFilter());
        registrationBean.setDispatcherTypes(DispatcherType.REQUEST);
        registrationBean.setOrder(2);
        return registrationBean;
    }

    @Bean
    public FilterRegistrationBean loggingFilterRegistrationBean() {
        RequestLoggingFilter filter = new RequestLoggingFilter();
        filter.setIncludeQueryString(true);
        filter.setIncludePayload(true);
        filter.setMaxPayloadLength(500);
        filter.setIncludeHeaders(false);
        filter.setSensitivePaths(Sets.newHashSet(
                "/api1/auth/login",
                "/api1/auth/register",
                "/api1/api/account/user/create",
                "/api1/api/account/user/modifyPwd"));
        filter.setBeforeMessagePrefix("api before request: ");
        filter.setAfterMessagePrefix("api after request: ");
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        registrationBean.setFilter(filter);
        registrationBean.setDispatcherTypes(DispatcherType.REQUEST);
        registrationBean.setOrder(3);
        return registrationBean;
    }

    @Bean
    public FilterRegistrationBean tokenFilterRegistrationBean() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        registrationBean.setFilter(tokenFilter());
        registrationBean.addUrlPatterns(validatePath + "/*");
        registrationBean.setDispatcherTypes(DispatcherType.REQUEST);
        registrationBean.setOrder(4);
        return registrationBean;
    }

    @Bean
    public Filter tokenFilter() {
        return new TokenFilter();
    }

    @Bean
    public DispatcherServlet dispatcherServlet() {
        return new DispatcherServlet();
    }

    @Bean
    public ServletRegistrationBean dispatcherServletRegistration() {
        ServletRegistrationBean registration = new ServletRegistrationBean(dispatcherServlet(), "/*");
        registration.setName("viewhigh-vhsc-dispatcherServlet");
        return registration;
    }

    @Bean
    public AuthorizationInterceptor interceptor() {
        return new AuthorizationInterceptor();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(interceptor()).addPathPatterns(validatePath + "/**");
    }

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        super.addArgumentResolvers(argumentResolvers);
        argumentResolvers.add(currentUserArgumentResolver());
    }

    @Bean
    public CurrentUserArgumentResolver currentUserArgumentResolver() {
        return new CurrentUserArgumentResolver();
    }

    @Bean
    public MethodValidationPostProcessor methodValidationPostProcessor() {
        return new MethodValidationPostProcessor();
    }

    @Bean
    public CommonsMultipartResolver commonsMultipartResolver() throws IOException {
        final CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver();
        commonsMultipartResolver.setMaxUploadSize(-1);
        commonsMultipartResolver.setUploadTempDir(new FileSystemResource(uploadTmpPath));
        commonsMultipartResolver.setMaxUploadSizePerFile(5120000L);
        commonsMultipartResolver.setMaxUploadSize(20480000L);
        return commonsMultipartResolver;
    }

    @Bean
    public FilterRegistrationBean multipartFilterRegistrationBean() {
        final MultipartFilter multipartFilter = new MultipartFilter();
        final FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(multipartFilter);
        filterRegistrationBean.addInitParameter("multipartResolverBeanName", "commonsMultipartResolver");
        return filterRegistrationBean;
    }

}