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

from dbqueries.integrations import db_zendesk
from utils import constants, errors, logging, var_names
import json
import requests


# Zendesk variables
var_channel = 'channel'
var_description = 'description'
var_link = 'link'
var_priority = 'priority'
var_requester_name = 'requester_name'
var_status = 'status'
var_ticket_id = 'ticket_id'
var_ticket_timestamp = 'ticket_timestamp'
var_title = 'title'

# Zendesk status
status_closed = 'Closed'
status_new = 'New'
status_open = 'Open'
status_pending = 'Pending'
status_solved = 'Solved'

# Zendesk priority mapped to TaskCall urgency
priority_map = {
    'urgent': constants.critical_urgency,
    'high': constants.high_urgency,
    'normal': constants.medium_urgency,
    'low': constants.low_urgency
}

# Zendesk default values
default_status_value = 'open'
default_priority_value = 'high'

# url paths
url_create_ticket = 'https://{0}.zendesk.com/api/v2/tickets'
url_update_ticket = 'https://{0}.zendesk.com/api/v2/tickets/{1}'


def zendesk_standard_header_params(access_token):
    '''
    Get the standard header params for a Zendesk request.
    :param access_token: the access token to be passed with the request
    :return: (dict) of header params
    '''
    return {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
        'Authorization': "Bearer " + access_token
    }


def create_zendesk_ticket(conn, timestamp, org_id, integ_key, integ_info, task_title, text_msg, instance_state, urgency,
                          voice_url=None, from_num=None, to_num=None):
    '''
    Creates a Zendesk 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 Jira 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
    :param voice_url: the URL of the voice message
    :param from_num: caller's phone number
    :param to_num: phone number that was called
    :return: (str) -> ticket ID (None if ticket cannot be created)
    '''
    zd_det = db_zendesk.get_zendesk_request_acceptance_details(conn, timestamp, org_id, integration_key=integ_key)
    subdomain = zd_det[var_names.external_id]
    access_token = zd_det[var_names.access_token]

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

    body = {
        'ticket': {
            'subject': task_title,
            'comment': {
                'body': text_msg if text_msg is not None else task_title
            },
            'type': integ_info[var_names.ticket_type],
            'status': zd_status,
            'priority': zd_priority
        }
    }
    if voice_url is not None:
        body['ticket']['voice_comment'] = {
            'from': from_num if from_num is not None else '+1',
            'to': to_num if to_num is not None else '+1',
            'recording_url': voice_url,
            'started_at': timestamp.strftime(constants.json_timestamp_milli_format_with_tz),
            'call_duration': 1
        }

    ticket_id, exe_status, exe_output = None, 400, errors.err_processing_failed
    try:
        response = requests.post(url_create_ticket.format(subdomain),
                                 headers=zendesk_standard_header_params(access_token),
                                 data=json.dumps(body))
        exe_status = response.status_code
        exe_output = response.json()
        if response.status_code in [200, 201]:
            ticket_id = str(exe_output['ticket']['id'])
        else:
            logging.exception('Failed to create Zendesk ticket')
            logging.exception(exe_output)
    except Exception as e:
        logging.exception('Failed to create Zendesk ticket')
        logging.exception(str(e))
    finally:
        return ticket_id, exe_status, exe_output


def update_zendesk_ticket(conn, timestamp, org_id, integ_key, integ_info, ticket_id, new_state=None, new_urgency=None,
                          new_note=None, voice_url=None, from_num=None, to_num=None):
    '''
    Update a Zendesk 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 Jira 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
    :param from_num: caller's phone number
    :param to_num: phone number that was called
    '''
    zd_det = db_zendesk.get_zendesk_request_acceptance_details(conn, timestamp, org_id, integration_key=integ_key)
    subdomain = zd_det[var_names.external_id]
    access_token = zd_det[var_names.access_token]

    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 to_sync_notes or voice_url is not None:
        body = {'ticket': {}}
        if to_sync_status:
            body['ticket']['status'] = integ_info[var_names.status][new_state]
        if to_sync_urgency:
            body['ticket']['priority'] = integ_info[var_names.urgency_level][str(new_urgency)]
        if to_sync_notes:
            body['ticket']['comment'] = {'body': new_note}
        if voice_url is not None:
            body['ticket']['voice_comment'] = {
                'from': from_num if from_num is not None else '+1',
                'to': to_num if to_num is not None else '+1',
                'recording_url': voice_url,
                'started_at': timestamp.strftime(constants.json_timestamp_milli_format_with_tz),
                'call_duration': 1
            }

        try:
            response = requests.put(url_update_ticket.format(subdomain, ticket_id),
                                    headers=zendesk_standard_header_params(access_token),
                                    data=json.dumps(body))
            if response.status_code not in [200, 201]:
                logging.error('Failed - Zendesk ticket ' + ticket_id + ' update - ' + str(body))
        except Exception as e:
            logging.exception('Failed to update Zendesk ticket')
            logging.exception(str(e))
