package com.pmease.quickbuild.plugin.artifact;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.hibernate.validator.constraints.NotEmpty;
import org.joda.time.DateTime;

import com.pmease.quickbuild.Context;
import com.pmease.quickbuild.QuickbuildException;
import com.pmease.quickbuild.annotation.ChoiceProvider;
import com.pmease.quickbuild.annotation.Editable;
import com.pmease.quickbuild.annotation.ScriptApi;
import com.pmease.quickbuild.annotation.Scriptable;
import com.pmease.quickbuild.grid.AgentManager;
import com.pmease.quickbuild.grid.Grid;
import com.pmease.quickbuild.grid.GridNode;
import com.pmease.quickbuild.model.Build;
import com.pmease.quickbuild.security.SecurityHelper;
import com.pmease.quickbuild.setting.configuration.artifactstorage.ArtifactStorage;
import com.pmease.quickbuild.util.Constants;
import com.pmease.quickbuild.web.BatchDownloadServlet;
import com.pmease.quickbuild.web.DownloadServlet;

@Editable(name="Store artifacts on specified agent", order=200)
public class AgentArtifactStorage extends ArtifactStorage {

	private static final long serialVersionUID = 1L;

	public static final int TOKEN_VALID_MINUTES = 30;
	
	private String agentAddress;
	
	@Editable(description="Choose an agent to store and serve artifacts for this configuration. By default, artifacts will " +
			"be stored on agent in directory &lt;QuickBuild agent install dir&gt;/storage, with directory stucture " +
			"conforming to storage directory setting specified above. If the storage directory evaluates to an absolute " +
			"path, you should make sure that that absolute directory exists on agent.")
	@ChoiceProvider("getAgentAddresses")
	@NotEmpty
	@Scriptable
	@ScriptApi("Get agent address.")
	public String getAgentAddress() {
		return agentAddress;
	}

	public void setAgentAddress(String agentAddress) {
		this.agentAddress = agentAddress;
	}
	
	@SuppressWarnings("unused")
	private static List<String> getAgentAddresses() {
		List<String> agentAddresses = new ArrayList<String>();
		for (GridNode node: AgentManager.instance.getBuildAgents())
			agentAddresses.add(node.getAddress());
		return agentAddresses;
	}
	
	private GridNode getAgent() {
		GridNode agent = Grid.instance.getNode(getAgentAddress());
		if (agent == null)
			throw new QuickbuildException("Unable to find build agent: " + getAgentAddress());
		return agent;
	}

	@Override
	public void download(Build build, String artifactFile, File destFile) {
		GridNode agent = getAgent();
		String buildPublishDir = agent.getNodeService().getBuildPublishDir(build.getId());
		Grid.instance.transferFile(agent, buildPublishDir + "/" + artifactFile, 
				Grid.instance.getLocalNode(), destFile.getAbsolutePath(), null, null);
	}

	@Override
	public void upload(File srcFile, Build build, String artifactFile) {
		GridNode agent = getAgent();
		String buildPublishDir = agent.getNodeService().getBuildPublishDir(build.getId());
		Grid.instance.transferFile(Grid.instance.getLocalNode(), srcFile.getAbsolutePath(), 
				agent, buildPublishDir + "/" + artifactFile, null, null);
	}

	@Override
	public String getUrl(Build build, String artifactFile) {
		String url = getAgent().getUrl() + DownloadServlet.SERVLET_PATH + "/" 
				+ build.getLatestAwareId() + "/" + artifactFile;
		if (Context.getUser() != null) {
			Date validUntil = new DateTime().plusMinutes(TOKEN_VALID_MINUTES).toDate();
			String token = SecurityHelper.generateAccessToken(Context.getUser(), validUntil);
			try {
				String encodedToken = URLEncoder.encode(token, Constants.UTF8);
				url += "?token=" + encodedToken;
			} catch (UnsupportedEncodingException e) {
				throw new RuntimeException(e);
			}
		}
		return url;
	}

	@Override
	public void cleanup(Build build, String artifactFile) {
		GridNode agent = getAgent();
		String buildPublishDir = agent.getNodeService().getBuildPublishDir(build.getId());
		agent.deleteFile(buildPublishDir + "/" + artifactFile);
	}
	
	@Override
	public BatchSupport getBatchSupport() {
		return new BatchSupport() {

			@Override
			public String getUrl(Build build, String artifactDir, String artifactPatterns) {
	            try {
					String url = getAgent().getUrl() + BatchDownloadServlet.SERVLET_PATH + "?" + 
							BatchDownloadServlet.BUILD_ID_PARAM + "=" + 
							build.getLatestAwareId() + "&" + 
							BatchDownloadServlet.SRC_PATH_PARAM + "=" +
							URLEncoder.encode(artifactDir, Constants.UTF8) + "&" + 
							BatchDownloadServlet.FILE_PATTERNS_PARAM + "=" + 
							URLEncoder.encode(artifactPatterns, Constants.UTF8);
					if (Context.getUser() != null) {
						Date validUntil = new DateTime().plusMinutes(TOKEN_VALID_MINUTES).toDate();
						String token = SecurityHelper.generateAccessToken(Context.getUser(), validUntil);
						url += "&token=" + URLEncoder.encode(token, Constants.UTF8); 
					}
					return url;
				} catch (UnsupportedEncodingException e) {
					throw new RuntimeException(e);
				}
			}

			@Override
			public void download(Build build, String artifactDir, String artifactPatterns, File destDir) {
				GridNode agent = getAgent();
				String buildPublishDir = agent.getNodeService().getBuildPublishDir(build.getId());
				Grid.instance.transferFiles(agent, buildPublishDir + "/" + artifactDir, 
						artifactPatterns, Grid.instance.getLocalNode(), destDir.getAbsolutePath(), false, null, null);
			}

			@Override
			public void upload(File srcDir, String srcPatterns, Build build, String artifactDir) {
				GridNode agent = getAgent();
				String buildPublishDir = agent.getNodeService().getBuildPublishDir(build.getId());
				Grid.instance.transferFiles(Grid.instance.getLocalNode(), srcDir.getAbsolutePath(), 
						srcPatterns, agent, buildPublishDir + "/" + artifactDir, false, null, null);
			}
			
			@Override
			public void cleanup(Build build, String artifactDir) {
				GridNode agent = getAgent();
				String buildPublishDir = agent.getNodeService().getBuildPublishDir(build.getId());
				if (artifactDir != null)
					agent.deleteDir(buildPublishDir + "/" + artifactDir);
				else
					agent.deleteDir(buildPublishDir);
			}
		};
	}

}
