package com.urbancode.air.plugin.onetest

import org.apache.http.Header
import org.apache.http.NameValuePair
import org.apache.http.client.entity.UrlEncodedFormEntity
import org.apache.http.client.methods.CloseableHttpResponse
import org.apache.http.client.methods.HttpGet
import org.apache.http.client.methods.HttpPost
import org.apache.http.entity.StringEntity
import org.apache.http.impl.client.CloseableHttpClient
import org.apache.http.impl.client.LaxRedirectStrategy
import org.apache.http.message.BasicNameValuePair
import org.apache.http.util.EntityUtils
import org.apache.commons.codec.net.URLCodec
import com.urbancode.air.XTrustProvider

//import com.urbancode.commons.httpcomponentsutil.HttpClientBuilder
import com.urbancode.util.ClientConfiguration
import com.urbancode.util.OneTestUCDConstants
import com.urbancode.util.TokenUtil
import groovy.json.JsonSlurper;
import java.util.concurrent.TimeUnit

class OneTestUCDHelper {

	def airPluginTool;
	def props = [];

	URL otsUrl;
	String userName
	String password
	String serverUrl;
	String offlineToken;
	String filepath;
	String repoId;
	String projId;
	String repoName;
	String projName;
	String assetName;
	String type;
	String desktopProjectId;
	String branchName;
	String startDate;
	String datasetsString;
	String variablesString;
	String tagsString;
	String[] datasetsList
	String[] variablesList
	String[] tagsList
	def datasets;
	def variables;
	def tags;
	String secretsCollectionName;
	String ritEnv;
	String proxyHost;
	String proxyPass;
	String proxyPort;
	String proxyUser;
	

	CloseableHttpClient client;

	TokenUtil util;
	String accessToken;
        String refreshToken;
			public OneTestUCDHelper(def airPluginToolIn) {
				
				
	    airPluginTool = airPluginToolIn;
	    props = airPluginTool.getStepProperties();
		
		filepath =props['filepath']?.trim();
		projName = props['projName']?.trim();
		repoName = props['repoName']?.trim();//'git@github01.hclpnp.com:akmathur/onetest-server-sample.git'
		userName = props['userName']?.trim();
		password = props['password']?.trim();
		//type = props['type']?.trim();
		branchName = props['branchName']?.trim();
		startDate =props['startDate']?.trim();
		datasetsString =props['datasets']
		variablesString =props['variables']
		tagsString = props['tags']
		secretsCollectionName = props['secretsCollectionName']?.trim();
		ritEnv = props['ritEnv']?.trim();
		proxyHost = props['proxyHost']?.trim();
		offlineToken= props['offlineToken']?.trim();
		try {
			otsUrl = new URL(props['otsUrl'].trim());
		} catch (MalformedURLException e) {
			println e.getMessage();
			System.exit(1);
		}
		validateInput()
		assetName = filepath.tokenize('/').last()
		assetName = assetName.take(assetName.lastIndexOf('.'))
        
		//Default branch to master if null
		if(!branchName){
			branchName = "master"
		}

		datasets = []
		if (datasetsString) {
			datasetsList = datasetsString.split("\n")*.trim()
			for (dataset in datasetsList) {
				datasets.add('{"internalResourceId":"' + dataset.tokenize(",")[0] +
						'", "override":"' + dataset.tokenize(",")[1] +
						'", "source":"' + dataset.tokenize(",")[2] +
						'"}')
			}
		}

		variables = []
		if (variablesString) {
			variablesList = variablesString?.split('\n')*.trim()
			for (variable in variablesList) {
				variables.add('{"name":"' + variable.tokenize(",")[0] + '", "value":"' + variable.tokenize(",")[1] + '"}')
			}
		}

		tags = []
		if (tagsString) {
			tagsList = tagsString?.split(',')*.trim()
			tags = tagsList.collect { '{"tag":"' + it + '"}' };
		}

		if (!secretsCollectionName) {
			secretsCollectionName = null
		}


		

		
		serverUrl =    'https://' + otsUrl.getAuthority()

		println "[Info] Using serverUrl: '${serverUrl}'";

			

		if (proxyHost) {
			proxyPass = props['proxyPass'];
			proxyPort = props['proxyPort'].trim();
			proxyUser = props['proxyUser'].trim();

			println "[Info] Using Proxy Host ${proxyHost}";
			println "[Info] Using Proxy Port ${proxyPort}";

		
			if(proxyUser) {
				println "[Info] Using Proxy User ${proxyUser}";
			}
		}

		
		
		 
		 util = new TokenUtil()
		 def tokenResponse = util.getToken(offlineToken,serverUrl)
		 accessToken = tokenResponse.access_token;
		 refreshToken = tokenResponse.refresh_token;
		 client = util.getClient();
	}
     def getProjectIdByName(String projName)
			{
		 String encodedProjName = URLEncoder.encode(projName, 'UTF-8')
		 String url = serverUrl+'/rest/projects?archived=false&member=true&name='+encodedProjName
		 HttpGet get=new HttpGet(url)
		 get.addHeader("Content-Type", "application/json");
		 get.addHeader("Authorization", "Bearer " + accessToken);
		 def response = client.execute(get)
		 def entity = EntityUtils.toString(response.getEntity());
		 def slurper = new JsonSlurper();
		 def parsedJson = slurper.parseText(entity);
		 def projId = parsedJson.data.id
		 if(!projId)
		 {
		 validateResponse(response,"Project Name",projName)
		 }
			return projId
					
	 }
	 
	 def getRepoIdByName(String repoName, String projId)
	 {   
		 String repoId =null
		 String url = serverUrl+'/rest/projects/'+projId+'/repositories/'
		 url= URLifyII(url)
		 HttpGet get = new HttpGet(url)
		 get.addHeader("Content-Type", "application/json");
		 get.addHeader("Authorization", "Bearer " + accessToken);
		 def response = client.execute(get)
		 
		 def entity = EntityUtils.toString(response.getEntity());
		 
		         def slurper = new JsonSlurper();
				 def parsedJson = slurper.parseText(entity);
					
			def repoArray= parsedJson.content
			
			
			for(def repos : repoArray)
			{   
				
				if(repos.uri.equals(repoName))
					{
						repoId= repos.id
					}
			}
					if(!repoId)
			        {
					validateResponse(response, "Repository Name", repoName)
			        }
					return repoId;
					
	 }
	 
	 def getAssetByName(String repoId, String projId)
	 {   
		 String assetId
		 String encodedAssetName = URLEncoder.encode(assetName, 'UTF-8')
		 String encodedBranchName = URLEncoder.encode(branchName, 'UTF-8')
		 String url=serverUrl+'/rest/projects/'+projId+'/assets/?name='+encodedAssetName+'&revision='+encodedBranchName
		 url= URLifyII(url)
		 HttpGet get = new HttpGet(url)
		 get.addHeader("Content-Type", "application/json");
		 get.addHeader("Authorization", "Bearer " + accessToken);
		 def response = client.execute(get)
		 
		 def entity = EntityUtils.toString(response.getEntity());
		 
				 def slurper = new JsonSlurper();
				 def parsedJson = slurper.parseText(entity);
				 	
			def assetArray= parsedJson.content
			
			
			for(def asset : assetArray)
			{
				
				if(asset.path.equals(filepath)&&asset.repository_id.equals(repoId))
					{
						assetId= asset.id
						type = asset.external_type
						desktopProjectId=asset.desktop_project_id
					}
			}
					if(!assetId)
					{
					validateResponse(response, "Asset Name", filepath)
					}
					return assetId;
					
	 }
	
	def startJob() {
		
		 projId = getProjectIdByName(projName)[0]
		
		 repoId = getRepoIdByName(repoName, projId)
		 
		 String assetId = getAssetByName(repoId, projId)
		 if(type.equals("APISUITE"))
		 {   if(!ritEnv)
			 {
				 println("Relative environment is mandatory attribute in order to run API Test")
				 System.exit(1);
			 }
			 else
			 {
				 validateEnvironment(ritEnv)
			 }
		 }
		 
		 String location =null;
		 
		HttpPost startJobMethod = new HttpPost(serverUrl + '/rest/projects/' + projId + '/jobs/');
		startJobMethod.addHeader("Accept", "application/json");
		startJobMethod.addHeader("Content-Type", "application/json");
		startJobMethod.addHeader("Authorization", "Bearer " + accessToken);
		String methodBody =
				'{"filepath": "' + filepath + '",' +
				'"repoid": "' + assetId + '",' +
				'"projId": "' + projId + '",' +
				'"assetName": "' + assetName + '",' +
				'"type": "' + type + '",' +
				'"branchName": "' + branchName + '",' +
				'"startDate": "' + startDate + '",' +
				'"datasets": ' + location + ',' +
				'"tags": ' + tags.toString() + ',' +
				'"offlineToken": "' + refreshToken + '",' +
				'"variables": ' +location + ',' +
				'"locations": ' + location + '}'
				
				if(type.equals("APISUITE"))
				{
				  methodBody =
				'{"filepath": "' + filepath + '",' +
				'"repoid": "' + assetId + '",' +
				'"projId": "' + projId + '",' +
				'"assetName": "' + assetName + '",' +
				'"type": "' + type + '",' +
				'"branchName": "' + branchName + '",' +
				'"startDate": "' + startDate + '",' +
				'"datasets": ' + location + ',' +
				'"tags": ' + tags.toString() + ',' +
				'"offlineToken": "' + refreshToken + '",' +
				'"variables": ' +location + ',' +
				'"ritEnv": "' +ritEnv + '",' +
				'"locations": ' + location + '}'
				}
				
				
		StringEntity postEntity = new StringEntity(methodBody);
		startJobMethod.setEntity(postEntity);

		def resp = client.execute(startJobMethod);
      
		
		
      
		def statusCode = resp.getStatusLine().getStatusCode();
		
		if (statusCode != 201) {
			println "[Error] Request failed with status ${statusCode}. Exiting Failure.";
			println('Response:\n' + resp.entity ?.content ?.getText("UTF-8"));
			System.exit(1);
		}
		def entity = EntityUtils.toString(resp.getEntity());
		
				def slurper = new JsonSlurper();
				def parsedJson = slurper.parseText(entity);
				def map=entity.properties
				def jobId = parsedJson.id
				def resultId = parsedJson.resultId;
				String status = parsedJson.status;
				String prevStatus = status;
				println("JobId is ="+jobId+"   "+"ResultsId is= "+resultId+"   "+"Status is = "+status)
				if(!(status.equals("COMPLETE") || status.equals("COMPLETE_WITH_ERROR") || status.equals("STOPPED_BY_USER") || status.equals("STOPPED_AUTOMATICALLY") || status.equals("INCOMPLETE") || status.equals("CANCELED") || status.equals("LAUNCH_FAILED"))) {
					
											String statusURL = serverUrl+"/rest/projects/"+projId+"/jobs/"+jobId;
					
											
											while( ! (status.equals("COMPLETE") || status.equals("COMPLETE_WITH_ERROR") || status.equals("STOPPED_BY_USER") || status.equals("STOPPED_AUTOMATICALLY") || status.equals("INCOMPLETE") || status.equals("CANCELED") || status.equals("LAUNCH_FAILED")) )  {
												TimeUnit.SECONDS.sleep(10);
												HttpGet get = new HttpGet(statusURL);
											    get.addHeader("Content-Type", "application/json");
											    get.addHeader("Authorization", "Bearer " + accessToken);
												CloseableHttpResponse response = client.execute(get);
												def httpStatus = response.getStatusLine().getStatusCode();
												if(httpStatus == 401)
												{   
													def tokenResponse = util.getToken(offlineToken,serverUrl)
													accessToken = tokenResponse.access_token;
													refreshToken = tokenResponse.refresh_token;
													client = util.getClient();
												}
												def resEntity = EntityUtils.toString(response.getEntity());
												def resEntitySlurper = new JsonSlurper();
												def parsedRes = resEntitySlurper.parseText(resEntity);
												status = parsedRes.status
					                            if(status!=null && !prevStatus.equals(status)) {
													
													println("Test execution status: "+status);
												}
					                 
												prevStatus = status;
												
											}
											
											getVerdict(projId,resultId,status)
					
										}
										
										else
										{
											getVerdict(projId,resultId,status)
										}
										
		if (statusCode != 201) {
			println "[Error] Request failed with status ${statusCode}. Exiting Failure.";
			println('Response:\n' + resp.entity ?.content ?.getText("UTF-8"));
			System.exit(1);
		}
	}
	
	def getVerdict(String projId, def resultId, String status)
	{
		HttpGet get = new HttpGet(serverUrl+"/rest/projects/"+projId+"/results/"+resultId)
		get.addHeader("Content-Type", "application/json");
		get.addHeader("Authorization", "Bearer " + accessToken);
		def response = client.execute(get)
		println("Job Status = "+response.getStatusLine().getStatusCode())
		def entity = EntityUtils.toString(response.getEntity());
		
				def slurper = new JsonSlurper();
				
				   def parsedJson = slurper.parseText(entity);
				   println("JOB VERDICT: "+parsedJson.verdict)
				   if(!(status.equals("CANCELED") || status.equals("LAUNCH_FAILED"))){
					   
					   
											   def reportArray = parsedJson.reports
					   
											   if(reportArray.size()>0)
											   {
												   
												   for(def jsonObj : reportArray)
												   {
													   String reportName = jsonObj.name
													   String href = jsonObj.href
													   println(reportName + " : "+new URI(serverUrl).resolve(href));
												   }
											   }
										   }
	}
	
	def ClientConfiguration buildClientConfig()
	{
		ClientConfiguration config = new ClientConfiguration();
		String api = serverUrl+'/rest/tokens/';
		String ctype = "application/x-www-form-urlencoded";
		String data = '';
		config.setApi(api);
		config.setContentType(ctype);
		config.setPassword(password);
		config.setUser(userName);
		config.setReturnValueOnly(true);
		config.setMethod('POST');
		config.setPostingData(data);
		config.setLoggingEnabled(true);
		
		return config
	}
	
	def validateInput()
	{
		boolean invalid = false
		String errorMsg
		if(!otsUrl)
		{
			invalid=true
			errorMsg="One Test Server URL can not be blank"
		} else
		if(!userName)
			{
				invalid=true
				errorMsg="User Name can not be blank"
			} else
		if(!password)
			{
				invalid=true
				errorMsg="Password can not be blank"
			} else
			
		if(!filepath)
			{
				invalid=true
				errorMsg="filepath can not be blank"
			} else
		if(!repoName)
			{
				invalid=true
				errorMsg="Repository Name can not be blank"
			} else
		if(!projName)
			{
				invalid=true
				errorMsg="Project Name can not be blank"
			} else
		/*if(!type)
			{
				invalid=true
				errorMsg="Asset Type can not be blank"
			} */
		if(invalid)	
		{
			Println("***************  "+errorMsg+"   ********************")
			System.exit(1);
		}
	}  
	
	def String URLifyII(String text) {
		
				  int startIndex = 0;
				  int endIndex = text.length() - 1;
				  StringBuilder urlifiedText = new StringBuilder();
		
				  // Find first non-space character
				  while (text.charAt(startIndex) == ' ' && startIndex < endIndex) {
					startIndex++;
				  }
		
				  // Find last non-space character
				  while (text.charAt(endIndex) == ' ' && endIndex >= startIndex) {
					endIndex--;
				  }
		
				  // Repeat text, and replace spaces with %20
				  for (int i=startIndex; i <= endIndex; i++) {
					if (text.charAt(i) != ' ') {
					  urlifiedText.append(text.charAt(i));
					} else {
					  urlifiedText.append("%20");
					}
				  }
		
				  return urlifiedText.toString();
				}
	
	def validateResponse(def response,String missingAttr,String attrVal)
	{      def errorMsg = '';
		   if(missingAttr.equals("Asset Name"))
		   {
			   errorMsg = OneTestUCDConstants.RESPONSE_ERROR_FILEPATH_ASSET.replace("<varName>",attrVal)
		   }
		   else
		   {
		    errorMsg = OneTestUCDConstants.RESPONSE_ERROR.replace("<missingVar>",missingAttr).replace("<varName>", attrVal)
		   }
		   
			   println(errorMsg)
			   System.exit(1);
		
			}
			
	def validateEnvironment(def ritEnv)	
	{   
		String encodedBranchName = URLEncoder.encode(branchName, 'UTF-8')
		HttpGet get = new HttpGet(serverUrl+"/rest/projects/"+projId+"/assets/?assetTypes=environment&revision="+encodedBranchName+"&desktopProjectId="+desktopProjectId)
		get.addHeader("Content-Type", "application/json");
		get.addHeader("Authorization", "Bearer " + accessToken);
		def response = client.execute(get)
		def entity = EntityUtils.toString(response.getEntity());
		
				def slurper = new JsonSlurper();
				
				   def parsedJson = slurper.parseText(entity);
				   def total = parsedJson.totalElements;
				   def retrievedEnvName;
				   def gotEnv = false;
				   if (total > 0) {
					   for (def i = 0; i < total; i++) {
						   retrievedEnvName = parsedJson.content[i].name;
						   if (ritEnv == retrievedEnvName) {
						        gotEnv = true;
							}
					   }
					   if (gotEnv == false) {
						   println("The test environment " + ritEnv + " is not valid for the test.");
						   System.exit(1);
					   }
				   }
				   
		
	}		

}
