/*
* Licensed Materials - Property of IBM Corp.
* IBM UrbanCode Build
* (c) Copyright IBM Corporation 2012, 2014. All Rights Reserved.
*
* U.S. Government Users Restricted Rights - Use, duplication or disclosure restricted by
* GSA ADP Schedule Contract with IBM Corp.
*/
package com.urbancode.air.plugin.test.cppunit

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.http.HttpResponse
import org.apache.http.client.HttpClient
import org.apache.http.client.methods.HttpPost
import org.apache.http.entity.StringEntity
import org.w3c.dom.Element;

import com.urbancode.commons.httpcomponentsutil.HttpClientBuilder
import com.urbancode.commons.util.IO

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

    //**************************************************************************
    // INSTANCE
    //**************************************************************************
    private def testCount = 0
    private def suiteCount = 0
    private Map<String, TestSuiteResult> suiteNameToSuite = new HashMap<String, TestSuiteResult>()
    
    public XMLHelper() {
    }
    
    def uploadResult (reportFiles, reportName) {
        def testsuiteName
        def result
        
        reportFiles.each { reportFile ->
            parseTestReport(reportFile)
        }
        
        def reportXml = new java.io.StringWriter()
        new groovy.xml.MarkupBuilder(reportXml).
        "test-report"("name": reportName, "type": 'CppUnit', "job-id": System.getenv('JOB_ID')) {
            suiteCount = suiteNameToSuite.keySet().size()
            suiteNameToSuite.keySet().each { suiteName ->
                "test-suite"("name": suiteName) {
                     suiteNameToSuite.get(suiteName).testCaseList.each { testCase ->
                         testCount++
                         if (testCase.isSuccessful()) {
                             result = 'success'
                         }
                         else {
                             result = 'failure'
                         }
                         "test" ("name": testCase.name, "result": result ,"time": "0") {
                             if (result == 'failure') {
                                 "message"(testCase.message)
                             }
                         }
                     }
                }
            }
        }
        
        if (reportXml) {
            sendPostRequest(reportName, reportXml.toString())
        }
        else {
            println 'No report was able to be generated'
        }
    }
    
    private void parseTestReport(def reportFile) {
        
        def rootNode = new XmlParser().parse(reportFile)
        if (rootNode.name().equals("TestRun")) {
            rootNode."FailedTests"."FailedTest".each { test ->
                def testCase = parseFailedTest(test)
                addTestCaseToAppropriateSuite(testCase, suiteNameToSuite)
            }
            
            rootNode."SuccessfulTests"."Test".each { test ->
                def testCase = parseSuccessfulTest(test)
                addTestCaseToAppropriateSuite(testCase, suiteNameToSuite)
            }
        }
    }
    
    private void addTestCaseToAppropriateSuite(TestCaseResult testCase, Map<String, TestSuiteResult> suiteNameToSuite) {
        String suiteName = testCase.scope
        if (suiteNameToSuite.containsKey(suiteName)) {
            suiteNameToSuite.get(suiteName).testCaseList.add(testCase)
        }
        else {
            TestSuiteResult suite = new TestSuiteResult()
            suite.name = suiteName
            suite.testCaseList.add(testCase)
            suiteNameToSuite.put(suiteName, suite)
        }
    }
    
    private TestCaseResult parseSuccessfulTest(def test) {
        TestCaseResult result = new TestCaseResult()
        
        String name = test."Name".text()
        if (name && name.indexOf("::") > 0) {
            result.name = name.substring(name.indexOf("::")  + "::".length(), name.length())
            result.scope = name.substring(0, name.indexOf("::"))
        }
        else {
            result.name = name
            result.scope = name
        }
        
        return result;
    }
    
    private TestCaseResult parseFailedTest(def test) {
        TestCaseResult result = new TestCaseResult()
        
        String name = test."Name".text()
        if (name && name.indexOf("::") > 0) {
            result.name = name.substring(name.indexOf("::")  + "::".length(), name.length())
            result.scope = name.substring(0, name.indexOf("::"))
        }
        else {
            result.name = name
            result.scope = name
        }
        
        result.failureType = test."FailureType".text()
        StringBuilder message = new StringBuilder(test."Message".text())
        if (test."Location") {
            message.append("\n")
                   .append("- Line: ")
                   .append(test."Location"."Line".text())
        }
        result.message = message.toString()
        
        return result;
    }
    
    private void sendPostRequest(String name, String xml) {
        def authToken = System.getenv("AUTH_TOKEN")
        String buildLifeId = System.getenv("BUILD_LIFE_ID")
        def encodedName = URLEncoder.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=${encodedName}"
        
        println "Sending request to $url"

        HttpPost postMethod = new HttpPost(url)
        if (authToken) {
            postMethod.addHeader("Authorization-Token", authToken)
            postMethod.addHeader("Content-Type", "application/xml")
        }
        
        println "Sending ${testCount} test results from ${suiteCount} test suites"
        postMethod.setEntity(new StringEntity(xml))
        
        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("Failed to upload CppUnit test results. StatusCode: ${responseCode}")
        }
    }
    
    private boolean isGoodResponseCode(int responseCode) {
        return responseCode >= 200 && responseCode < 300;
    }
}