# By: Riasat Ullah
# This file contains all constants and functions related to the Freshdesk integration.

from dbqueries.integrations import db_freshdesk
from requests.auth import HTTPBasicAuth
from utils import constants, errors, logging, var_names
import json
import requests


# Freshdesk variables
var_freshdesk_webhook = 'freshdesk_webhook'
var_ticket_description = 'ticket_description'
var_ticket_id = 'ticket_id'
var_ticket_latest_private_comment = 'ticket_latest_private_comment'
var_ticket_latest_public_comment = 'ticket_latest_public_comment'
var_ticket_priority = 'ticket_priority'
var_ticket_status = 'ticket_status'
var_ticket_subject = 'ticket_subject'
var_ticket_tags = 'ticket_tags'
var_ticket_url = 'ticket_url'
var_triggered_event = 'triggered_event'

# Freshdesk status
status_closed = 'Closed'
status_open = 'Open'
status_pending = 'Pending'
status_resolved = 'Resolved'

# Freshdesk priority mapped to TaskCall urgency
priority_map = {
    'Urgent': constants.critical_urgency,
    'High': constants.high_urgency,
    'Medium': constants.medium_urgency,
    'Low': constants.low_urgency
}

# Freshdesk status label mapped to Freshdesk status value
fd_label_to_fd_status_map = {
    'Open': '2',
    'Pending': '3',
    'Resolved': '4',
    'Closed': '5'
}

# Freshdesk default values
default_status_value = 2    # open
default_priority_value = 3  # high

# Freshdesk url paths
url_add_ticket_note = 'https://{0}/api/v2/tickets/{1}/notes'
url_create_ticket = 'https://{0}/api/v2/tickets'
url_update_ticket = 'https://{0}/api/v2/tickets/{1}'

# TaskCall internalized event types as per Freshdesk "triggered_event" variable
fd_new_ticket_event = 'fd_new_ticket'
fd_priority_update_event = 'fd_priority_update'
fd_private_add_note_event = 'fd_private_add_note'
fd_public_add_note_event = 'fd_public_add_note'
fd_status_update_event = 'fd_status_update'

# default Freshdesk ticket subject
default_ticket_subject = 'Freshdesk Ticket'


def get_event_type(event_str):
    '''
    Gets the type of event that the webhook has been sent for. Currently, parses the following:
        - '{ticket_action:created}'
        - '{priority:{from:2,to:3}}'
        - '{note_type:private}'
        - '{status:{from:2,to:4}}'

    :param event_str: value of the 'triggered_event' variable
    :return: internalized type of event
    '''
    parsed_string = event_str.lstrip('{').rstrip('}').split(':')
    event_type = parsed_string[0]

    if event_type == 'ticket_action' and parsed_string[1] == 'created':
        return fd_new_ticket_event
    if event_type == 'note_type' and parsed_string[1] == 'private':
        return fd_private_add_note_event
    elif event_type == 'note_type' and parsed_string[1] == 'public':
        return fd_public_add_note_event
    elif event_type == 'priority':
        return fd_priority_update_event
    elif event_type == 'status':
        return fd_status_update_event
    else:
        return None


def create_freshdesk_ticket(conn, timestamp, org_id, integ_key, integ_info, task_title, text_msg,
                            instance_state, urgency):
    '''
    Creates a Freshdesk ticket.
    :param conn: db connection
    :param timestamp: timestamp when this request is being made
    :param org_id: organization ID
    :param integ_key: the integration key of this Freshdesk integration
    :param integ_info: additional info of this integration
    :param task_title: instance task title that will be used as the summary of the issue
    :param text_msg: instance text details that will be used as the description of the issue
    :param instance_state: the current state of the instance
    :param urgency: the urgency level of the instance
    :return: (str) -> ticket ID (None if ticket cannot be created)
    '''
    fd_det = db_freshdesk.get_freshdesk_accounts(conn, timestamp, org_id, integration_key=integ_key)
    if len(fd_det) == 0:
        raise LookupError(errors.err_freshdesk_integration_not_found)

    fd_status = int(integ_info[var_names.status][instance_state])\
        if integ_info[var_names.status] is not None else default_status_value
    fd_priority = int(integ_info[var_names.urgency_level][str(urgency)]) \
        if integ_info[var_names.urgency_level] is not None else default_priority_value

    domain = fd_det[0][var_names.vendor_endpoint_name]
    api_key = fd_det[0][var_names.secret_token]
    headers = {'Content-Type': 'application/json'}
    body = {
        'subject': task_title,
        'description': '<div>' + text_msg if text_msg is not None else task_title + '</div>',
        'type': integ_info[var_names.ticket_type],
        'status': fd_status,
        'priority': fd_priority,
        'unique_external_id': 'TaskCall'
    }
    ticket_id, exe_status, exe_output = None, 400, errors.err_processing_failed
    try:
        response = requests.post(url_create_ticket.format(domain), headers=headers, data=json.dumps(body),
                                 auth=HTTPBasicAuth(api_key, 'x'))
        exe_status = response.status_code
        exe_output = response.json()
        if exe_status in [200, 201]:
            ticket_id = str(exe_output['id'])
        else:
            logging.exception('Failed to create Freshdesk ticket')
            logging.exception(exe_output)
    except Exception as e:
        logging.exception('Failed to create Freshdesk ticket')
        logging.exception(str(e))
    finally:
        return ticket_id, exe_status, exe_output


def update_freshdesk_ticket(conn, timestamp, org_id, integ_key, integ_info, ticket_id, new_state=None, new_urgency=None,
                            new_note=None, voice_url=None):
    '''
    Update a Freshdesk ticket.
    :param conn: db connection
    :param timestamp: timestamp when this request is being made
    :param org_id: organization ID
    :param integ_key: the integration key of this Freshdesk integration
    :param integ_info: additional info of this integration
    :param ticket_id: ID of the ticket
    :param new_state: new state of the instance
    :param new_urgency: new urgency level of the instance
    :param new_note: new note added to the instance
    :param voice_url: the URL of the voice message
    '''
    fd_det = db_freshdesk.get_freshdesk_accounts(conn, timestamp, org_id, integration_key=integ_key)
    if len(fd_det) == 0:
        raise LookupError(errors.err_freshdesk_integration_not_found)

    domain = fd_det[0][var_names.vendor_endpoint_name]
    api_key = fd_det[0][var_names.secret_token]
    headers = {'Content-Type': 'application/json'}

    to_sync_status = new_state is not None and integ_info[var_names.status] is not None
    to_sync_urgency = new_urgency is not None and integ_info[var_names.urgency_level] is not None
    to_sync_notes = new_note is not None and integ_info[var_names.to_sync_notes]

    if to_sync_status or to_sync_urgency or voice_url is not None:
        body = {}
        if to_sync_status:
            body['status'] = int(integ_info[var_names.status][new_state])
        if to_sync_urgency:
            body['priority'] = int(integ_info[var_names.urgency_level][str(new_urgency)])
        if voice_url is not None:
            curr_desc = get_freshdesk_description(domain, api_key, ticket_id)
            body['description'] = curr_desc + '<br/><br/><audio controls><source src="' + voice_url + '"></audio>'
        try:
            response = requests.put(url_update_ticket.format(domain, ticket_id), headers=headers, data=json.dumps(body),
                                    auth=HTTPBasicAuth(api_key, 'x'))
            if response.status_code not in [200, 201]:
                logging.error('Failed - Freshdesk ticket ' + ticket_id + ' update - ' + str(body))
                logging.exception(response.json())
        except Exception as e:
            logging.exception('Failed to update Freshdesk ticket')
            logging.exception(str(e))

    if to_sync_notes:
        body = {'body': new_note}
        try:
            response = requests.post(url_add_ticket_note.format(domain, ticket_id), headers=headers,
                                     data=json.dumps(body), auth=HTTPBasicAuth(api_key, 'x'))
            if response.status_code not in [200, 201]:
                logging.error('Failed - Freshdesk ticket ' + ticket_id + ' add note - ' + str(body))
                logging.exception(response.json())
        except Exception as e:
            logging.exception('Failed to add note to Freshdesk ticket')
            logging.exception(str(e))


def get_freshdesk_description(domain, api_key, ticket_id):
    '''
    Get the current description of a Freshdesk ticket.
    :param domain: Freshdesk domain
    :param api_key: Freshdesk api key
    :param ticket_id: Freshdesk ticket ID
    :return: (str) HTML content
    '''
    try:
        response = requests.get(url_update_ticket.format(domain, ticket_id), auth=HTTPBasicAuth(api_key, 'x'))
        if response.status_code in [200, 201]:
            return response.json()['description']
        else:
            logging.error('Failed - Freshdesk ticket ' + ticket_id + ' get description')
            logging.exception(response.json())
            return None
    except Exception as e:
        logging.exception('Failed to fetch Freshdesk ticket')
        logging.exception(str(e))
