package com.societegenerale.cidroid.config;

import com.fasterxml.classmate.TypeResolver;
import com.societegenerale.cidroid.api.ResourceToUpdate;
import com.societegenerale.cidroid.api.actionToReplicate.ActionToReplicate;
import com.societegenerale.cidroid.api.gitHubInteractions.AbstractGitHubInteraction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.builders.ModelPropertyBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelProperty;
import springfox.documentation.schema.TypeNameExtractor;
import springfox.documentation.service.AllowableListValues;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.schema.ModelBuilderPlugin;
import springfox.documentation.spi.schema.contexts.ModelContext;
import springfox.documentation.spring.web.DescriptionResolver;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger.common.SwaggerPluginSupport;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static java.util.stream.Collectors.toList;
import static springfox.documentation.schema.ResolvedTypes.modelRefFactory;

@Configuration
@EnableSwagger2
public class SwaggerConfiguration {

    @Bean
    public Docket productApi() {
        Docket docket = new Docket(DocumentationType.SWAGGER_2)
                .select().apis(RequestHandlerSelectors.withClassAnnotation(RestController.class))
                .paths(PathSelectors.any())
                .build();
        return docket;
    }

    @Component
    @Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER + 1000)
    public class ModelEnhancement implements ModelBuilderPlugin {
        private final DescriptionResolver descriptions;

        @Autowired
        public ModelEnhancement(DescriptionResolver descriptions) {
            this.descriptions = descriptions;
        }

        @Autowired
        TypeResolver resolver;

        @Autowired
        TypeNameExtractor typeNameExtractor;

        @Autowired
        List<ActionToReplicate> availableActions;

        @Override
        public boolean supports(DocumentationType delimiter) {
            return SwaggerPluginSupport.pluginDoesApply(delimiter);
        }

        @Override
        public void apply(ModelContext modelContext) {

            Class<?> modelClass = forClass(modelContext);

            modelContext.getType();

            if(modelClass.equals(AbstractGitHubInteraction.class)){
                abstractGitHubInteractionSpecificConfig(modelContext);
            }

            else if(modelClass.equals(ActionToReplicate.class)){
                actionToReplicateSpecificConfig(modelContext);
            }

            else if(modelClass.equals(ResourceToUpdate.class)){
                resourcesToUpdateSpecificConfig(modelContext);
            }

        }

        private void actionToReplicateSpecificConfig(ModelContext modelContext) {
            Map actionToReplicateProperties = new HashMap<String, ModelProperty>();

            List<String> availableActionClassNames=availableActions.stream().map(a -> a.getClass().getCanonicalName()).collect(toList());

            ModelProperty actionToReplicateClassNameProperty=new ModelPropertyBuilder().required(true)
                    .allowEmptyValue(false)
                    .name("fully qualified class name identifier")
                    .description("fully qualified name of the class implementing com.societegenerale.cidroid.api.actionToReplicate.ActionToReplicate")
                    .allowableValues(new AllowableListValues(availableActionClassNames,"string"))
                    .example((Object)"com.societegenerale.cidroid.extensions.actionToReplicate.OverwriteStaticFileAction")
                    .type(resolver.resolve(String.class)).build();
            actionToReplicateClassNameProperty.updateModelRef(modelRefFactory(modelContext, typeNameExtractor));

            actionToReplicateProperties.put("@class",actionToReplicateClassNameProperty);


            ModelProperty actionToReplicateAttributeProperty=new ModelPropertyBuilder().required(true)
                    .allowEmptyValue(false)
                    .name("example of attribute")
                    .description("depends on the @class picked. See in GET /cidroid-actions/availableActions , which classes you can use, and what are the expected fields for each")
                    .type(resolver.resolve(String.class)).build();
            actionToReplicateAttributeProperty.updateModelRef(modelRefFactory(modelContext, typeNameExtractor));

            actionToReplicateProperties.put("someActionAttribute",actionToReplicateAttributeProperty);

            modelContext.getBuilder()
                    .properties(actionToReplicateProperties)
                    .description("see the Jackson config and the subclasses of in https://github.com/societe-generale/ci-droid-internal-api/blob/master/src/main/java/com/societegenerale/cidroid/api/actionToReplicate/ActionToReplicate.java");

        }

        private void abstractGitHubInteractionSpecificConfig(ModelContext modelContext) {
            Map abstractGitHubInteractionProperties=new HashMap<String, ModelProperty>();

            ModelProperty abstractGitHubInteractionClassNameProperty=new ModelPropertyBuilder().required(true)
                    .allowEmptyValue(false)
                    .name("class identifier")
                    .allowableValues(new AllowableListValues(
                            Arrays.asList(".DirectPushGitHubInteraction",".PullRequestGitHubInteraction"),"string"))
                    .example((Object)".DirectPushGitHubInteraction")
                    .type(resolver.resolve(String.class)).build();
            abstractGitHubInteractionClassNameProperty.updateModelRef(modelRefFactory(modelContext, typeNameExtractor));
            abstractGitHubInteractionProperties.put("@c",abstractGitHubInteractionClassNameProperty);


            ModelProperty branchNameProperty=new ModelPropertyBuilder().required(false)
                    .name("branch name in case of PR")
                    .description("when @c=.PullRequestGitHubInteraction, then we need to provide the name of the branch in which we want to the PR")
                    .example((Object)"myBranch")
                    .type(resolver.resolve(String.class)).build();
            branchNameProperty.updateModelRef(modelRefFactory(modelContext, typeNameExtractor));
            abstractGitHubInteractionProperties.put("branchNameToCreate",branchNameProperty);


            ModelProperty prTitleProperty=new ModelPropertyBuilder().required(false)
                    .name("title of the PR")
                    .description("when @c=.PullRequestGitHubInteraction, then we can provide a title for the PR. If not provided, the PR title will be the same as the branch name")
                    .example((Object)"'[XYZ-123] adding a feature'")
                    .type(resolver.resolve(String.class)).build();
            prTitleProperty.updateModelRef(modelRefFactory(modelContext, typeNameExtractor));
            abstractGitHubInteractionProperties.put("pullRequestTitle",prTitleProperty);

            modelContext.getBuilder()
                    .properties(abstractGitHubInteractionProperties)
                    .description("see the Jackson config in https://github.com/societe-generale/ci-droid-internal-api/blob/master/src/main/java/com/societegenerale/cidroid/api/gitHubInteractions/AbstractGitHubInteraction.java");
        }

        private void resourcesToUpdateSpecificConfig(ModelContext modelContext) {
            Map resourcesToUpdateProperties=new HashMap<String, ModelProperty>();

            ModelProperty repoFullNameProperty=new ModelPropertyBuilder().required(true)
                    .allowEmptyValue(false)
                    .name("repo full name")
                    .position(1)
                    .example((Object)"vincent-fuchs/in-memory-testing")
                    .type(resolver.resolve(String.class)).build();
            repoFullNameProperty.updateModelRef(modelRefFactory(modelContext, typeNameExtractor));
            resourcesToUpdateProperties.put("repoFullName",repoFullNameProperty);

            ModelProperty filePathOnRepoProperty=new ModelPropertyBuilder().required(true)
                    .allowEmptyValue(false)
                    .name("file path")
                    .position(2)
                    .example((Object)"pom.xml")
                    .type(resolver.resolve(String.class)).build();
            filePathOnRepoProperty.updateModelRef(modelRefFactory(modelContext, typeNameExtractor));
            resourcesToUpdateProperties.put("filePathOnRepo",filePathOnRepoProperty);

            ModelProperty branchProperty=new ModelPropertyBuilder().required(true)
                    .allowEmptyValue(false)
                    .name("branch")
                    .position(3)
                    .example((Object)"master")
                    .type(resolver.resolve(String.class)).build();
            branchProperty.updateModelRef(modelRefFactory(modelContext, typeNameExtractor));
            resourcesToUpdateProperties.put("branchName",branchProperty);

            ModelProperty placeHolderValueProperty=new ModelPropertyBuilder().required(false)
                    .name("placeHolderValue")
                    .position(4)
                    .description("when using @class TemplateBasedContentAction, we use a template with a placeholder : the value to use is provided with each resource")
                    .type(resolver.resolve(String.class)).build();
            placeHolderValueProperty.updateModelRef(modelRefFactory(modelContext, typeNameExtractor));
            resourcesToUpdateProperties.put("placeHolderValue",placeHolderValueProperty);

            modelContext.getBuilder()
                    .properties(resourcesToUpdateProperties)
                    .description("a list of description of resources, for which the updateAction will be performed");


        }

        private Class<?> forClass(ModelContext context) {
            return resolver.resolve(context.getType()).getErasedType();
        }
    }



}