package com.heroku;

import com.atlassian.bamboo.build.logger.BuildLogger;
import com.atlassian.bamboo.task.*;
import com.herokuapp.directto.client.DirectToHerokuClient;
import com.herokuapp.directto.client.VerificationException;
import org.jetbrains.annotations.NotNull;

import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * @author Ryan Brainard
 */
public abstract class AbstractDeploymentTask<P extends DeploymentPipeline> implements TaskType {

    /**
     * A sandbox for static methods that don't play well with jMock
     */
    protected static interface StaticSandbox {
        TaskResult success(TaskContext taskContext);
        TaskResult failed(TaskContext taskContext);
    }
    
    private final P pipeline;
    private final StaticSandbox staticSandbox;

    public AbstractDeploymentTask(P pipeline) {
        this(pipeline, new StaticSandbox() {
            @Override
            public TaskResult success(TaskContext taskContext) {
                return TaskResultBuilder.create(taskContext).success().build();
            }

            @Override
            public TaskResult failed(TaskContext taskContext) {
                return TaskResultBuilder.create(taskContext).failed().build();
            }
        });
    }

    public AbstractDeploymentTask(P pipeline, StaticSandbox staticSandbox) {
        this.pipeline = pipeline;
        this.staticSandbox = staticSandbox;
    }

    @NotNull
    @Override
    public TaskResult execute(@NotNull final TaskContext taskContext) throws TaskException {
        final BuildLogger buildLogger = taskContext.getBuildLogger();
        final String apiKey = taskContext.getConfigurationMap().get("apiKey");
        final String appName = taskContext.getConfigurationMap().get("appName");
        final String pipelineName = pipeline.getPipelineName();
        final DirectToHerokuClient client = new DirectToHerokuClient(apiKey);

        buildLogger.addBuildLogEntry("Preparing to deploy to Heroku app [" + appName + "] via [" + pipelineName + "] pipeline");
        
        final Map<String, File> files = new HashMap<String, File>(1);
        addFiles(taskContext, files);
        for (Map.Entry<String,File> file : files.entrySet()) {
            buildLogger.addBuildLogEntry("Adding [" + file.getKey()  + "]: " + file.getValue());
        }

        try {
            client.verify(pipelineName, appName, files);
        } catch (VerificationException e) {
            for (String msg : e.getMessages()) {
                buildLogger.addErrorLogEntry(msg);
            }
            return staticSandbox.failed(taskContext);
        }

        final Future<Map<String, String>> deployFuture = client.deployAsync(pipelineName, appName, files);
        buildLogger.addBuildLogEntry("Deploying...");

        final Map<String, String> deployResults;
        try {
            deployResults = deployFuture.get(5, TimeUnit.MINUTES);
        } catch (TimeoutException e) {
            buildLogger.addErrorLogEntry("Deploy timeout", e);
            return staticSandbox.failed(taskContext);
        } catch (InterruptedException e) {
            buildLogger.addErrorLogEntry("Deploy interrupted", e);
            return staticSandbox.failed(taskContext);
        } catch (ExecutionException e) {
            buildLogger.addErrorLogEntry("Unknown deploy error", e);
            return staticSandbox.failed(taskContext);
        }

        buildLogger.addBuildLogEntry("Deploy results:");
        for (Map.Entry<String, String> result : deployResults.entrySet()) {
            buildLogger.addBuildLogEntry(" - " + result.getKey() + ":" + result.getValue());
        }

        return "success".equals(deployResults.get("status"))
                ? staticSandbox.success(taskContext) 
                : staticSandbox.failed(taskContext);
    }

    protected void addFiles(TaskContext taskContext, Map<String, File> files) {
        final String workingDir = taskContext.getWorkingDirectory().getAbsolutePath() + "/";
        for (String reqFile : pipeline.getRequiredFiles()) {
            files.put(reqFile, new File(workingDir + taskContext.getConfigurationMap().get(reqFile)));
        }
    }
}

@@ -338,176 +338,8 @@
 Map;
-%0Aimport java.util.concurrent.ExecutionException;%0Aimport java.util.concurrent.Future;%0Aimport java.util.concurrent.TimeUnit;%0Aimport java.util.concurrent.TimeoutException;
 %0A%0A/*
@@ -2649,15 +2649,8 @@
 nal 
-Future%3C
 Map%3C
@@ -2668,22 +2668,22 @@
 ing%3E
-%3E
  deploy
-Future
+Results
  = c
@@ -2698,13 +2698,8 @@
 ploy
-Async
 (pip
@@ -2785,633 +2785,8 @@
 );%0A%0A
-        final Map%3CString, String%3E deployResults;%0A        try %7B%0A            deployResults = deployFuture.get(5, TimeUnit.MINUTES);%0A        %7D catch (TimeoutException e) %7B%0A            buildLogger.addErrorLogEntry(%22Deploy timeout%22, e);%0A            return staticSandbox.failed(taskContext);%0A        %7D catch (InterruptedException e) %7B%0A            buildLogger.addErrorLogEntry(%22Deploy interrupted%22, e);%0A            return staticSandbox.failed(taskContext);%0A        %7D catch (ExecutionException e) %7B%0A            buildLogger.addErrorLogEntry(%22Unknown deploy error%22, e);%0A            return staticSandbox.failed(taskContext);%0A        %7D%0A%0A
     

remove fake async deploy