package dp.kotul.bamboospecsapplication;

import com.atlassian.bamboo.specs.api.BambooSpec;
import com.atlassian.bamboo.specs.api.builders.AtlassianModule;
import com.atlassian.bamboo.specs.api.builders.deployment.Deployment;
import com.atlassian.bamboo.specs.api.builders.deployment.Environment;
import com.atlassian.bamboo.specs.api.builders.deployment.ReleaseNaming;
import com.atlassian.bamboo.specs.api.builders.notification.AnyNotificationRecipient;
import com.atlassian.bamboo.specs.api.builders.notification.Notification;
import com.atlassian.bamboo.specs.api.builders.permission.DeploymentPermissions;
import com.atlassian.bamboo.specs.api.builders.permission.EnvironmentPermissions;
import com.atlassian.bamboo.specs.api.builders.permission.PermissionType;
import com.atlassian.bamboo.specs.api.builders.permission.Permissions;
import com.atlassian.bamboo.specs.api.builders.permission.PlanPermissions;
import com.atlassian.bamboo.specs.api.builders.plan.Job;
import com.atlassian.bamboo.specs.api.builders.plan.Plan;
import com.atlassian.bamboo.specs.api.builders.plan.PlanIdentifier;
import com.atlassian.bamboo.specs.api.builders.plan.Stage;
import com.atlassian.bamboo.specs.api.builders.plan.branches.PlanBranchManagement;
import com.atlassian.bamboo.specs.api.builders.project.Project;
import com.atlassian.bamboo.specs.api.builders.requirement.Requirement;
import com.atlassian.bamboo.specs.builders.notification.PlanStatusChangedNotification;
import com.atlassian.bamboo.specs.builders.trigger.BitbucketServerTrigger;
import com.atlassian.bamboo.specs.util.BambooServer;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.ObjectMapper;
import dp.kotul.bamboospecsapplication.configuration.Configuration;
import dp.kotul.bamboospecsapplication.configuration.DeploymentConfiguration;
import dp.kotul.bamboospecsapplication.configuration.JobConfiguration;
import dp.kotul.bamboospecsapplication.configuration.ProjectConfiguration;
import dp.kotul.bamboospecsapplication.configuration.StageConfiguration;
import dp.kotul.bamboospecsapplication.creator.BuildArtifactCreator;
import dp.kotul.bamboospecsapplication.creator.BuildTaskCreator;
import dp.kotul.bamboospecsapplication.creator.DeploymentTaskCreator;
import dp.kotul.bamboospecsapplication.creator.DeploymentVariableCreator;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * Plan configuration for Bamboo.
 * Learn more on: <a href="https://confluence.atlassian.com/display/BAMBOO/Bamboo+Specs">https://confluence.atlassian.com/display/BAMBOO/Bamboo+Specs</a>
 */
@BambooSpec
public class PlanSpec {

    /**
     * Run main to publish plan on Bamboo
     */
    public static void main(final String[] args) {
        PlanSpec planSpec = new PlanSpec();
        Configuration configuration = planSpec.getConfiguration();
        if (configuration == null) return;

        //By default credentials are read from the '.credentials' file.
        BambooServer bambooServer = new BambooServer("http://bamboo.brandmaster.com:8085");

        Plan plan = planSpec.createPlan(configuration);
        bambooServer.publish(plan);

        PlanPermissions planPermission = planSpec.createPlanPermission(plan.getIdentifier());
        bambooServer.publish(planPermission);

        Environment[] envs = planSpec.createEnvironments(configuration.getDeployment());
        DeploymentConfiguration deployment = configuration.getDeployment();
        Deployment deploymentPlan = new Deployment(plan.getIdentifier(), deployment.getName())
                .description(deployment.getDescription())
                .releaseNaming(new ReleaseNaming(deployment.getReleaseNaming().getVersion())
                        .autoIncrement(deployment.getReleaseNaming().isReleaseAutoIncrement()))
                .environments(envs);
        bambooServer.publish(deploymentPlan);

        DeploymentPermissions deploymentPermissions = new DeploymentPermissions(deployment.getName())
                .permissions(new Permissions().loggedInUserPermissions(PermissionType.VIEW, PermissionType.EDIT));
        bambooServer.publish(deploymentPermissions);

        for (EnvironmentEnum env: EnvironmentEnum.values()) {
            EnvironmentPermissions environmentPermissions = new EnvironmentPermissions(deployment.getName())
                    .environmentName(env.name())
                    .permissions(new Permissions().loggedInUserPermissions(PermissionType.VIEW, PermissionType.EDIT, PermissionType.BUILD));
            bambooServer.publish(environmentPermissions);
        }

    }

    Configuration getConfiguration() {
        Logger log = LoggerFactory.getLogger(PlanSpec.class);

        Configuration configuration = null;
        try(InputStream is = this.getClass().getClassLoader().getResourceAsStream("configurationSolrApi.json")) {
            ObjectMapper mapper = new ObjectMapper().enable(JsonParser.Feature.ALLOW_COMMENTS);
            configuration = mapper.readValue(is, Configuration.class);
            log.info(new ObjectMapper().writeValueAsString(configuration));
        } catch (IOException e) {
            log.error("Could not load json configuration [configurationSolrApi.json]", e);
            return configuration;
        }
        return configuration;
    }


    PlanPermissions createPlanPermission(PlanIdentifier planIdentifier) {
        Permissions permission = new Permissions().groupPermissions("bamboo-admin", PermissionType.ADMIN).loggedInUserPermissions(PermissionType.ADMIN);
        return new PlanPermissions(planIdentifier.getProjectKey(), planIdentifier.getPlanKey()).permissions(permission);
    }

    Project project(ProjectConfiguration projectConfig) {
        return new Project()
                .name(projectConfig.getName())
                .description(projectConfig.getDescription())
                .key(projectConfig.getKey());
    }

    Plan createPlan(Configuration config) {
        return new Plan(
                project(config.getProject()),
                config.getPlan().getName(), config.getPlan().getKey())
                .description(config.getPlan().getDescription())
                .enabled(true)
                .stages(createStage(config.getPlan().getStage()))
                .triggers(new BitbucketServerTrigger())
                .planBranchManagement(new PlanBranchManagement().createManually().notificationForCommitters())
                .linkedRepositories(config.getPlan().getLinkedRepositoryName())
                .notifications(new Notification().type(new PlanStatusChangedNotification()).recipients(new AnyNotificationRecipient(
                        new AtlassianModule("com.atlassian.bamboo.plugins.bamboo-slack:recipient.slack"))
                        .recipientString("https://hooks.slack.com/services/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|||")
                ))
                .variables(DeploymentVariableCreator.createVariables(EnvironmentEnum.DEV, config.getDeployment()));
    }

    Stage createStage(StageConfiguration stageConfig) {
        return new Stage(stageConfig.getName())
                .description(stageConfig.getName())
                .jobs(createJob(stageConfig.getJob()));
    }

    Job createJob(JobConfiguration jobConfig) {
        return new Job(jobConfig.getName(), jobConfig.getKey())
                .enabled(true)
                .tasks(BuildTaskCreator.createBuildTasks(jobConfig.getTasks()))
                .artifacts(BuildArtifactCreator.createArtifacts(jobConfig.getArtifacts()));

    }

    Environment[] createEnvironments(DeploymentConfiguration deployment) {
        List<Environment> environments = new ArrayList<>();
        for (EnvironmentEnum environmentEnum : EnvironmentEnum.values()) {
            environments.add(createEnvironment(environmentEnum, deployment));
        }
        return environments.stream().toArray(Environment[]::new);
    }

    Environment createEnvironment(EnvironmentEnum environment, DeploymentConfiguration deployment) {
        Environment env = new Environment(environment.name())
                .description(environment.name() + " Environment")
                .variables(DeploymentVariableCreator.createVariables(environment, deployment))
                .tasks(DeploymentTaskCreator.createDeploymentTasks(deployment.getTasks(), environment));

        if (deployment.getRequirements() != null && !deployment.getRequirements().isEmpty()) {
            List<Requirement> requirements = new ArrayList<>();
            for (String requirement : deployment.getRequirements()) {
                requirements.add(new Requirement(requirement));
            }

            env.requirements(requirements.stream().toArray(Requirement[]::new));
        }
        return env;
    }
}
