package com.urbancode.air.plugin.automation

import org.apache.commons.httpclient.*
import org.apache.commons.httpclient.methods.*
import org.apache.commons.httpclient.protocol.*
import org.apache.http.HttpResponse
import org.apache.http.client.HttpClient
import org.apache.http.client.methods.HttpGet
import org.apache.http.client.methods.HttpPost
import org.apache.http.entity.StringEntity
import org.jsoup.*

import com.urbancode.air.*
import com.urbancode.commons.httpcomponentsutil.HttpClientBuilder
import com.urbancode.commons.util.IO
import com.urbancode.ubuild.plugin.rtcworkitems.RTCWorkItemHelper


public class PublishDefectReport extends AutomationBase {
    
    //*****************************************************************************************************************
    // CLASS
    //*****************************************************************************************************************
    
    //*****************************************************************************************************************
    // INSTANCE
    //*****************************************************************************************************************
    
    String workItemProperty = "workItems"
    java.util.regex.Pattern defectPattern
    
    HttpClient client = null
    
    public void execute(props, apTool) {
        HttpClientBuilder builder = new HttpClientBuilder();
        builder.setTrustAllCerts(true);
        client = builder.buildClient();
        
        XTrustProvider.install();
        def rtcHelper = new RTCWorkItemHelper(props, apTool);
        def catalogurl = rtcHelper.getCatalogUrl(client);
        rtcHelper.authenticateRTCUser(catalogurl, client);
        def servicesUrl = rtcHelper.getServicesUrl(catalogurl, client);
        def workItemUrl = rtcHelper.getWorkItemUrl(servicesUrl, client);
        
        try {
            def changesXml = getChangeSets()
            System.out.println "Parsing source changes for work items..."
            
            def workItemMap = [:] as Map
            def workItems = [] as Set
            // get changeset info
            new XmlSlurper().parseText(changesXml)."change-set".each { element ->
                if (element.'properties'.size() > 0) {
                    element.'properties'.'property'.each {
                        if (workItemProperty.equalsIgnoreCase(it.'name'.text())) {
                            it.'value'.text().split(',').each { item ->
                                if (item.matches(defectPattern)) {
                                    workItems << item
                                }
                            }
                        }
                    } 
                    
                    workItems.each { item ->
                        def currentIdSet = workItemMap[item] ?: [] as Set
                        currentIdSet << element.'change-id'.text() ?: element.'id'.text()
                        workItemMap.put(item, currentIdSet)
                    }
                }
            }
            
            if (workItems.size() == 0) {
                println "No RTC work items were found in the source changes.";
            }
            else {
                println "${workItems.size()} RTC work items were found in the source changes.";
                println("Creating Issus.xml.")
                def issuesXml = new java.io.StringWriter()
                new groovy.xml.MarkupBuilder(issuesXml).issues() {
                    for (def defectId in workItems) {
                        println "Adding $defectId to Issues.xml."
                        def defectData = getWorkItemByIdentifier(defectId, workItemUrl, client);
                        def defectXml = new XmlParser().parseText(defectData);
                        if (defectXml.attributes()[RTCWorkItemHelper.n_oslc_cm.'totalCount'].equals("1")) {
                            println "Found RTC work item $defectId";
                            
                            for (def id in workItemMap[defectId]) {
                            
                                //create issue element
                                issue(id:defectId, "issue-tracker":"RTC", "change-id":id) {
                                    name(defectXml[RTCWorkItemHelper.n_oslc_cm.ChangeRequest][RTCWorkItemHelper.n_dc.title].text())
                                    def descrip = defectXml[RTCWorkItemHelper.n_oslc_cm.ChangeRequest][RTCWorkItemHelper.n_dc.description].text();
                                    def statusURL = defectXml[RTCWorkItemHelper.n_oslc_cm.ChangeRequest][RTCWorkItemHelper.n_rtc_cm.state][0].attributes()[RTCWorkItemHelper.n_rdf.'resource'];
                                    def statusXML=getURL(statusURL, client);
                                    def statusInfo = new XmlParser().parseText(statusXML);
                                    def typeURL = defectXml[RTCWorkItemHelper.n_oslc_cm.ChangeRequest][RTCWorkItemHelper.n_dc.type][0].attributes()[RTCWorkItemHelper.n_rdf.'resource'];
                                    def typeXML=getURL(typeURL, client);
                                    def typeInfo = new XmlParser().parseText(typeXML);
                                    def reportStatus = statusInfo[RTCWorkItemHelper.n_dc.title][0].text()
                                    def reportType = typeInfo[RTCWorkItemHelper.n_dc.title][0].text()
                                    type(reportType);
                                    status(reportStatus);
                                    description(descrip)
                                    String workitemUrl = url;
                                    if (!workItemUrl.endsWith('/')) {
                                        workitemUrl += '/'
                                    }
                                    workitemUrl += "web/projects/" + projectName + "#action=com.ibm.team.workitem.viewWorkItem&id=" + defectId;
                                    url(workitemUrl);
                                }
                            }
                        }
                        else {
                            println "Unable to find RTC work item $defectId";
                        }
                    }
                }
                println "Uploading Issues.xml.";
                sendPost(issuesXml.toString())
            }
        }
        catch (Exception e) {
            e.printStackTrace()
            System.exit(1)
        }
    }
    
    def final String getWorkItemByIdentifier(String identifier, String queryUrl, HttpClient client) {
        HttpGet queryMethod = null;
        String resp ="";
        try {
            queryMethod = new HttpGet(queryUrl + '.xml?oslc_cm.query=' + URLEncoder.encode('dc:identifier="' + identifier + '"', "UTF-8"));
            queryMethod.addHeader("Accept", "application/x-oslc-cm-change-request+xml");
            HttpResponse response = client.execute(queryMethod)
            resp = IO.readText(response.entity.content)
        }
        finally {
            if (queryMethod != null) {
                queryMethod.releaseConnection();
            }
        }
        return resp;
    }
    
    def final String getURL(String url, HttpClient client) {
        HttpGet getMethod = null;
        String resp = "";
        try {
            getMethod = new HttpGet(url);
            getMethod.addHeader("Accept", "application/xml");
            HttpResponse response = client.execute(getMethod)
            resp = IO.readText(response.entity.content)
        }
        finally {
            if (getMethod != null) {
                getMethod.releaseConnection();
            }
        }
        return resp;
    }
    
    private String getChangeSets() {
        def authToken = System.getenv("AUTH_TOKEN")
        int buildLifeId = Integer.parseInt(System.getenv("BUILD_LIFE_ID"))
        String webUrl = System.getenv("WEB_URL")
        webUrl += webUrl.endsWith("/") ? "" : "/"
        
        String changesRequestUrl = webUrl + "rest/buildlife/${buildLifeId}/sourcechanges"
        
        println "Getting source changes from server"
        
        HttpGet getMethod = new HttpGet(changesRequestUrl)
        if (authToken) {
            getMethod.addHeader("Authorization-Token", authToken)
        }
        
        HttpResponse response = client.execute(getMethod)
        def responseCode = response.statusLine.statusCode
        InputStream responseStream = response.entity.content
        
        String changesXml = IO.readText(responseStream)
        
        if (!isGoodResponseCode(responseCode)) {
            throw new Exception("Failed to get build life source changes from the server: $changesXml")
        }
        
        getMethod.releaseConnection()
        
        return changesXml
    }
    
    private boolean isGoodResponseCode(int responseCode) {
        return responseCode >= 200 && responseCode < 300;
    }
    
    private void sendPost(String issuesXml) {
        int buildLifeId = Integer.parseInt(System.getenv("BUILD_LIFE_ID"))
        def authToken = System.getenv("AUTH_TOKEN")
        String webUrl = System.getenv("WEB_URL")
        webUrl += webUrl.endsWith("/") ? "" : "/"
        
        String postUrl = webUrl + "rest/buildlife/${buildLifeId}/issues"
 
        HttpPost postMethod = new HttpPost(postUrl)
        try {
            if (authToken) {
                postMethod.addHeader("Authorization-Token", authToken)
                postMethod.addHeader("Content-Type", "application/xml")
            }
            
            postMethod.setEntity(new StringEntity(issuesXml));
    
            HttpResponse response = client.execute(postMethod)
            def responseCode = response.statusLine.statusCode
            InputStream responseStream = response.entity.content
            if (isGoodResponseCode(responseCode)) {
                IO.copy(responseStream, System.out)
                println ""
            }
            else {
                IO.copy(responseStream, System.err)
                throw new RuntimeException("RTC results upload to server failed with exit code: ${responseCode}")
            }
        }
        finally {
            postMethod.releaseConnection()
        }
    }
    
}
