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 com.urbancode.air.*
import com.urbancode.commons.httpcomponentsutil.HttpClientBuilder
import com.urbancode.commons.util.IO


public class PublishDefectReport extends AutomationBase {
    
    //=============================================================================================
    // CLASS
    //=============================================================================================
    
    //=============================================================================================
    // INSTANCE
    //=============================================================================================
    
    String workItemProperty = "workItems"
    String workItemRegex

    public void execute() {
        String changeXml = getChangeSets()
        publishDefectReport(changeXml)
    }
    
    public void publishDefectReport(String changeXml) {
        try {
            def workItemMap = [:] as Map
            def workItems = []
            // get changeset info
            new XmlSlurper().parseText(changeXml)."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(workItemRegex)) {
                                    workItems << item
                                }
                            }
                        }
                    } 
                
                    workItems.each { item ->
                        def currentCommentSet = workItemMap[item]?:[] as Set
                        currentCommentSet << element.'change-id'.text() ?: element.'id'.text()
                        workItemMap.put(item, currentCommentSet)
                    }
                }
            }
        
            // now prepare tftool command
            if (workItemMap.size() > 0) {
                def exe = System.getenv("PLUGIN_HOME") + "${fileSep}lib${fileSep}tftool" + tfsVersion + ".exe"
                def command = [exe, 'report', "/server:$serverUrl"]
        
                if (serverUserName && serverPassword) {
                    command << "/user:$serverUserName"
                    command << "/password:$serverPassword"
                }
        
                command << workItemMap.keySet().join(',')
        
                def workItemXmlBuilder = new groovy.xml.StreamingMarkupBuilder()
                def workItemXml
                def hasIssues = false
        
                ch.runCommand('Getting Work Item Report', command) { proc ->
                    proc.consumeProcessErrorStream(System.out)
                    proc.out.close()
        
                    workItemXml = workItemXmlBuilder.bind() {
                        proc.in.withReader { reader ->
                            issues() {
                                def line = reader.readLine()
                                while (line != null) {
                                    if (line.startsWith('ID:')) {
                                        hasIssues = true
                                        def workItemId = line.substring(3)
                                        println "Processing work item $workItemId"
                                        issue(id:workItemId, "issue-tracker":"TFS-WorkItems", "change-id":workItemMap[workItemId].join(',')) {
                                            line = reader.readLine()
                                            while (line != null && !line.startsWith("===")) {
                                                if (line.startsWith('Type:')) {
                                                    type(line.substring(5))
                                                }
                                                else if (line.startsWith('Title:')) {
                                                    name(line.substring(6))
                                                }
                                                else if (line.startsWith('State:')) {
                                                    status(line.substring(6))
                                                }
                                                else if (line.startsWith('Description:')) {
                                                    description(line.substring(12))
                                                }
                                                line = reader.readLine()
                                            }
                                        }
                                    }
                                    else {
                                        println line
                                    }
                                    line = reader.readLine()
                                }
                            }
                        }
                    }
                    sendPost(workItemXml.toString())
        
                    proc.waitFor()
        
                    if (proc.exitValue()) {
                        println "tftool command returned exit code ${proc.exitValue()}"
                        System.exit(1)
                    }
        
                    if (!hasIssues) {
                        println "TFS did not return any results for work items: ${workItemMap.keySet().join(',')}"
                    }
                }
            }
            else {
                println 'Did not detect any work items in changelog!'
            }
        }
        catch (Throwable t) {
            t.printStackTrace()
            System.exit 1
        }
    }
    
    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)
        }
        
        HttpClientBuilder builder = new HttpClientBuilder()
        builder.setTrustAllCerts(true)
        HttpClient client = builder.buildClient()
        
        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 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"
        
        println "Uploading Issues.xml"
        //println issuesXml
 
        HttpPost postMethod = new HttpPost(postUrl)
        try {
            if (authToken) {
                postMethod.addHeader("Authorization-Token", authToken)
                postMethod.addHeader("Content-Type", "application/xml")
            }
            
            postMethod.setEntity(new StringEntity(issuesXml));
    
            HttpClientBuilder builder = new HttpClientBuilder()
            builder.setTrustAllCerts(true)
            HttpClient client = builder.buildClient()
            
            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("TFS results upload to server failed. StatusCode: ${responseCode}")
            }
        }
        finally {
            postMethod.releaseConnection()
        }
    }
    
    private boolean isGoodResponseCode(int responseCode) {
        return responseCode >= 200 && responseCode < 300;
    }
}