package com.urbancode.air.plugin.test.junit

import org.apache.commons.httpclient.HttpClient
import org.apache.commons.httpclient.UsernamePasswordCredentials
import org.apache.commons.httpclient.auth.AuthScope
import org.apache.commons.httpclient.methods.StringRequestEntity
import org.apache.commons.httpclient.methods.PostMethod
import org.apache.commons.httpclient.params.HttpClientParams
import org.apache.commons.httpclient.protocol.Protocol
import org.apache.commons.httpclient.protocol.ProtocolSocketFactory

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

public class XMLHelper {
    
    //**************************************************************************
    // CLASS
    //**************************************************************************

    //**************************************************************************
    // INSTANCE
    //**************************************************************************
    private def testCount = 0
    private def suiteCount = 0
    
    public XMLHelper() {
    }
    
    def uploadResult (reportFiles, reportName, truncate) {
        def testsuiteName
        def xml
        def result
        def name
        def time
        testCount = 0
        suiteCount = 0
        
        def reportXml = new java.io.StringWriter()
        new groovy.xml.MarkupBuilder(reportXml).
        "test-report"("name":reportName,
        "type": 'JUnit',
        "job-id": System.getenv('JOB_ID')) {
            reportFiles.each {
                xml = it
                def rootNode = new XmlParser().parse(xml)
                if (rootNode.name().equals("testsuite")) {
                    suiteCount++
                    def testsuite = rootNode
                    if (truncate) {
                        testsuiteName = testsuite.@name.substring(testsuite.@name.lastIndexOf('.') + 1)
                    }
                    else {
                        testsuiteName = testsuite.@name
                    }
                    "test-suite"("name": testsuiteName) {
                         testsuite.testcase.each { testcase ->
                             testCount++
                             if (!testcase.failure && !testcase.error) {
                                 result = 'success'
                             }
                             else {
                                 result = 'failure'
                             }
                             if (testcase.@classname){
                                 name = testcase.@classname + ' ' +  testcase.@name
                             }
                                 
                             else {
                                 name = testcase.@name
                             }
                             time = testcase.@time
                             if (!time) {
                                 time = "0"
                             }
                             time = time.replaceAll(',', '')
                             time = (int) (Double.parseDouble(time) * 1000)
                             "test" ("name": name, "result": result ,"time": time) {
                                 if (result == 'failure') {
                                        def errorMsg = ""
                                        testcase.error.findAll { error -> errorMsg += 'Error Message: ' + error.@message + ' Error Text: ' +  error.text() }
                                        testcase.failure.findAll { error -> errorMsg += 'Error Message: ' + error.@message + ' Error Text: ' + error.text() }
                                        "message"(errorMsg)
                                 }
                             }
                         }
                    }
                }
                else {
                    rootNode.testsuite.each { testsuite ->
                        suiteCount++
                        if (truncate) {
                            testsuiteName = testsuite.@name.substring(testsuite.@name.lastIndexOf('.') + 1)
                        }
                        else {
                            testsuiteName = testsuite.@name
                        }
                        "test-suite"("name": testsuiteName) {
                             testsuite.testcase.each { testcase ->
                                 testCount++
                                 if (!testcase.failure && !testcase.error) {
                                     result = 'success'
                                 }
                                 else {
                                     result = 'failure'
                                 }
                                 
                                 if (testcase.@classname){
                                     name = testcase.@classname + ' ' +  testcase.@name
                                 }
                                 
                                 else {
                                     name =  testcase.@name
                                 }
                                 
                                 time = testcase.@time
                                 if (!time) {
                                     time = "0"
                                 }
                                 time = time.replaceAll(',', '')
                                 time = (int) (Double.parseDouble(time) * 1000)
                                 "test" ("name": name, "result": result ,"time": time) {
                                    if (result == 'failure') {
                                        def errorMsg = ""
                                        testcase.error.findAll { error -> errorMsg += 'Error Message: ' + error.@message + ' Error Text: ' +  error.text() }
                                        testcase.failure.findAll { error -> errorMsg += 'Error Message: ' + error.@message + ' Error Text: ' + error.text() }
                                        "message"(errorMsg)
                                    }
                                 }
                             }
                        }
                    }
                }
            }
        }
        
        if (reportXml) {
            sendPostRequest(reportName, reportXml.toString())
        }
        else {
            println 'No report was able to be generated'
        }
    }
    
    private void sendPostRequest(String name, String xml) {
        def authToken = System.getenv("AUTH_TOKEN")
        String buildLifeId = System.getenv("BUILD_LIFE_ID")
        name = encode(name)
        
        // construct the URL with property replacements
        String baseUrl = System.getenv("WEB_URL")
        
        baseUrl += baseUrl.endsWith("/") ? "" : "/"
        String url = baseUrl + "rest/buildlife/${buildLifeId}/tests?reportName=${name}"
        
        println "Sending request to $url"

        ProtocolSocketFactory socketFactory = new OpenSSLProtocolSocketFactory()
        Protocol https = new Protocol("https", socketFactory, 443)
        Protocol.registerProtocol("https", https)

        PostMethod postMethod = new PostMethod(url)
        if (authToken) {
            postMethod.setRequestHeader("Authorization-Token", authToken)
            postMethod.setRequestHeader("Content-Type", "application/xml")
        }
        
        println "Sending ${testCount} test results from ${suiteCount} test suites"
        postMethod.setRequestEntity(new StringRequestEntity(xml));

        HttpClient client = new HttpClient()

        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("Failed to upload JUnit test results. StatusCode: ${responseCode}")
        }
    }
    
    private boolean isGoodResponseCode(int responseCode) {
        return responseCode >= 200 && responseCode < 300;
    }
    
    private def encode = {
        return !it ? it : new java.net.URI(null, null, it, null).toASCIIString()
    }
}