/*
* 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.scm

import java.text.SimpleDateFormat

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

public class SCMChangelog extends SCMStep {
    
    //**************************************************************************
    // CLASS
    //**************************************************************************

    //**************************************************************************
    // INSTANCE
    //**************************************************************************
      
    // Changeset Variables
    final def TFS_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss 'Z'")
    final def AIR_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.S Z");
    
    final ChangeSetFilter changeSetFilter = new ChangeSetFilter()
    Date startDate
    Date endDate
    String label
    String changeset
    String changesUrl
    String tfToolPath
    
    List<ChangeSet> changeSets
    
    public def execute() {
        TFS_DATE_FORMAT.timeZone = TimeZone.getTimeZone("UTC")
        AIR_DATE_FORMAT.timeZone = TimeZone.getTimeZone("GMT")
        
        def logOutput = runLogCommand()
        changeSets = parseChangesets(logOutput)
        
        if (changeSets) {
            ChangeSetXMLHelper xmlHelper = new ChangeSetXMLHelper()
            xmlHelper.repoType = REPO_TYPE;
            def xml = xmlHelper.toXML(changeSets);
            sendPostRequest(xml)
        }
        else {
            println "No changes detected"
            return null
        }
    }
    
    public List<ChangeSet> parseChangesets(String logOutput) {
        CommandHelper ch = new CommandHelper(workDir)
        
        List<ChangeSet> changeSets = []
        def changeLog = new XmlParser().parseText(logOutput)

        changeLog.'change-sets'[0].'change-set'.each { changeXml ->
            ChangeSet changeSet = new ChangeSet()

            def changeSetDate = TFS_DATE_FORMAT.parse(changeXml.'change-date'[0].text())
            if (changeSetDate.compareTo(endDate) <= 0 && changeSetDate.compareTo(startDate) >= 0) {
                
                changeSet.id = changeXml.'change-id'.text()
                changeSet.date = changeSetDate
                changeSet.user = changeXml.'change-user'.text()
                changeSet.message = changeXml.'change-comment'.text()
                
                changeXml.'change'.each {change ->
                    ChangeSetFileEntry entry = new ChangeSetFileEntry()
                    entry.type = change.'change-type'[0].text()
                    entry.path = change.'change-item'[0].text()
                    changeSet.fileSet << entry
                }
                
                if (changeXml.'work-item'?.size() > 0) {
                    def workItems = changeXml.'work-item'.collect{ it.text().trim() }.join(',')
                    changeSet.properties.put("workItems", workItems)
                }
                
                boolean hasAllowedAuthor = changeSet.hasAllowedAuthor(changeSetFilter);
                boolean hasAllowedFile = changeSet.hasAllowedPath(changeSetFilter);
                
                if (hasAllowedAuthor && hasAllowedFile) {
                    changeSets << changeSet
                }
                else {
                    def message = new StringBuilder("Changeset ${changeSet.id} skipped because ")
                    if (!hasAllowedAuthor) {
                        message << "it has excluded author ${changeSet.user}"
                    }
                    if (!hasAllowedAuthor && !hasAllowedFile) {
                        message << " and "
                    }
                    if (!hasAllowedFile) {
                        message << "it contains only excluded file paths (${changeSet.fileSet.collect{it.path}})"
                    }
                    println message
                }
            }
        }
        
        return changeSets
    }
    
    public String runLogCommand() {
        CommandHelper ch = new CommandHelper(workDir)
        
        def changes
        def fileSep = File.separator
        def exe = tfToolPath ?: System.getenv("PLUGIN_HOME") + "${fileSep}lib${fileSep}tftool" + tfsVersion + ".exe"
        def command = [exe, 'changelog', "/server:$serverUrl"]
        
        if (serverUserName && serverPassword) {
                command << "/user:$serverUserName"
                command << "/password:$serverPassword"
        }
        
        if (changeset) {
            command << "/version:C$changeset"
        }
        else if (label) {
            command << "/version:L$label"
        }
        
        command << "/from:${TFS_DATE_FORMAT.format(startDate)}"
        command << "/to:${TFS_DATE_FORMAT.format(endDate)}"
        command << sourceLocation
        
        ch.runCommand('Getting Changelog', command) { proc ->
            proc.consumeProcessErrorStream(System.out)
            proc.out.close()
            changes = proc.text
        }
        
        return changes
    }
    
    private void sendPostRequest(String xml) {
        // construct the URL with property replacements
        String url = changesUrl
        def authToken = System.getenv("AUTH_TOKEN")
        
        println "Sending request to $url"
 
        // Debug/testing stub
        if (url.startsWith("file://")) {
            File xmlOut = new File(workDir, "xmlOut.xml")
            xmlOut << xml
        }
        else {
            HttpPost postMethod = new HttpPost(url)
            if (authToken) {
                postMethod.addHeader("Authorization-Token", authToken)
                postMethod.addHeader("Content-Type", "application/xml")
            }
            
            println "Sending ${changeSets.size()} changes"
            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 source changes. StatusCode: ${responseCode}")
            }
        }
    }
    
    private boolean isGoodResponseCode(int responseCode) {
        return responseCode >= 200 && responseCode < 300;
    }
}