package com.urbancode.air.plugin.nunit

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
    private def builder
    def testSuiteStack = []
    
    public XMLHelper() {
    }
    
    def parseTestSuite (builder, testSuiteNode) {
        testSuiteStack << testSuiteNode.@name
        def testSuiteResultsNode = testSuiteNode.results
        if (testSuiteResultsNode.'test-case') {
            builder."test-suite"("name": testSuiteStack.join('.')) {
                testSuiteResultsNode.'test-case'.each { testCaseNode ->
                    if ('True'.equalsIgnoreCase(testCaseNode.@executed)) {
                        def name = testCaseNode.@name
                        def result = 'True'.equalsIgnoreCase(testCaseNode.@success) ? 'success' : 'failure'
                        def time = testCaseNode.@time
                        if (!time) {
                            time = 0
                        }
                        else {
                            time = time.replaceAll(',', '')
                            time = (long) (Double.parseDouble(time) * 1000)
                        }
                        builder."test"("name": name, "result": result, "time": time) {
                            if (result == 'failure') {
                                def errorMsg = 'Message: ' + testCaseNode.failure.message.text() + ' Stack Trace: ' +  testCaseNode.failure.'stack-trace'.text()
                                "message"(errorMsg)
                            }
                        }
                    }
                }
            }
        }
        testSuiteResultsNode.'test-suite'.each { childTestSuiteNode ->
            parseTestSuite(builder, childTestSuiteNode)
            testSuiteStack.pop()
        }
    }
    
    def parseTestResults (builder, testResultsNode) {
        testResultsNode.'test-suite'.each { testSuiteNode ->
            parseTestSuite(builder, testSuiteNode)
            testSuiteStack.pop()
        }
    }

    def uploadResult (reportFiles, reportName, truncate) {
        def testSuiteStack = []
        def testsuiteName
        def result
        def name
        def time
        testCount = 0
        suiteCount = 0
        
        def reportXml = new java.io.StringWriter()
        builder = new groovy.xml.MarkupBuilder(reportXml)
        builder."test-report"("name": reportName, "type": 'NUnit', "build-life-id": System.getenv("BUILD_LIFE_ID")) {
            reportFiles.each { xml ->
                def rootNode = new XmlParser().parse(xml)
                if (rootNode.name().equals("test-results")) {
                    def testResultsNode = rootNode
                    parseTestResults(builder, testResultsNode)
                }
            }
        }
        
        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()
    }
}