/*******************************************************************************
 * This file is part of OpenNMS(R).
 *
 * Copyright (C) 2008-2011 The OpenNMS Group, Inc.
 * OpenNMS(R) is Copyright (C) 1999-2011 The OpenNMS Group, Inc.
 *
 * OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc.
 *
 * OpenNMS(R) is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published
 * by the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 *
 * OpenNMS(R) is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with OpenNMS(R).  If not, see:
 *      http://www.gnu.org/licenses/
 *
 * For more information contact:
 *     OpenNMS(R) Licensing <license@opennms.org>
 *     http://www.opennms.org/
 *     http://www.opennms.com/
 *******************************************************************************/

package org.opennms.netmgt.ticketer.rt;

import java.util.List;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;
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.LogUtils;
import org.opennms.netmgt.rt.ReadOnlyRtConfigDao;
import org.opennms.netmgt.rt.RTTicket;
import org.opennms.netmgt.rt.RequestTracker;
import org.opennms.netmgt.rt.RequestTrackerException;

/**
 * OpenNMS Trouble Ticket Plugin API implementation for RT
 *
 * @author <a href="mailto:jonathan@opennms.org">Jonathan Sartin</a>
 * @author <a href="mailto:ranger@opennms.org">Benjamin Reed</a>
 */
public class RtTicketerPlugin implements Plugin {
    private static Pattern m_tagPattern = Pattern.compile("<[^>]*>");
    
    private RequestTracker m_requestTracker;

    private String m_queue;
    private String m_requestor;

    private String m_openStatus;
    private String m_closedStatus;
    private String m_cancelledStatus;
    private List<Integer> m_validOpenStatus;
    private List<String> m_validClosedStatus;
    private List<String> m_validCancelledStatus;

	/**
	 * <p>Constructor for RtTicketerPlugin.</p>
	 */
	public RtTicketerPlugin() {
	    ReadOnlyRtConfigDao dao = new ReadOnlyRtConfigDao();
	    m_openStatus = dao.getOpenStatus();
	    m_closedStatus = dao.getClosedStatus();
        m_cancelledStatus = dao.getCancelledStatus();
	    m_validOpenStatus = dao.getValidOpenStatus();
	    m_validClosedStatus = dao.getValidClosedStatus();
	    m_validCancelledStatus = dao.getValidCancelledStatus();
	    
	    m_queue = dao.getQueue();
	    m_requestor = dao.getRequestor();

	    m_requestTracker = new RequestTracker(dao.getBaseURL(), dao.getUsername(), dao.getPassword(), dao.getTimeout(), dao.getRetry());
	}
    
	/**
	 * {@inheritDoc}
	 *
	 * Gets ticket details from the RT trouble ticket system
	 */
	public Ticket get(final String ticketId) throws PluginException {

		Ticket ticket = null;
		RTTicket rtt = null;
        try {
            rtt = m_requestTracker.getTicket(Long.valueOf(ticketId), false);
        } catch (final RequestTrackerException e) {
            throw new PluginException(e);
        }
		
		if (rtt != null) {
		    ticket = new Ticket();
    		ticket.setState(rtToOpenNMSState(rtt.getStatus()));
    		ticket.setId(rtt.getId().toString());
    		ticket.setUser(StringUtils.join(rtt.getRequestors(), ", "));
    		ticket.setSummary(rtt.getSubject());
    		ticket.setDetails(rtt.getText());
		} else {
		    throw new PluginException("could not find ticket in RT for Ticket: " + ticketId);
		}
		
		return ticket;

	}

	/**
	 * {@inheritDoc}
	 *
	 * 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.
	 */
	public void saveOrUpdate(final Ticket newTicket) throws PluginException {
		
		String newTicketID;
		
		Ticket currentTicket = null;
		
		try {
		    
		    // If there's no external ID in the OpenNMS ticket, we need to create one
			
			if ((newTicket.getId() == null) ) {
			    
			    LogUtils.debugf(this, "TicketId is null creating a new ticket");
                RTTicket ticket = rtTicketFromTicket(newTicket);
                
                Long rtTicketNumber = null;
                try {
                    rtTicketNumber = m_requestTracker.createTicket(ticket);
                } catch (final Exception e) {
                    throw new PluginException(e);
                }
                
                if (rtTicketNumber == null) {
                    throw new PluginException("Received no ticket number from RT");
                }
				
			    newTicketID = rtTicketNumber.toString();
				newTicket.setId(newTicketID);

				LogUtils.debugf(this, "created new ticket: %s", newTicket.getId());
				
				
			} else {
			    
			    currentTicket = get(newTicket.getId()); 
				LogUtils.debugf(this, "updating existing ticket: %s", currentTicket.getId());
				
				if (currentTicket.getState() != newTicket.getState()) {
					updateRtStatus(newTicket);
				} else {
					// There is no else at the moment
					// Tickets are _only_ updated with new state
				}
				
			}
			
		} catch (final PluginException e) {
			LogUtils.errorf(this, e, "Failed to create or update RT ticket");
			throw e;
		}
			
	}

	/**
	* Convenience method for updating the Ticket Status in RT
	* 
	* @param ticket the ticket details
	*/
	
	private void updateRtStatus(final Ticket ticket) throws PluginException {

        try {
            m_requestTracker.updateTicket(Long.valueOf(ticket.getId()), "Status: " + openNMSToRTState(ticket.getState()));
        } catch (final Exception e) {
            LogUtils.warnf(this, e, "Error updating ticket %s to state %s", ticket.getId(), ticket.getState().toString());
        }
	}

	private RTTicket rtTicketFromTicket(final Ticket ticket) {
	    final RTTicket rtt = new RTTicket();

	    final String id = ticket.getId();
	    if (id != null && id.length() > 0) {
	        rtt.setId(Long.valueOf(id));
	    }
	    rtt.setQueue(m_queue);
	    rtt.setRequestor(m_requestor);
	    if (ticket.getSummary() != null) rtt.setSubject(ticket.getSummary());
        // Remove any HTML tags in the ticket details.
	    if (ticket.getDetails() != null) rtt.setText(m_tagPattern.matcher(ticket.getDetails()).replaceAll(""));
	    rtt.setStatus(openNMSToRTState(ticket.getState()));

	    return rtt;
	}

	/**
     * Convenience method for converting OpenNMS enumerated ticket states to
     * RT status.
     *
     * @param   state   a valid <code>org.opennms.netmgt.ticketd.Ticket.State</code>.
     * @return a String representing the RT Status of the ticket.
     */
	
	private String openNMSToRTState(final Ticket.State state) {

		String rtStatus;
		
		LogUtils.debugf(this, "getting RT status from OpenNMS State %s", state.toString());

        switch (state) {
        
            case OPEN:
            	// ticket is new
            	rtStatus = m_openStatus;
            	LogUtils.debugf(this, "OpenNMS Status OPEN matched rt status %s", rtStatus);
            	break;
            case CLOSED:
                // closed successful
                rtStatus = m_closedStatus;
                LogUtils.debugf(this, "OpenNMS Status CLOSED matched rt status %s", rtStatus);
                break;
            case CANCELLED:
            	// not sure how often we see this
            	rtStatus = m_cancelledStatus;
            	LogUtils.debugf(this, "OpenNMS Status CANCELLED matched rt status %s", rtStatus);
            	break;
            default:
                LogUtils.debugf(this, "No valid OpenNMS state on ticket");
                rtStatus = m_openStatus;
        }
        
        LogUtils.debugf(this, "OpenNMS state was %s, setting RT status to %s", state.toString(), rtStatus);
        
        return rtStatus;
    }

    /**
     * Convenience method for converting RT ticket Status to 
     * OpenNMS enumerated ticket states.
     * 
     * @param rtStatus a valid RT status string
     * @return the converted <code>org.opennms.netmgt.ticketd.Ticket.State</code>
     */
	
    private Ticket.State rtToOpenNMSState(final String rtStatus) {
    	
        if (m_validOpenStatus.contains(rtStatus)) {
        	LogUtils.debugf(this, "RT status %s matched OpenNMS state Open", rtStatus);
        	return Ticket.State.OPEN;
        } else if (m_validClosedStatus.contains(rtStatus)) {
            LogUtils.debugf(this, "RT status %s matched OpenNMS state Closed", rtStatus);
            return Ticket.State.CLOSED;
		} else if (m_validCancelledStatus.contains(rtStatus)) {
            LogUtils.debugf(this, "RT status %s matched OpenNMS state Cancelled", rtStatus);
            return Ticket.State.CANCELLED;
		}
        
        // we don't know what it is, so default to keeping it open.
        return Ticket.State.OPEN;
        
    }
    
    /**
     * <p>setUser</p>
     *
     * @param user a {@link java.lang.String} object.
     */
    public void setUser(final String user) {
        m_requestTracker.setUser(user);
    }


    /**
     * <p>setPassword</p>
     *
     * @param password a {@link java.lang.String} object.
     */
    public void setPassword(final String password) {
        m_requestTracker.setPassword(password);
    }
	
	

}
