package com.artfess.base.conf;

import com.artfess.base.annotation.ApiGroup;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.Lists;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.convert.converter.Converter;
import org.springframework.format.FormatterRegistry;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import springfox.documentation.RequestHandler;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.Contact;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.service.VendorExtension;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.ApiSelectorBuilder;
import springfox.documentation.spring.web.plugins.Docket;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * Swagger配置辅助方法基础类
 *
 * @author mikel
 */
public abstract class SwaggerConfigHelper implements WebMvcConfigurer {

    @Value("${spring.profiles.title}")
    private String title;
    @Value("${spring.profiles.description}")
    private String description;
    @Value("${spring.profiles.version}")
    private String version;
    @Value("${file.upload}")
    private String upLoadPath;

    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverter(localDateConvert());
    }

    public Converter<String, LocalDate> localDateConvert() {
        // 不能替换为lambda表达式
        return new Converter<String, LocalDate>() {
            @Override
            public LocalDate convert(String source) {
                if (StringUtils.hasText(source)) {
                    return LocalDate.parse(source, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
                }
                return null;
            }
        };
    }

    /**
     * 根据分组构建完整的API注册信息
     *
     * @param apiGroupName
     * @param apiGroupKey
     * @return
     */
    public Docket buildProductApi(String apiGroupName, String apiGroupKey) {
        Docket docket = buildDocket(apiGroupName).apiInfo(buildBaseProductApiInfo());
        ApiSelectorBuilder builder = buildApiForGroupKey(docket, apiGroupKey);
        return buildApiSecurityAuth(builder);
    }

    /**
     * 根据分组名创建Docket
     *
     * @param apiGroupName
     * @return
     */
    public Docket buildDocket(String apiGroupName) {
        return new Docket(DocumentationType.SWAGGER_2)
                .groupName(apiGroupName)
                //.apiInfo(buildBaseProductApiInfo(title, description, version))
                .useDefaultResponseMessages(false)
                .forCodeGeneration(false);
    }

    /**
     * 根据分组key对API进行注册
     *
     * @param docket
     * @param apiGroupKey
     * @return
     */
    public ApiSelectorBuilder buildApiForGroupKey(Docket docket, String apiGroupKey) {
        return docket.select()
                .apis(getPredicateWithGroup(apiGroupKey))
                .paths(PathSelectors.any());
    }

    /**
     * 根据基础包名对API进行注册
     *
     * @param docket
     * @param basePackage
     * @return
     */
    public ApiSelectorBuilder buildApiForPackage(Docket docket, String basePackage) {
        return docket.select()
                .apis(RequestHandlerSelectors.basePackage(basePackage))
                .paths(PathSelectors.any());
    }

    /**
     * 构建API安全认证
     *
     * @param builder
     * @return
     */
    public Docket buildApiSecurityAuth(ApiSelectorBuilder builder) {
        return builder.build().securityContexts(Lists.newArrayList(buildSecurityContext()))
                .securitySchemes(Lists.newArrayList(apiSecurityKey()));
    }

    private ApiKey apiSecurityKey() {
        return new ApiKey("BearerToken", "Authorization", "header");
    }

    private SecurityContext buildSecurityContext() {
        return SecurityContext.builder()
                .securityReferences(defaultAuth())
                .forPaths(PathSelectors.regex("/.*"))
                .build();
    }

    /**
     * 默认的认证方式
     *
     * @return
     */
    private List<SecurityReference> defaultAuth() {
        AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
        AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
        authorizationScopes[0] = authorizationScope;
        return Lists.newArrayList(new SecurityReference("BearerToken", authorizationScopes));
    }

    /**
     * 通过接口分组过滤
     *
     * @param group
     * @return
     */
    protected Predicate<RequestHandler> getPredicateWithGroup(String group) {
        return new Predicate<RequestHandler>() {
            @Override
            public boolean apply(RequestHandler input) {
                // 找到controller类上的ApiGroup注解
                Optional<ApiGroup> ApiGroup = input.findControllerAnnotation(ApiGroup.class);
                if (ApiGroup.isPresent() && Arrays.asList(ApiGroup.get().group()).contains(group)) {
                    return true;
                }
                return false;
            }
        };
    }

    protected ApiInfo buildBaseProductApiInfo() {
        Contact contact = new Contact("liangyan", "www.Artfess.com", "liangyan@artfess.com");
        return buildApiInfo(title, description, version, "http://127.0.0.1:8080", contact);
    }

    @SuppressWarnings("rawtypes")
    protected ApiInfo buildApiInfo(String title, String description, String version, String url, Contact contact) {
        ApiInfo apiInfo = new ApiInfo(title, description, version, url, contact, "license", "license url",
                new ArrayList<VendorExtension>());
        return apiInfo;
    }

    /**
     * 显示swagger-ui.html文档展示页，还必须注入swagger资源：
     *
     * @param registry
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
        registry.addResourceHandler("/m3u8/**")
                .addResourceLocations("file:" +  upLoadPath+ "/");
    }
}
