package com.urbancode.air.plugin.automation

import org.jsoup.*

import org.apache.commons.httpclient.*
import org.apache.commons.httpclient.methods.*;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.protocol.*

import com.urbancode.commons.util.IO
import com.urbancode.commons.util.https.OpenSSLProtocolSocketFactory

import com.urbancode.air.*
import com.urbancode.ubuild.plugin.rtcworkitems.RTCWorkItemHelper;


public class PublishDefectReport extends AutomationBase {
    
    //*****************************************************************************************************************
    // CLASS
    //*****************************************************************************************************************
    
    //*****************************************************************************************************************
    // INSTANCE
    //*****************************************************************************************************************
    
    String workItemProperty = "workItems"
    String defectPattern
    
    HttpClient client= new HttpClient();
    
    public void execute(props, apTool) {
        publishReport(props, apTool);
    }
    
    public void publishReport(props, apTool) {
        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 defectIdSet = [] as Set
            def defectIdToChangeSetListMap = [:]
            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)) {
                                    defectIdSet << item
                                }
                            }
                        }
                    } 
                
                    defectIdSet.each { item ->
                        def currentCommentSet = defectIdToChangeSetListMap[item] ?: [] as Set
                        currentCommentSet << element.'change-id'.text() ?: element.'id'.text()
                        defectIdToChangeSetListMap.put(item, currentCommentSet)
                    }
                }
            }
            if (defectIdSet.size() == 0) {
                println "No RTC work items were found in the source changes.";
            }
            else {
                println "${defectIdSet.size()} RTC work items were found in the source changes.";
                println("Creating Issues.xml.")
                def issuesXml = new java.io.StringWriter()
                new groovy.xml.MarkupBuilder(issuesXml).issues() {
                    for (def defectId in defectIdSet) {
                        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";
                            //create issue element
                            String changeId = defectIdToChangeSetListMap.get(defectId).join(',');
                            
                            issue(id:defectId, "issue-tracker":"RTC", "change-id":changeId) {
                                name(defectXml[RTCWorkItemHelper.n_oslc_cm.ChangeRequest][RTCWorkItemHelper.n_dc.title].text())
                                String descrip = Jsoup.parse(defectXml[RTCWorkItemHelper.n_oslc_cm.ChangeRequest][RTCWorkItemHelper.n_dc.description].text()).text();
                                description(descrip)
                                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()
                                status(reportStatus);
                                type(reportType);
                                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) {
        HttpMethod queryMethod = null;
        String resp ="";
        try {
            queryMethod = new GetMethod(queryUrl + '.xml?oslc_cm.query=' + URLEncoder.encode('dc:identifier="' + identifier + '"', "UTF-8"));
            queryMethod.setRequestHeader("Accept", "application/x-oslc-cm-change-request+xml");
            def result = client.executeMethod(queryMethod);
            resp = queryMethod.getResponseBodyAsString();
        }
        finally {
            if (queryMethod != null) {
                queryMethod.releaseConnection();
            }
        }
        return resp;
    }
    
    def final String getURL(String url, HttpClient client) {
        HttpMethod getMethod = null;
        String resp = "";
        try {
            getMethod = new GetMethod(url);
            getMethod.setRequestHeader("Accept", "application/xml");
            def result = client.executeMethod(getMethod);
            resp=getMethod.getResponseBodyAsString();
        }
        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"
        ProtocolSocketFactory socketFactory = new OpenSSLProtocolSocketFactory()
        Protocol https = new Protocol("https", socketFactory, 443)
        Protocol.registerProtocol("https", https)
        
        GetMethod getMethod = new GetMethod(changesRequestUrl)
        if (authToken) {
            getMethod.setRequestHeader("Authorization-Token", authToken)
        }
        
        def responseCode = client.executeMethod(getMethod)
        InputStream responseStream = getMethod.getResponseBodyAsStream()
        
        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"
        
        ProtocolSocketFactory socketFactory = new OpenSSLProtocolSocketFactory()
        Protocol https = new Protocol("https", socketFactory, 443)
        Protocol.registerProtocol("https", https)
 
        PostMethod postMethod = new PostMethod(postUrl)
        try {
            if (authToken) {
                postMethod.setRequestHeader("Authorization-Token", authToken)
                postMethod.setRequestHeader("Content-Type", "application/xml")
            }
            
            postMethod.setRequestEntity(new StringRequestEntity(issuesXml));
    
            def responseCode = client.executeMethod(postMethod)
            InputStream responseStream = postMethod.getResponseBodyAsStream()
            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()
        }
    }
    
}
