package com.apobates.forum.thrones;

import com.apobates.forum.thrones.controller.helper.StrategyCondition;
import com.apobates.forum.attention.ImageStoreDomain;
import com.apobates.forum.core.ImageIOMeta;
import com.apobates.forum.member.storage.OnlineMemberStorage;
import com.apobates.forum.member.storage.cookie.CookieMetaConfig;
import com.apobates.forum.member.storage.redis.OnlineMemberRedisStorage;
import com.apobates.forum.thrones.controller.helper.AuthenticationInterceptor;
import com.apobates.forum.thrones.controller.helper.MemberInviteCodeInterceptorAdapter;
import com.apobates.forum.thrones.controller.helper.OnlineDescriptorAspect;
import com.apobates.forum.member.storage.redis.RedisMemberStorageCondition;
import com.apobates.forum.thrones.controller.helper.RegisteChannelInterceptor;
import com.apobates.forum.thrones.controller.helper.StrategyInterceptorAdapter;
import com.apobates.forum.thrones.rss.TopicRssView;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.http.MediaType;
import org.springframework.lang.Nullable;
import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
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.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.BeanNameViewResolver;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;

/**
 * 前端Spring MVC配置类
 *
 * @author xiaofanku
 * @since 20200511
 */
@Configuration
@PropertySource(value = "classpath:global.properties", ignoreResourceNotFound = true, encoding = "UTF-8")
@EnableWebMvc
@EnableAspectJAutoProxy(proxyTargetClass=true)
@ComponentScan(
        basePackages = {"com.apobates.forum.thrones.controller"},
        useDefaultFilters = false,
        includeFilters = {
                @Filter(classes = {org.springframework.stereotype.Controller.class}),
                @Filter(classes = {org.springframework.stereotype.Component.class})})
@Import(value = {WebSocketConfig.class})
public class ThronesFrontConfig implements WebMvcConfigurer {
    @Value("${site.cookieSymbol}")
    private String cookieName;
    @Value("${site.cookieDomain}")
    private String cookieDomain;
    @Value("${site.cookiePath}")
    private String cookiePath;
    @Value("${site.domain}")
    private String siteDomain;
    @Value("${img.bucket.domain}")
    private String imageBucketDomain;
    @Value("${img.bucket.upload.direct}")
    private String imageBucketUploadDirect;
    @Value("${site.origin.admin}")
    private String adminDomain;
    
    //授权访问的拦截器
    @Bean
    public AuthenticationInterceptor getAuthInter() {
        return new AuthenticationInterceptor();
    }
    //注册通道检查拦截器
    @Bean
    public RegisteChannelInterceptor getRegisInter() {
        return new RegisteChannelInterceptor();
    }
    //策略检测拦截器,以Env(site.strategy)来控制开启,默认开启
    @Bean
    @Conditional(StrategyCondition.class)
    public StrategyInterceptorAdapter getStrategyInter(){
        return new StrategyInterceptorAdapter();
    }
    //注册邀请码拦截器
    @Bean
    public MemberInviteCodeInterceptorAdapter getInviteCodeInter(){
        return new MemberInviteCodeInterceptorAdapter();
    }
    //会员在线记录 aspectJ 
    @Bean
    public OnlineDescriptorAspect getOnlineAspect() { //在线会员
        return new OnlineDescriptorAspect();
    }
    /**
     * 配置请求视图映射
     *
     * @return
     */
    @Bean
    public InternalResourceViewResolver resourceViewResolver() {
        InternalResourceViewResolver internalResourceViewResolver = new InternalResourceViewResolver();
        //请求视图文件的前缀地址
        internalResourceViewResolver.setPrefix("/WEB-INF/layout/page/");
        //请求视图文件的后缀
        internalResourceViewResolver.setSuffix(".jsp");
        internalResourceViewResolver.setViewClass(JstlView.class);
        return internalResourceViewResolver;
    }
    //静态资源映射
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/**").addResourceLocations("/static/").setCachePeriod(31536000);
    }
    //定义无Controller的path<->view直接映射
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("redirect:/home");
    }
    //RSS
    @Bean(name = "topicRssView")
    public TopicRssView getRssView() {
        return new com.apobates.forum.thrones.rss.TopicRssView();
    }
    //定义视图文件解析View resolvers
    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        BeanNameViewResolver beanResolver = new BeanNameViewResolver();
        beanResolver.setOrder(1);
        registry.viewResolver(beanResolver);
        registry.viewResolver(resourceViewResolver());
        registry.enableContentNegotiation(getRssView());
    }
    //对静态资源文件的访问， 将无法mapping到Controller的path交给default servlet handler处理 
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
    //注册媒体类型
    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        configurer.ignoreAcceptHeader(false)
                .favorPathExtension(true)
                .mediaType("json", MediaType.APPLICATION_JSON)
                .mediaType("xml", MediaType.APPLICATION_XML)
                .mediaType("rss", MediaType.APPLICATION_RSS_XML);
    }
    //拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 授权访问的拦截器
        registry.addInterceptor(getAuthInter()).excludePathPatterns("/member/home/board/active/json", "/member/home/topic/publish/json", "/member/home/topic/reply/json").addPathPatterns("/member/home/**", "/message/**", "/topic/create", "/posts/create", "/posts/reply", "/search/");
        // 注册通道检查拦截器
        registry.addInterceptor(getRegisInter()).addPathPatterns("/member/register");
        // Condition 返回false时不会创建Bean,所以无法找到定义
        try {
            // 策略检测拦截器
            registry.addInterceptor(getStrategyInter()).addPathPatterns("/board/**", "/posts/**", "/topic/**");
        } catch (org.springframework.beans.factory.NoSuchBeanDefinitionException e) {}
        // 邀请码拦截器
        registry.addInterceptor(getInviteCodeInter()).addPathPatterns("/member/register");
    }
    //HV && BV
    @Override
    public Validator getValidator() {
        LocalValidatorFactoryBean lvfb = new org.springframework.validation.beanvalidation.LocalValidatorFactoryBean();
        lvfb.setProviderClass(org.hibernate.validator.HibernateValidator.class);
        lvfb.setValidationMessageSource(messageSource());
        return lvfb;
    }
    //To enable CORS for the whole application
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/member/home/topic/**").allowedMethods("GET").allowedOrigins(adminDomain);
    }
    @Bean
    public MessageSource messageSource() {
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        messageSource.setBasename("messages");
        messageSource.setDefaultEncoding("UTF-8");
        messageSource.setCacheSeconds(60);
        messageSource.setUseCodeAsDefaultMessage(false);
        return messageSource;
    }
    @Bean
    public static PropertySourcesPlaceholderConfigurer propertySourceConfigurer() {
        PropertySourcesPlaceholderConfigurer pp = new PropertySourcesPlaceholderConfigurer();
        pp.setIgnoreUnresolvablePlaceholders(true);
        return pp;
    }
    /*apache fileupload
    @Bean(name = "multipartResolver")
    public MultipartResolver multipartResolver() {
        CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver();
        commonsMultipartResolver.setDefaultEncoding("utf-8");
        commonsMultipartResolver.setMaxUploadSize(maxBytes);
        commonsMultipartResolver.setResolveLazily(false);
        return commonsMultipartResolver;
    }*/
    //Cookie
    @Bean(name = "cookieConfig")
    public CookieMetaConfig getCookieConfig() {
        CookieMetaConfig cnf = new com.apobates.forum.member.storage.cookie.CookieMetaConfig(cookieName, cookieDomain, cookiePath, siteDomain);
        return cnf;
    }
    @Bean(name = "onlineMemberStorage")
    public OnlineMemberStorage getMemberStorage(CookieMetaConfig cookieConfig, @Nullable OnlineMemberRedisStorage redisMemberStorage) {
        if(null == redisMemberStorage){
            //cookie
            return new com.apobates.forum.member.storage.cookie.HttpCookieProvider(cookieConfig);
        }
        return redisMemberStorage;
    }
    @Bean(value = "redisMemberStorage")
    @Conditional(RedisMemberStorageCondition.class)
    public OnlineMemberRedisStorage getMemberRedisStorage(){
        //redis
        return new com.apobates.forum.member.storage.redis.OnlineMemberRedisStorage();
    }
    @Bean(name="imageStoreDomain")
    public ImageStoreDomain getImageStore(){
        return new com.apobates.forum.attention.ImageStoreDomain(){
            @Override
            public String getDefaultDomain() {
                return imageBucketDomain;
            }
            
            @Override
            public String getLocalDomain() {
                return siteDomain;
            }
        };
    }
    @Bean(name="imageIOMeta")
    public ImageIOMeta getImageIOMeta(){
        return new com.apobates.forum.core.ImageIOMeta(){
            @Override
            public String getImageBucketDomain() {
                return imageBucketDomain;
            }
            
            @Override
            public String getUploadImageDirectName() {
                return imageBucketUploadDirect;
            }
        };
    }
}