package ink.wgink.common.config;

import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import ink.wgink.interfaces.consts.ISystemConstant;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.RequestMethod;
import springfox.documentation.RequestHandler;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.builders.ResponseMessageBuilder;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ResponseMessage;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;

/**
 * @ClassName: SwaggerConfig
 * @Description: swagger的配置文件
 * @Author: WangGeng
 * @Date: 2019/3/1 3:58 PM
 * @Version: 1.0
 **/
@Configuration
public class SwaggerConfig {

    @Value("${swagger.title:相关接口文档}")
    private String title;
    @Value("${swagger.description:相关接口文档}")
    private String description;
    @Value("${swagger.service-url:http://www.wgink.ink}")
    private String serviceUrl;
    @Value("${swagger.version:1.0}")
    private String version;
    @Value("#{'${swagger.base-package-list}'.split(',')}")
    private List<String> basePackageList;
    private Predicate<RequestHandler>[] basePackages;

    @PostConstruct
    private void initBasePackages() {
        basePackages = new Predicate[basePackageList.size()];
        for (int i = 0; i < basePackages.length; i++) {
            basePackages[i] = RequestHandlerSelectors.basePackage(basePackageList.get(i));
        }
    }

    private List<ResponseMessage> responseMessageList() {
        List<ResponseMessage> responseMessageList = new ArrayList<>();
        responseMessageList.add(new ResponseMessageBuilder().code(400).message("请求失败").build());
        responseMessageList.add(new ResponseMessageBuilder().code(404).message("请求不存在").build());
        responseMessageList.add(new ResponseMessageBuilder().code(503).message("服务不可用").build());
        return responseMessageList;
    }

    @Bean
    public Docket createRestGlobalApi() {
        List<ResponseMessage> responseMessageList = responseMessageList();
        return new Docket(DocumentationType.SWAGGER_2)
                .globalResponseMessage(RequestMethod.POST, responseMessageList)
                .globalResponseMessage(RequestMethod.DELETE, responseMessageList)
                .globalResponseMessage(RequestMethod.PUT, responseMessageList)
                .globalResponseMessage(RequestMethod.GET, responseMessageList)
                .apiInfo(apiInfo())
                .select()
                .apis(Predicates.or(basePackages))
                .paths(PathSelectors.any())
                .build();
    }

    @Bean
    public Docket createRestApiSystem() {
        List<ResponseMessage> responseMessageList = responseMessageList();
        return new Docket(DocumentationType.SWAGGER_2)
                .globalResponseMessage(RequestMethod.POST, responseMessageList)
                .globalResponseMessage(RequestMethod.DELETE, responseMessageList)
                .globalResponseMessage(RequestMethod.PUT, responseMessageList)
                .globalResponseMessage(RequestMethod.GET, responseMessageList)
                .groupName(ISystemConstant.API_GROUP_SYSTEM)
                .apiInfo(apiInfo())
                .select()
                .apis(Predicates.or(basePackages))
                .paths(PathSelectors.ant(ISystemConstant.API_PREFIX + "/**"))
                .build();
    }

    @Bean
    public Docket createRestApiResource() {
        List<ResponseMessage> responseMessageList = responseMessageList();
        return new Docket(DocumentationType.SWAGGER_2)
                .globalResponseMessage(RequestMethod.POST, responseMessageList)
                .globalResponseMessage(RequestMethod.DELETE, responseMessageList)
                .globalResponseMessage(RequestMethod.PUT, responseMessageList)
                .globalResponseMessage(RequestMethod.GET, responseMessageList)
                .groupName(ISystemConstant.API_GROUP_RESOURCE)
                .apiInfo(apiInfo())
                .select()
                .apis(Predicates.or(basePackages))
                .paths(PathSelectors.ant(ISystemConstant.RESOURCE_PREFIX + "/**"))
                .build();
    }

    @Bean
    public Docket createRestApiApp() {
        List<ResponseMessage> responseMessageList = responseMessageList();
        return new Docket(DocumentationType.SWAGGER_2)
                .globalResponseMessage(RequestMethod.POST, responseMessageList)
                .globalResponseMessage(RequestMethod.DELETE, responseMessageList)
                .globalResponseMessage(RequestMethod.PUT, responseMessageList)
                .globalResponseMessage(RequestMethod.GET, responseMessageList)
                .groupName(ISystemConstant.API_GROUP_APP)
                .apiInfo(apiInfo())
                .select()
                .apis(Predicates.or(basePackages))
                .paths(PathSelectors.ant(ISystemConstant.APP_PREFIX + "/**"))
                .build();
    }

    @Bean
    public Docket createRestApiRoute() {
        List<ResponseMessage> responseMessageList = responseMessageList();
        return new Docket(DocumentationType.SWAGGER_2)
                .globalResponseMessage(RequestMethod.POST, responseMessageList)
                .globalResponseMessage(RequestMethod.DELETE, responseMessageList)
                .globalResponseMessage(RequestMethod.PUT, responseMessageList)
                .globalResponseMessage(RequestMethod.GET, responseMessageList)
                .groupName(ISystemConstant.API_GROUP_ROUTE)
                .apiInfo(apiInfo())
                .select()
                .apis(Predicates.or(basePackages))
                .paths(PathSelectors.ant("/route/**"))
                .build();
    }

    @Bean
    public Docket createRestApiWechat() {
        List<ResponseMessage> responseMessageList = responseMessageList();
        return new Docket(DocumentationType.SWAGGER_2)
                .globalResponseMessage(RequestMethod.POST, responseMessageList)
                .globalResponseMessage(RequestMethod.DELETE, responseMessageList)
                .globalResponseMessage(RequestMethod.PUT, responseMessageList)
                .globalResponseMessage(RequestMethod.GET, responseMessageList)
                .groupName(ISystemConstant.API_GROUP_WECHAT)
                .apiInfo(apiInfo())
                .select()
                .apis(Predicates.or(basePackages))
                .paths(PathSelectors.ant(ISystemConstant.WECHAT_PREFIX + "/**"))
                .build();
    }

    @Bean
    public Docket createRestApiWechatMiniapp() {
        List<ResponseMessage> responseMessageList = responseMessageList();
        return new Docket(DocumentationType.SWAGGER_2)
                .globalResponseMessage(RequestMethod.POST, responseMessageList)
                .globalResponseMessage(RequestMethod.DELETE, responseMessageList)
                .globalResponseMessage(RequestMethod.PUT, responseMessageList)
                .globalResponseMessage(RequestMethod.GET, responseMessageList)
                .groupName(ISystemConstant.API_GROUP_WECHAT_MINI_APP)
                .apiInfo(apiInfo())
                .select()
                .apis(Predicates.or(basePackages))
                .paths(PathSelectors.ant(ISystemConstant.WECHAT_MINI_APP_PREFIX + "/**"))
                .build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title(title)
                .description(description)
                .termsOfServiceUrl(serviceUrl)
                .version(version)
                .build();
    }

    public static Predicate<RequestHandler> basePackage(final List<String> basePackageList) {
        return input -> declaringClass(input).transform(handlerPackage(basePackageList)).or(true);
    }

    private static Function<Class<?>, Boolean> handlerPackage(final List<String> basePackageList) {
        return input -> {
            // 循环判断匹配
            for (String strPackage : basePackageList) {
                boolean isMatch = input.getPackage().getName().startsWith(strPackage);
                if (isMatch) {
                    return true;
                }
            }
            return false;
        };
    }

    private static Optional<? extends Class<?>> declaringClass(RequestHandler input) {
        return Optional.fromNullable(input.getClass());
    }

}
