package com.urbancode.air.plugin.automation

import com.urbancode.air.http.*

public class QCUpdate extends AutomationBase {
    
    String projectKey
    String captureMode
    String bugPattern
    String failMode
    String comment
    
    String assignee
    String summary
    String priority
    String status
    String severity
    String qcProjectKey
    String detectedBy
    Properties additionalFields
    def bugIds
    
    private def reqNumBugs
    private def foundBugIds
    private def exitCode = 0
    
    public void execute() {
        init()
        setupBugIds()
        
        if (bugIds && bugIds.size()) {
            println "Defect Ids to update: " + bugIds.join(", ")
            findBugs()
            updateDefects(foundBugIds)
        }
        else {
            println "No defects found in source changes. Nothing to do."
        }
        
        if (exitCode != 0) {
            System.exit(1)
        }
    }
    
    private void setupBugIds() {
        if ("list".equalsIgnoreCase(captureMode)) {
            bugIds = bugPattern.split(",")
        }
        else if ("regex".equalsIgnoreCase(captureMode)) {
            bugIds = getChangeSets()
        }
        else {
            throw new Exception("Unrecognized capture mode for determining defect Ids to update: " + capturemode);
        }
    }
    
    private def getChangeSets() {
        def changesXml = HttpHelper.getChangeSets()
        
        def pattern = java.util.regex.Pattern.compile(bugPattern)
        def Map<String, String> idToChangeComment = new HashMap<String,String>()

        // will throw a parse exception at random times - any ideas?
        new XmlSlurper().parseText(changesXml)."change-set".each { element ->
            def matcher = pattern.matcher(element.comment.text())
            while (matcher.find()) {
                def patternid
                def changeComment
                if (matcher.groupCount() > 0) {
                    // they specified a '(...)' group within the pattern, use that as the bug-id
                    patternid = matcher.group(1)
                }
                else {
                    // use the whole matching substring as the bug-id
                    patternid = matcher.group()
                }
                changeComment = (element."comment".text())
                if (patternid != null && changeComment != null) {
                    id2changecomment.put(patternid, changeComment)
                }
            }
        }
        
        return idToChangeComment.keySet()
    }
    
    private void findBugs() {
        reqNumBugs = bugIds.size()
        
        def outputFileName = ".numberOfBugsFound.txt"
        def findBugsCommand = [cscriptExe]
        findBugsCommand << PLUGIN_HOME + "\\qc_find_bugs.vbs"
        findBugsCommand << serverUrl
        findBugsCommand << username
        findBugsCommand << password
        findBugsCommand << domain
        findBugsCommand << project
        findBugsCommand << bugIds.join(",")
        findBugsCommand << outputFileName
        
        runCommand("Finding bugs by their ID", findBugsCommand)
        
        verifyBugs(outputFileName)
    }
    
    private void verifyBugs(String outputFileName) {
        def outputFile = new File(outputFileName)
        foundBugIds = outputFile.text.split(",")
        def actNumBugs = foundBugIds.length
        outputFile.delete()
        
        println "Found $actNumBugs of $reqNumBugs defects in Quality Center."
        
        if (reqNumBugs != actNumBugs) {
            if (failmode == "slow") {
                println "Fail mode is 'Fail' and not all requested defects were found in Quality Center. Scheduling failure after the found defects have been updated."
                exitcode = 1
            }
            else if (failmode == "warn") {
                println "Fail mode is 'Warn' and not all requested defects were found in Quality Center. Only logging a warning."
            }
            else { // default is fast
                println "Fail mode is 'Fail-fast' and not all requested defects were found in Quality Center. Failing now."
                throw new Exception("Not all requested defects were found in Quality Center and fail mode is 'Fail-fast'")
            }
        }
    }
    
    private void updateDefects(def foundBugIds) {
        for (foundBugId in foundBugIds) {
            
            def updateCommand = [cscriptExe]
            updateCommand << PLUGIN_HOME + "\\qc_update.vbs"
            updateCommand << serverUrl
            updateCommand << username
            updateCommand << password
            updateCommand << domain
            updateCommand << project
            updateCommand << foundBugId
            
            if (assignee) {
                updateCommand << "AssignedTo"
                updateCommand << assignee
            }
            if (summary) {
                updateCommand << "Summary"
                updateCommand << summary
            }
            if (priority) {
                updateCommand << "Priority"
                updateCommand << priority
            }
            if (status) {
                updateCommand << "Status"
                updateCommand << status
            }
            if (severity) {
                updateCommand << "Field('BG_SEVERITY')"
                updateCommand << severity
            }
            if (qcProjectKey) {
                updateCommand << "Project"
                updateCommand << qcProjectKey
            }
            if (detectedBy) {
                updateCommand << "DetectedBy"
                updateCommand << detectedBy
            }
            additionalFields.keySet().each() { additionalFieldName ->
                updateCommand << additionalFieldName
                updateCommand << additionalFields.getProperty(additionalFieldName)
            }
            
            runCommand("Updating defect ${foundBugId}", updateCommand)
            
            if (comment) {
                addComment(foundBugId)
            }
        }
    }
    
    private void addComment(def foundBugId) {
        def exit

        def commentFile = new File(".qccomment.html")
        commentFile.text = createHtmlFormattedComment(username, comment)
        
        try {
            def addCommentCommand = [cscriptExe]
            addCommentCommand << PLUGIN_HOME + "\\qc_add_comment.vbs"
            addCommentCommand << serverUrl
            addCommentCommand << username
            addCommentCommand << password
            addCommentCommand << domain
            addCommentCommand << project
            addCommentCommand << foundBugId
            addCommentCommand << commentFile

            runCommand("Adding comment to defect ${foundBugId}", addCommentCommand)
        }
        finally {
            commentFile.delete()
        }
    }
}