/*
* Licensed Materials - Property of IBM Corp.
* IBM UrbanCode Build
* IBM UrbanCode Deploy
* IBM UrbanCode Release
* IBM AnthillPro
* (c) Copyright IBM Corporation 2002, 2017. 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.rtcworkitems

import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.StringEntity
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.apache.http.HttpEntityEnclosingRequest
import org.apache.http.client.entity.UrlEncodedFormEntity
import org.apache.http.message.BasicNameValuePair;
import org.xml.sax.SAXParseException;

public class RTCWorkItemHelper {

    //*****************************************************************************************************************
    // CLASS
    //*****************************************************************************************************************

    static final def n_rdf = new groovy.xml.Namespace("http://www.w3.org/1999/02/22-rdf-syntax-ns#");
    static final def n_dc = new groovy.xml.Namespace("http://purl.org/dc/terms/");
    static final def n_jfs = new groovy.xml.Namespace("http://jazz.net/xmlns/prod/jazz/jfs/1.0/");
    static final def n_jd = new groovy.xml.Namespace("http://jazz.net/xmlns/prod/jazz/discovery/1.0/");
    static final def n_jp06 = new groovy.xml.Namespace("http://jazz.net/xmlns/prod/jazz/process/0.6/");
//    static final def n_rdf_about = new groovy.xml.Namespace("https://10.15.6.26:9443/jazz/rootservices");
    static final def n_oslc_cm = new groovy.xml.Namespace("http://open-services.net/xmlns/cm/1.0/");
    static final def n_oslc_disc = new groovy.xml.Namespace("http://open-services.net/xmlns/discovery/1.0/");
    static final def n_rtc_cm = new groovy.xml.Namespace('http://jazz.net/xmlns/prod/jazz/rtc/cm/1.0/');

    //*****************************************************************************************************************
    // INSTANCE
    //*****************************************************************************************************************

    String username
    String password
    String url
    String projectName

    public RTCWorkItemHelper(Map<String,String> props) {
        username    = props['username'];
        password    = props['password'];
        url         = props['url'];
        projectName = props['projectName'];
    }

    def final authenticateRTCUser(String url, HttpClient client){
        HttpGet getterMethod = null;
        HttpPost authMethod = null;
        try {
            def tempURL = new URL(url);
            def index;
            if (tempURL.getPath().lastIndexOf("jazz") > 0) {
                index = tempURL.getPath().lastIndexOf("jazz");
            }
            else if (tempURL.getPath().lastIndexOf("ccm") > 0){
                index = tempURL.getPath().lastIndexOf("ccm");
            }
            else {
                throw new IOException ("[Error] Invalid URL given. Must contain 'jazz' or 'ccm' in the path.");
            }

            // Index of where the path element ends
            def slashIndex = tempURL.getPath().indexOf("/", index);
            if (slashIndex == -1) slashIndex = tempURL.getPath().size();
            // Retreive substring of entire path element ( Result == '/ccm', '/ccm*', '/*ccm', or '/*ccm*')
            def path = tempURL.getPath().substring(0, slashIndex);

            def host = tempURL.getProtocol() + '://' + tempURL.getHost() + ':' + tempURL.getPort() + path;
            println "[Ok] Host generated for authorization: '${host}'";
            getterMethod = new HttpGet(host + '/authenticated/identity');
            def getterResp = client.execute(getterMethod);

            authMethod = new HttpPost(host + '/authenticated/j_security_check');
            def data = [ new BasicNameValuePair("j_username", username),  new BasicNameValuePair("j_password", password)];
            authMethod.setEntity(new UrlEncodedFormEntity(data));
            //authMethod.setRequestHeader(getterMethod.getRequestHeader("Cookie"));
            authMethod.setHeader(getterMethod.getFirstHeader("Host"));
            authMethod.setHeader(getterMethod.getFirstHeader("User-Agent"));

            getterMethod.releaseConnection();
            def authResponse = client.execute(authMethod);
            println "[Ok] Auth Response: ${authResponse.getStatusLine()}";
            //getterMethod.setRequestHeader(authMethod.getRequestHeader("Cookie"));
            getterMethod.setHeader(authMethod.getFirstHeader("Host"));
            getterMethod.setHeader(authMethod.getFirstHeader("User-Agent"));
            authMethod.releaseConnection();
            getterResp = client.execute(getterMethod);
            EntityUtils.consume(getterResp.getEntity());
            getterMethod.releaseConnection();
        }
        finally {
            if(authMethod != null) {
                authMethod.releaseConnection();
            }
        }
    }

    def final String getCatalogUrl(HttpClient client) {
        HttpGet rootServicesMethod;
        try {
            String rootServicesUrl = url;
            if (!rootServicesUrl.endsWith('/')) {
                rootServicesUrl += '/'
            }
            rootServicesUrl += "rootservices"
            System.out.println("Root Services URL = " + rootServicesUrl);
            rootServicesMethod = new HttpGet(rootServicesUrl);
            rootServicesMethod.setHeader("Accept", "application/rdf+xml");
            def rootServicesResult = client.execute(rootServicesMethod);
            def rtSrvcResp = rootServicesResult.getEntity().getContent();
            def srvcxml;
            try {
                srvcxml = new XmlParser().parse(rtSrvcResp)
            }
            catch (Exception e) {
                System.out.println(rtSrvcResp);
                throw e;
            }
            def catalogUrl = srvcxml[n_oslc_cm.cmServiceProviders][0].attributes()[n_rdf.'resource'];
            System.out.println("Catalog URL = " + catalogUrl);
            return catalogUrl;
        }
        finally {
            if(rootServicesMethod != null) {
                rootServicesMethod.releaseConnection()
            }
        }
    }

    def final String getServicesUrl(String rootServices, HttpClient client) {
        HttpGet serviceDescriptor = null;
        try {
          serviceDescriptor = new HttpGet(rootServices);
          def serviceResponse = client.execute(serviceDescriptor);
          String serviceResult = serviceResponse.getEntity().getContent().getText("UTF-8");

          def serviceXml = null;
          try {
              serviceXml = new XmlParser(false, true).parseText(serviceResult);
          }
          catch (SAXParseException e) {
              System.out.println("")
              System.out.println("[Error] Unable to parse the XML response.")
              System.out.println("[Possible Solution] Confirm the username and password properties are correct.")
              System.out.println("")
              System.out.println(serviceResult);
              System.err.println ("problem encountered $e")
              e.printStackTrace();
          }
          catch (Exception e) {
              System.out.println(serviceResult);
              System.err.println ("problem encountered $e")
              e.printStackTrace();
              throw e;
          }

          String returnUrl = null;
          try {
              returnUrl = serviceXml[n_oslc_disc.entry].find { it -> it[n_oslc_disc.ServiceProvider][n_dc.title].text().equals(projectName) }[n_oslc_disc.ServiceProvider][n_oslc_disc.services][0].attributes()[n_rdf.'resource'];
          }
          catch (Exception e) {
              System.out.println("Error getting the Services Url: Check Project Name and Credientials are accurate!");
              System.out.println(serviceResult);
              throw e;
          }
          System.out.println("Services URL = " + returnUrl);
          System.out.println();
          return returnUrl;
        }
        finally {
            if(serviceDescriptor != null) {
                serviceDescriptor.releaseConnection();
            }
        }
    }

    def final String getWorkItemUrl(String servicesUrl, HttpClient client) {
        String workItemUrl = "";
        HttpGet getServices;
        try {
            getServices = new HttpGet(servicesUrl);
            def response = client.execute(getServices);
            def resp = response.getEntity().getContent();

            def respXml;
            try {
                respXml = new XmlParser().parse(resp);
            }
            catch (Exception e) {
                System.out.println(resp);
                throw e;
            }

            workItemUrl = respXml[n_oslc_cm.changeRequests][n_oslc_cm.simpleQuery][n_oslc_cm.url].text();
            System.out.println("Work Item URL = " + workItemUrl);
            return workItemUrl;
        }
        finally {
            if(getServices!=null) {
                getServices.releaseConnection();
            }
        }
    }

    def final String getWorkItemStatus(String statusUrl, HttpClient client) {
        String workItemStatus = "";
        HttpGet getServices;
        try {
            getServices = new HttpGet(statusUrl);
            getServices.setHeader("Accept", "application/xml")
            def response = client.execute(getServices);
            def resp = response.getEntity().getContent();
            def respXml;
            try {
                respXml = new XmlParser().parse(resp);
            }
            catch (Exception e) {
                System.out.println(resp);
                throw e;
            }
            workItemStatus = respXml[n_dc.title].text()
            return workItemStatus;
        }
        finally {
            if(getServices!=null) {
                getServices.releaseConnection();
            }
        }
    }

    def final String getCreationUrl(String servicesUrl, HttpClient client) {
        HttpGet getServices = null;
        try {
            getServices = new HttpGet(servicesUrl);
            def response = client.execute(getServices);
            def resp = response.getEntity().getContent();

            def respXml = null;
            try {
                respXml = new XmlParser().parse(resp);
            }
            catch (Exception e) {
                System.out.println(resp);
                throw e;
            }

            def creationUrl = respXml[n_oslc_cm.changeRequests][n_oslc_cm.factory].find { it -> it.attributes()[n_oslc_cm.'default'].equals("true") } [n_oslc_cm.url].text();
            System.out.println("Creation URL = " + creationUrl);
            return creationUrl;
        }
        finally {
            if(getServices!=null) {
                getServices.releaseConnection();
            }
        }
    }

    def final String getWorkItemCreationUrl(String workItemType, String servicesUrl, HttpClient client) {
        HttpGet getServices = null
        try {
            getServices = new HttpGet(servicesUrl)
            def response = client.execute(getServices)
            def resp = response.getEntity().getContent()

            def respXml = null
            try {
                respXml = new XmlParser().parse(resp)
            }
            catch (Exception e) {
                System.out.println(resp)
                throw e
            }

            def creationUrl = respXml[n_oslc_cm.changeRequests][n_oslc_cm.factory]?.find { it -> it[n_oslc_cm.url]?.text()?.trim().endsWith(workItemType) } [n_oslc_cm.url]?.text()
            System.out.println("Work Item Creation URL = " + creationUrl)
            return creationUrl
        }
        finally {
            if(getServices!=null) {
                getServices.releaseConnection()
            }
        }
    }

    def final String getCategoryUrl(String url, HttpClient client, String categoryName) {
        return ""

    }

    def final getWorkItemByIdentifier(String identifier, String queryUrl, HttpClient client, String etag) {
        String respStr = "";
        def resp = "";
        HttpRequestBase queryMethod = null;

        try {
            queryMethod = new HttpGet(queryUrl + '.xml?oslc_cm.query=' + URLEncoder.encode('dc:identifier="' + identifier + '"', "UTF-8"));
            queryMethod.setHeader("Accept", "application/x-oslc-cm-change-request+xml");
            def response = client.execute(queryMethod);
            if (etag) {
                etag = response.getFirstHeader("ETag").getValue();
            }
            respStr = response.getEntity().getContent().getText("UTF-8");
        }
        finally {
            if (queryMethod != null) {
                queryMethod.releaseConnection();
            }
        }
        return parseToXml(respStr);
    }

    def final String getWorkItemTypeId(String typeName, String contextId, HttpClient client) {
        HttpGet getTypes = null
        try {
            String rootServicesUrl = url
            if (!rootServicesUrl.endsWith('/')) {
                rootServicesUrl += '/'
            }
            getTypes = new HttpGet(rootServicesUrl + "oslc/types/" + contextId)
            getTypes.setHeader("Accept", "application/xml")

            def response = client.execute(getTypes)
            def resp = response.getEntity().getContent()

            def respXml = null
            try {
                respXml = new XmlParser().parse(resp)
            }
            catch (Exception e) {
                System.out.println(resp)
                throw e
            }

            def workItemTypeId = respXml[n_rtc_cm.Type]?.find { it -> it[n_dc.title]?.text()?.equals(typeName) } [n_dc.identifier]?.text()
            System.out.println("WorkItem Type ID = " + workItemTypeId)
            return workItemTypeId
        }
        finally {
            if(getTypes!=null) {
                getTypes.releaseConnection()
            }
        }
    }

    def final String getFiledAgainstId(String filedAgainst, String contextId, HttpClient client) {
        HttpGet getCategories = null
        String categoryID
        int iter = 0
        int totalCount = 99999999;
        try {
            String rootServicesUrl = url
            if (!rootServicesUrl.endsWith('/')) {
                rootServicesUrl += '/'
            }
            while (!categoryID && (totalCount > 50 * iter)) {
                // Retrieves contents in sets of 50 (max)
                getCategories = new HttpGet(rootServicesUrl + "oslc/categories.xml?oslc_cm.query=rtc_cm:projectArea=%22" + contextId + "%22&_startIndex=" + (50 * iter))
                getCategories.setHeader("Accept", "application/xml")
                def response = client.execute(getCategories)
                def resp = response.getEntity().getContent()

                def respXml = null
                try {
                    respXml = new XmlParser().parse(resp)
                }
                catch (Exception e) {
                    System.out.println(resp)
                    throw e
                }

                // If it's the first call, retrieve the total number of items
                if (iter == 0) {
                    try {
                        String totalCountStr = respXml?.attributes()[n_oslc_cm.'totalCount']
                        println "Filed Against Total Count: " + totalCountStr
                        totalCount = totalCountStr.isInteger() ? totalCountStr.toInteger() : null
                        if (!totalCount) {
                            println "[Error] Could not identify a valid total count."
                            throw new NullPointerException();
                        }
                    } catch (NullPointerException ex) {
                        throw new NullPointerException("[Error] Could not find total count of filed against categories.")
                    }
                }

                // Look for matching filed against name
                try {
                    def category = respXml[n_rtc_cm.Category]?.find { it -> it[n_rtc_cm.hierarchicalName]?.text()?.equals(filedAgainst) }
                    if (category) {
                        categoryID = category?.attributes()[n_rdf.'resource']
                    }
                } catch (NullPointerException ex) {
                    throw new NullPointerException("[Error] Filed Against property '${filedAgainst}' is not a valid value.")
                }
                iter++;
            }
        }
        finally {
            if(getCategories!=null) {
                getCategories.releaseConnection()
            }
            if (!categoryID) {
                throw new Exception("[Error] Could not identify the Filed Against property '${filedAgainst}'.")
            }
        }
        System.out.println("Filed Against `${filedAgainst}` ID = " + categoryID)

        return categoryID
    }

    def final parseToXml(String rtcRep) {
        def workItemXml = null;
        try {
            workItemXml = new XmlParser().parseText(rtcRep);
        }
        catch (Exception e) {
            System.out.println(rtcRep);
            throw e;
        }
        return workItemXml;
    }
}
