package me.chyxion.tigon.webmvc.springboot;

import java.util.List;
import java.util.Locale;
import java.util.Collections;
import java.nio.charset.Charset;
import org.springframework.util.Assert;
import org.springframework.http.MediaType;
import org.springframework.util.PathMatcher;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.CharEncoding;
import org.springframework.context.annotation.Bean;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.LocaleResolver;
import me.chyxion.tigon.webmvc.TigonReturnValueHandler;
import me.chyxion.tigon.webmvc.TigonHandlerInterceptor;
import com.alibaba.fastjson.serializer.SerializerFeature;
import org.springframework.web.servlet.config.annotation.*;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.mvc.WebContentInterceptor;
import org.springframework.web.servlet.i18n.CookieLocaleResolver;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver;
import me.chyxion.tigon.webmvc.formatter.TrimAnnotationFormatterFactory;
import me.chyxion.tigon.webmvc.converter.StringToJSONArrayTypeConverter;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter4;
import me.chyxion.tigon.webmvc.converter.StringToJSONObjectTypeConverter;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import me.chyxion.tigon.webmvc.formatter.EmptyToNullAnnotationFormatterFactory;
import org.springframework.format.number.NumberFormatAnnotationFormatterFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;

/**
 * @author Shaun Chyxion <br>
 * chyxion@163.com <br>
 * Mar 01, 2017 10:35:14
 */
@EnableWebMvc
@Configuration
@EnableAspectJAutoProxy
@ConditionalOnWebApplication
@ImportResource("classpath*:spring/spring-*.xml")
public class TigonWebMvcSpringBootConfiguration extends WebMvcConfigurerAdapter {
    @Autowired(required = false)
    private List<TigonHandlerInterceptor> handlerInterceptors;

    /**
     * {@inheritDoc}
     */
    @Override
    public void addFormatters(FormatterRegistry registry) {
        super.addFormatters(registry);
        // converters
        registry.addConverter(new StringToJSONObjectTypeConverter());
        registry.addConverter(new StringToJSONArrayTypeConverter());

        // formatters
        registry.addFormatterForFieldAnnotation(new NumberFormatAnnotationFormatterFactory());
        registry.addFormatterForFieldAnnotation(new EmptyToNullAnnotationFormatterFactory());
        registry.addFormatterForFieldAnnotation(new TrimAnnotationFormatterFactory());
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        super.addInterceptors(registry);
        registry.addInterceptor(new WebContentInterceptor())
            .excludePathPatterns("/assets/**", "classpath:webapp/assets/**");

        if (handlerInterceptors != null &&
                !handlerInterceptors.isEmpty()) {
            for (TigonHandlerInterceptor handlerInterceptor : handlerInterceptors) {
                final String[] includePathPatterns = handlerInterceptor.includePathPatterns();
                Assert.state(includePathPatterns != null && includePathPatterns.length > 0,
                        "Handler Interceptor 'includePathPatterns' Could Not Be Empty");
                final InterceptorRegistration interceptorRegistration =
                        registry.addInterceptor(handlerInterceptor)
                                .addPathPatterns(includePathPatterns);
                final String[] excludePathPatterns =
                        handlerInterceptor.excludePathPatterns();
                if (excludePathPatterns != null && excludePathPatterns.length > 0) {
                    interceptorRegistration.excludePathPatterns(excludePathPatterns);
                }
                final PathMatcher pathMatcher = handlerInterceptor.pathMatcher();
                if (pathMatcher != null) {
                    interceptorRegistration
                            .pathMatcher(pathMatcher);
                }
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void addReturnValueHandlers(
            List<HandlerMethodReturnValueHandler> returnValueHandlers) {
        returnValueHandlers.add(returnValueHandler());
        super.addReturnValueHandlers(returnValueHandlers);
    }

    @Bean
    public HandlerMethodReturnValueHandler returnValueHandler() {
        return new TigonReturnValueHandler();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        configurer.setUseSuffixPatternMatch(false);
        configurer.setUseRegisteredSuffixPatternMatch(false);
        super.configurePathMatch(configurer);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void configureMessageConverters(
            List<HttpMessageConverter<?>> converters) {
        converters.add(new StringHttpMessageConverter(Charset.forName(CharEncoding.UTF_8)));
        final FastJsonHttpMessageConverter4 fastJsonMessageConverter =
            new FastJsonHttpMessageConverter4();
        fastJsonMessageConverter.setSupportedMediaTypes(
                Collections.singletonList(MediaType.APPLICATION_JSON_UTF8));

        fastJsonMessageConverter.getFastJsonConfig()
            .setSerializerFeatures(
                SerializerFeature.WriteMapNullValue,
                SerializerFeature.WriteNullListAsEmpty,
                SerializerFeature.WriteNullBooleanAsFalse);

        converters.add(fastJsonMessageConverter);
        super.configureMessageConverters(converters);
    }

    @Bean
    public LocaleResolver localeResolver() {
        return new CookieLocaleResolver() {
            private final LocaleResolver acceptHeaderLocaleResolver =
                new AcceptHeaderLocaleResolver();

            /**
             * {@inheritDoc}
             */
            @Override
            public Locale resolveLocale(HttpServletRequest request) {
                final Locale locale = super.determineDefaultLocale(request);
                return locale != null ?
                    locale : acceptHeaderLocaleResolver.resolveLocale(request);
            }
        };
    }
}
