import com.urbancode.air.*
import com.urbancode.commons.util.CSVUtil
import com.urbancode.commons.util.https.OpenSSLProtocolSocketFactory
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.GetMethod
import org.apache.commons.httpclient.methods.PostMethod
import org.apache.commons.httpclient.methods.StringRequestEntity
import org.apache.commons.httpclient.params.HttpClientParams
import org.apache.commons.httpclient.protocol.Protocol
import org.apache.commons.httpclient.protocol.ProtocolSocketFactory
import org.apache.commons.httpclient.util.URIUtil
import org.codehaus.jettison.json.JSONArray
import org.codehaus.jettison.json.JSONObject

void getReportParams(Properties stepProps, JSONArray jsonParams, JSONObject query) {
    JSONArray jsonCriteriaArray = query.optJSONArray('criteria')
    if (jsonCriteriaArray) {
        for (int criteriaIndex=0; criteriaIndex<jsonCriteriaArray.length(); criteriaIndex++) {
            JSONObject jsonCriteria = jsonCriteriaArray.getJSONObject(criteriaIndex);
            if (jsonCriteria.has('paramName')) {
                def paramName = jsonCriteria.getString('paramName')
                def paramValue = stepProps.getProperty('params/' + paramName);
                JSONObject jsonParam = new JSONObject()
                jsonParam.put("name", paramName)
                if (jsonCriteria.getString('type') == 'in') {
                    JSONArray jsonValues = new JSONArray()
                    if (paramValue) {
                        for (String splitValue : paramValue.split('[\r\n]+')) {
                            jsonValues.put(splitValue)
                        }
                    }
                    jsonParam.put("value", jsonValues)
                }
                else {
                    jsonParam.put("value", paramValue)
                }
                jsonParams.put(jsonParam)
            }
        }
    }
    if (query.has('joins')) {
        JSONArray jsonJoins = query.getJSONArray('joins')
        for (int joinIndex=0; joinIndex<jsonJoins.length(); joinIndex++) {
            JSONObject jsonJoin = jsonJoins.getJSONObject(joinIndex)
            getReportParams(stepProps, jsonParams, jsonJoin.getJSONObject('query'))
        }
    }
}

final AirPluginTool airPluginTool = new AirPluginTool(this.args[0], this.args[1]);
final Properties stepProps = airPluginTool.getStepProperties()
final File workDir = new File('.').canonicalFile
final String osName = System.getProperty('os.name').toLowerCase(Locale.US)
final boolean windows = (osName =~ /windows/)

def urbancodeReportName = stepProps['urbancodeReportName']
def outputFile = new File(stepProps['outputFile'])
def outputFormat = stepProps['outputFormat']

def authToken = System.getenv('AUTH_TOKEN')
def reportUrl = System.getenv("WEB_URL") + "/rest2/reporting/reports/" + URIUtil.encodePath(urbancodeReportName)
def reportOuputUrl = reportUrl + "/output"

assert !workDir.isFile() : "Working directory ${workDir} is a file!"
workDir.mkdirs()
outputFile.delete()

Protocol.registerProtocol("https", new Protocol("https", new OpenSSLProtocolSocketFactory(), 443))

println "Running ${urbancodeReportName} and sending results to ${outputFile} as ${outputFormat}"

def jsonParams = new JSONArray()
println "Report Info URL: ${reportUrl}"
def method = new GetMethod(reportUrl)
try {
    if (authToken) {
        method.setRequestHeader("Authorization-Token", authToken)
    }
    method.setRequestHeader("Content-Type", "application/json")
    method.setRequestHeader("Accept", "application/json")
    
    HttpClient client = new HttpClient()
    
    def responseCode = client.executeMethod(method)
    if (responseCode >= 200 && responseCode < 300) {
        def jsonReport = new JSONObject(method.getResponseBodyAsString())
        def jsonQuery = jsonReport.get('query')
        getReportParams(stepProps, jsonParams, jsonQuery)
    }
    else {
        throw new Exception("Getting report information failed with response code: " + responseCode)
    }
}
finally {
    method.releaseConnection()
}

println "Report Output URL: ${reportOuputUrl}"
println "Parameters: ${jsonParams}"

method = new PostMethod(reportOuputUrl)
try {
    if (authToken) {
        method.setRequestHeader("Authorization-Token", authToken)
    }
    method.setRequestHeader("Content-Type", "application/json")
    if ('CSV'.equals(outputFormat)) {
        method.setRequestHeader("Accept", "text/csv")
    }
    else {
        method.setRequestHeader("Accept", "application/json")
    }
    method.setRequestEntity(new StringRequestEntity(jsonParams.toString(), "application/json", "UTF-8"));
    
    HttpClient client = new HttpClient()
    
    def responseCode = client.executeMethod(method)
    if (responseCode >= 200 && responseCode < 300) {
        def writer = new BufferedWriter(new FileWriter(outputFile))
        try {
            if ('CSV'.equals(outputFormat)) {
                writer << method.getResponseBodyAsStream()
            }
            else { // HTML
                def json = new JSONObject(method.getResponseBodyAsString())
                writer << '<html><head></head><body><table><thead><tr>'
                // write column headers
                def jsonColumns = json.getJSONArray('columns')
                for (int columnIndex=0; columnIndex<jsonColumns.length(); columnIndex++) {
                    writer << '<th>' + jsonColumns.getString(columnIndex).replace("<", "&lt;") + '</th>'
                }
                writer << '</tr></thead><tbody>'
                // write content
                def jsonResults = json.getJSONArray('results')
                for (int resultIndex=0; resultIndex<jsonResults.length(); resultIndex++) {
                    writer << '<tr>'
                    def jsonResultValues = jsonResults.getJSONArray(resultIndex)
                    for (int valueIndex=0; valueIndex<jsonResultValues.length(); valueIndex++) {
                        writer << '<td>' + jsonResultValues.getString(valueIndex).replace("<", "&lt;") + '</td>'
                    }
                    writer << '</tr>'
                }
                writer << '</tbody></table></body></html>'
            }
            writer.flush()
        }
        finally {
            writer.close()
        }
        println "Report output completed"
    }
    else {
        throw new Exception("Running report failed with response code: " + responseCode)
    }
}
finally {
    method.releaseConnection()
}