package com.inspect.base.swagger.config;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
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.SecurityScheme;
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 springfox.documentation.swagger2.annotations.EnableSwagger2;

@Configuration
@EnableSwagger2
@EnableAutoConfiguration
@ConditionalOnProperty(
   name = {"swagger.enabled"},
   matchIfMissing = true
)
public class SwaggerAutoConfiguration {
   private static final List<String> DEFAULT_EXCLUDE_PATH = Arrays.asList(new String[]{"/error", "/actuator/**"});
   private static final String BASE_PATH = "/**";

   @Bean
   @ConditionalOnMissingBean
   public SwaggerProperties swaggerProperties() {
      return new SwaggerProperties();
   }

   @Bean
   public Docket api(SwaggerProperties swaggerProperties) {
      if(swaggerProperties.getBasePath().isEmpty()) {
         swaggerProperties.getBasePath().add("/**");
      }

      List<Predicate<String>> basePath = new ArrayList();
      swaggerProperties.getBasePath().forEach((path) -> {
         basePath.add((Predicate<String>) PathSelectors.ant(path));
      });
      if(swaggerProperties.getExcludePath().isEmpty()) {
         swaggerProperties.getExcludePath().addAll(DEFAULT_EXCLUDE_PATH);
      }

      List<Predicate<String>> excludePath = new ArrayList();
      swaggerProperties.getExcludePath().forEach((path) -> {
         excludePath.add((Predicate<String>) PathSelectors.ant(path));
      });
      ApiSelectorBuilder builder = (new Docket(DocumentationType.SWAGGER_2)).host(swaggerProperties.getHost()).apiInfo(this.apiInfo(swaggerProperties)).select().apis(RequestHandlerSelectors.basePackage(swaggerProperties.getBasePackage()));
      swaggerProperties.getBasePath().forEach((p) -> {
         builder.paths(PathSelectors.ant(p));
      });
      swaggerProperties.getExcludePath().forEach((p) -> {
         builder.paths(PathSelectors.ant(p).negate());
      });
      return builder.build().securitySchemes(this.securitySchemes()).securityContexts(this.securityContexts()).pathMapping("/");
   }

   private List<SecurityScheme> securitySchemes() {
      List<SecurityScheme> apiKeyList = new ArrayList();
      apiKeyList.add(new ApiKey("Authorization", "Authorization", "header"));
      return apiKeyList;
   }

   private List<SecurityContext> securityContexts() {
      List<SecurityContext> securityContexts = new ArrayList();
      securityContexts.add(SecurityContext.builder().securityReferences(this.defaultAuth()).operationSelector((o) -> {
         return o.requestMappingPattern().matches("/.*");
      }).build());
      return securityContexts;
   }

   private List<SecurityReference> defaultAuth() {
      AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
      AuthorizationScope[] authorizationScopes = new AuthorizationScope[]{authorizationScope};
      List<SecurityReference> securityReferences = new ArrayList();
      securityReferences.add(new SecurityReference("Authorization", authorizationScopes));
      return securityReferences;
   }

   private ApiInfo apiInfo(SwaggerProperties swaggerProperties) {
      return (new ApiInfoBuilder()).title(swaggerProperties.getTitle()).description(swaggerProperties.getDescription()).license(swaggerProperties.getLicense()).licenseUrl(swaggerProperties.getLicenseUrl()).termsOfServiceUrl(swaggerProperties.getTermsOfServiceUrl()).contact(new Contact(swaggerProperties.getContact().getName(), swaggerProperties.getContact().getUrl(), swaggerProperties.getContact().getEmail())).version(swaggerProperties.getVersion()).build();
   }
}
