/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.opennms.netmgt.ticketer.servicedesk;

/**
 * OpenNMS Trouble Ticket Plugin API implementation for AdventNet ServiceDesk
 * blatantly copied from Jonathan Sartin's RT Plugin.
 * 
 * @author <a href="mailto:aj@thepaxson5.org">Aaron Paxson</a>
 */
import java.io.IOException;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.cookie.CookiePolicy;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.params.HttpClientParams;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.log4j.Category;
import org.opennms.api.integration.ticketing.Plugin;
//import org.opennms.api.integration.ticketing.PluginException;
import org.opennms.api.integration.ticketing.Ticket;
import org.opennms.core.utils.ThreadCategory;

public class ServiceDeskTicketerPlugin implements Plugin {

    private DefaultServiceDeskConfigDao m_configDao;
    private String m_user;
    private String m_password;
    private String m_domainName;
    private String m_logonDomainType;
    private String m_requestor;

    public ServiceDeskTicketerPlugin() {
        m_configDao = new DefaultServiceDeskConfigDao();
        m_user = m_configDao.getUserName();
        m_password = m_configDao.getPassword();
        m_domainName = m_configDao.getDomainName();
        m_logonDomainType = m_configDao.getLogonDomainType();
        m_requestor = m_configDao.getRequestor();
    }

    /**
     * Gets ticket details from the ServiceDesk trouble ticket system
     * 
     * @param   ticketId    a string representation of the SD ticketID
     * @return  ticket      the ticket details
     */
    public Ticket get(String ticketId) {

        String operation = "GetRequestDetails";

        boolean ticketFound = false;

        Ticket ticket = null;

        HashMap<String, String> ticketAttributes = new HashMap<String, String>();
        // don't try to get ticket if it's marked as not available

        if (ticketId == null) {

            log().error("No ServiceDesk ticketID available in OpenNMS Ticket");
            //throw new PluginException("No ServiceDesk ticketID available in OpenNMS Ticket");

        } else {

            PostMethod post = new PostMethod(m_configDao.getBaseUrl());

            int httpStatus;

            NameValuePair[] ticketGetParams = {
                new NameValuePair("username", m_user),
                new NameValuePair("password", m_password),
                new NameValuePair("workOrderId", ticketId),
                new NameValuePair("operation", operation),
                new NameValuePair("DOMAIN_NAME", m_domainName),
                new NameValuePair("logonDomainName", m_logonDomainType)
            };

            post.setRequestBody(ticketGetParams);

            try {

                httpStatus = getClient().executeMethod(post);

                if (httpStatus != HttpStatus.SC_OK) {

                    log().error("Bad Request accessing ServiceDesk: " + httpStatus);

                } else {

                    String in = post.getResponseBodyAsString();
                    log().debug(in);
                    Pattern okPattern = Pattern.compile("(?s)propname\\skey='(\\w+)'>(.*?)</propname>", Pattern.MULTILINE);
                    Matcher matcher = okPattern.matcher(in);

                    while (matcher.find()) {

                        ticketFound = true;
                        ticketAttributes.put(matcher.group(1), matcher.group(2));

                    }


                }
            } catch (HttpException e) {
                log().error("HTTP exception attempting to logon to ServiceDesk: " + e.getMessage());
            } catch (IOException e) {
                log().error("HTTP exception attempting to logon to ServiceDesk: " + e.getMessage());
            } finally {
                post.releaseConnection();
            }
        }

        if (ticketFound) {
            ticket = new Ticket();
            ticket.setState(serviceDeskToOnmsState(ticketAttributes.get("atatus")));
            ticket.setId(ticketId);
            ticket.setUser(ticketAttributes.get("technician"));
            ticket.setSummary(ticketAttributes.get("title"));
        } else {
            //throw new Exception("could not find ticket in ServiceDesk for Ticket: " + ticketId);
            log().error("Could not find ticket in ServiceDesk for Ticket: " + ticketId);
        }

        return ticket;
    }

    /**
     * Creates a new ticket (if none exists) or updates an existing ticket in the
     * RT trouble ticket system. Ticket updates are currently limited to updating
     * the ticket status only.
     * 
     * @param   ticket      the ticket details
     */
    public void saveOrUpdate(Ticket ticket)  {

        String newTicketID;

        Ticket currentTicket = null;

        try {

            // If there's no external ID in the OpenNMS ticket, we need to create one

            if ((ticket.getId() == null)) {

                log().debug("TicketId for OpenNMS is null.  Creating a new ticket");

                newTicketID = createServiceDeskTicket(ticket);

                ticket.setId(newTicketID);

                log().debug("created new ticket: " + ticket.getId());


            } else {

                currentTicket = get(ticket.getId());

                log().debug("updating existing ticket : " + currentTicket.getId());

                if (currentTicket.getState() != ticket.getState()) {
                   
                    log().debug("Attempting to update ServiceDesk Ticket " + ticket.getId() +
                            " from status " + currentTicket.getState().name() + " to status " +
                            ticket.getState().name());

                    updateServiceDeskStatus(ticket);

                } else {
                    // There is no else at the moment
                    // Tickets are _only_ updated with new state
                }

            }

        } catch (Exception e) {
            log().error("Failed to create or update ServiceDesk ticket" + e);
            //throw new PluginException("Failed to create or update ServiceDesk ticket");
        }
    }

    /**
     * Covenience logging.
     * 
     * @return a log4j Category for this class
     */
    Category log() {
        return ThreadCategory.getInstance(getClass());
    }

    private HttpClient getClient() {

        HttpClient client = new HttpClient();

        HttpClientParams clientParams = new HttpClientParams();
        clientParams.setConnectionManagerTimeout(m_configDao.getTimeOut());
        clientParams.setSoTimeout(m_configDao.getTimeOut());
        clientParams.setParameter(HttpMethodParams.RETRY_HANDLER,
                new DefaultHttpMethodRetryHandler(m_configDao.getRetry(), false));
        clientParams.setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY);

        client.setParams(clientParams);

        return client;

    }

    private Ticket.State serviceDeskToOnmsState(String sdState) {
        Ticket.State onmsState;

        if (m_configDao.getValidOpenStatus().contains(sdState)) {
            log().debug("ServiceDesk Status " + sdState + " matched valid OpenNMS Open Status");
            onmsState = Ticket.State.OPEN;
        } else if (m_configDao.getValidClosedStatus().contains(sdState)) {
            log().debug("ServiceDesk Status " + sdState + " matched valid OpenNMS Closed Status");
            onmsState = Ticket.State.CLOSED;
        } else if (m_configDao.getValidCancelledStatus().contains(sdState)) {
            log().debug("ServiceDesk Status " + sdState + " matched valid OpenNMS Cancelled Status");
            onmsState = Ticket.State.CANCELLED;
        } else {
            log().debug("ServiceDesk Status " + sdState + " has no matching status for OpenNMS.  Defaulting to Open");
            onmsState = Ticket.State.OPEN;
        }

        return onmsState;
    }

    private String openNMSToServiceDeskState(Ticket.State state) {

        String sdStatus;

        log().debug("getting ServiceDesk status from OpenNMS State " + state.toString());

        switch (state) {

            case OPEN:
                // ticket is new
                sdStatus = m_configDao.getOpenStatus();
                log().debug("OpenNMS Status OPEN matched ServiceDesk status " + sdStatus);
                break;
            case CANCELLED:
                // not sure how often we see this
                sdStatus = m_configDao.getCancelledStatus();
                log().debug("OpenNMS Status CANCELLED matched ServiceDesk status " + sdStatus);
                break;
            case CLOSED:
                // closed successful
                sdStatus = m_configDao.getClosedStatus();
                log().debug("OpenNMS Status CLOSED matched ServiceDesk status " + sdStatus);
                break;
            default:
                log().debug("No valid OpenNMS state on ticket.  Defaulting to 'Open'");
                sdStatus = m_configDao.getOpenStatus();
        }

        log().debug("OpenNMS state was             " + state.toString());
        log().debug("setting ServiceDesk status to " + sdStatus);

        return sdStatus;
    }

    private String createServiceDeskTicket(Ticket ticket) {

        PostMethod post = new PostMethod(m_configDao.getBaseUrl());

        String operation = "AddRequest";

        int httpStatus;
        String workOrderId =null;

        NameValuePair[] ticketGetParams = {
            new NameValuePair("username", m_user),
            new NameValuePair("password", m_password),
            new NameValuePair("operation", operation),
            new NameValuePair("DOMAIN_NAME", m_domainName),
            new NameValuePair("logonDomainName", m_logonDomainType),
            new NameValuePair("requestor", m_requestor),
            new NameValuePair("subject", ticket.getSummary()),
            new NameValuePair("description", ticket.getDetails())
        };

        post.setRequestBody(ticketGetParams);

        try {

            httpStatus = getClient().executeMethod(post);

            if (httpStatus != HttpStatus.SC_OK) {

                log().error("Bad Request accessing ServiceDesk: " + httpStatus);

            } else {

                String in = post.getResponseBodyAsString();
                log().debug(in);
                Pattern okPattern = Pattern.compile("(?s)<workorderid>(\\d+)</workorderid>");
                Matcher matcher = okPattern.matcher(in);

                if (matcher.find()) {
                    workOrderId = matcher.group(1);
                    log().debug("Ticket " + workOrderId + " created successfully in ServiceDesk");
                } else {
                    log().error("Did not receive confirmation that the ServiceDesk ticket was created");
                }

            }
        
        } catch (HttpException e) {
            log().error("HTTP exception attempting to logon to ServiceDesk: " + e.getMessage());
        } catch (IOException e) {
            log().error("IO exception attempting to logon to ServiceDesk: " + e.getMessage());
        } finally {
            post.releaseConnection();
        }
            
                return workOrderId;
    }
    
    /**
	* Convenience method for updating the Ticket Status in RT
	* 
	* @param   ticket      the ticket details
	*/
    private void updateServiceDeskStatus(Ticket ticket) {
        PostMethod post = new PostMethod(m_configDao.getBaseUrl());

        String operation = "UpdateRequest";
        int httpStatus;
        String workOrderId = ticket.getId();

        NameValuePair[] ticketGetParams = {
            new NameValuePair("username", m_user),
            new NameValuePair("password", m_password),
            new NameValuePair("operation", operation),
            new NameValuePair("DOMAIN_NAME", m_domainName),
            new NameValuePair("logonDomainName", m_logonDomainType),
            new NameValuePair("workOrderID", workOrderId),
            new NameValuePair("status", openNMSToServiceDeskState(ticket.getState()))
        };

        post.setRequestBody(ticketGetParams);
        
        try {

            httpStatus = getClient().executeMethod(post);

            if (httpStatus != HttpStatus.SC_OK) {

                log().error("Bad Request accessing ServiceDesk: " + httpStatus);

            } else {

                String in = post.getResponseBodyAsString();
                log().debug(in);
                if (in.contains("successfully")) {
                    log().debug("Ticket " + workOrderId + " updated successfully in ServiceDesk.");
                } else {
                    log().error("Did not receive confirmation that the ServiceDesk ticket was successfully updated");
                }

            }
        } catch (HttpException e) {
            log().error("HTTP exception attempting to logon to ServiceDesk: " + e.getMessage());
        } catch (IOException e) {
            log().error("IO exception attempting to logon to ServiceDesk: " + e.getMessage());
        } finally {
            post.releaseConnection();
        }
        
    }
}