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

from dbqueries.integrations import db_team_dynamix
from utils import errors, logging, url_paths, var_names
import json
import requests


# TeamDynamix variables
var_active = 'Active'
var_app_id = 'AppID'
var_change_type = 'ChangeType'
var_is_active = 'IsActive'
var_name = 'Name'
var_type = 'Type'

# TeamDynamix url paths
url_accounts = 'https://{0}/TDWebApi/api/accounts'
url_applications = 'https://{0}/TDWebApi/api/applications'
url_auth = 'https://{0}/TDWebApi/api/auth'
url_auth_user = 'https://{0}/TDWebApi/api/auth/getuser'
url_create_ticket = 'https://{0}/TDWebApi/api/{1}/tickets'
url_priorities = 'https://{0}/TDWebApi/api/{1}/tickets/priorities'
url_statuses = 'https://{0}/TDWebApi/api/{1}/tickets/statuses'
url_ticket = 'https://{0}/TDWebApi/api/{1}/tickets/{2}'
url_ticket_types = 'https://{0}/TDWebApi/api/{1}/tickets/types?isActive=true'
url_update_ticket = 'https://{0}/TDWebApi/api/{1}/tickets/{2}/feed'


def get_jwt_token(acc_domain, acc_user, acc_pwd):
    '''
    Obtain a jwt token with which requests can be made to the TeamDynamix API endpoint.
    :param acc_domain: TeamDynamix account domain
    :param acc_user: admin user
    :param acc_pwd: admin password
    :return: (str) JWT token
    '''
    headers = {'Content-type': 'application/json'}
    body = {'username': acc_user, 'password': acc_pwd}
    response = requests.post(url_auth.format(acc_domain), headers=headers, data=json.dumps(body))
    if response.status_code == 200:
        return response.text
    else:
        raise PermissionError


def get_standard_headers(token):
    '''
    Get the standard header params.
    :param token: token obtained from TeamDynamix using admin credentials
    :return: (dict) of header params
    '''
    return {'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json'}


def get_admin_uid(acc_domain, acc_user, acc_pwd):
    '''
    Get the UID of the admin user whose credentials have been provided.
    :param acc_domain: account domain
    :param acc_user: admin user
    :param acc_pwd: admin password
    :return: (str) UID
    '''
    token = get_jwt_token(acc_domain, acc_user, acc_pwd)
    headers = get_standard_headers(token)
    response = requests.get(url_auth_user.format(acc_domain), headers=headers)
    if response.status_code == 200:
        return response.json()['UID']
    else:
        return None


def get_accounts(token, acc_domain):
    '''
    Get list of department accounts that are associated with a TeamDynamix account.
    :param token: token obtained with admin credentials
    :param acc_domain: account domain
    :return: (list of dict) -> [{item_id: , name: }, ...]
    '''
    resp = requests.get(url_accounts.format(acc_domain), headers=get_standard_headers(token))
    if resp.status_code == 200:
        resp_json = resp.json()
        return [{var_names.item_id: x['ID'], var_names.name: x['Name']} for x in resp_json if x[var_is_active]]
    else:
        return None


def get_applications(token, acc_domain):
    '''
    Get list of applications that are associated with a TeamDynamix account.
    :param token: token obtained with admin credentials
    :param acc_domain: account domain
    :return: (list of dict) -> [{item_id: , name: }, ...]
    '''
    resp = requests.get(url_applications.format(acc_domain), headers=get_standard_headers(token))
    if resp.status_code == 200:
        resp_json = resp.json()
        return [{var_names.item_id: x['AppID'], var_names.name: x['Name']} for x in resp_json
                if x[var_active] and x[var_type] == 'Ticketing']
    else:
        return None


def get_ticket_types(token, acc_domain, application_id):
    '''
    Get list of ticket types associated with an application.
    :param token: token obtained with admin credentials
    :param acc_domain: account domain
    :param application_id: ID of the application
    :return: (list of dict) -> [{item_id: , name: }, ...]
    '''
    resp = requests.get(url_ticket_types.format(acc_domain, application_id), headers=get_standard_headers(token))
    if resp.status_code == 200:
        resp_json = resp.json()
        return [{var_names.item_id: x['ID'], var_names.name: x['Name']} for x in resp_json if x[var_is_active]]
    else:
        return None


def get_statuses(token, acc_domain, application_id):
    '''
    Get list of statuses associated with an application.
    :param token: token obtained with admin credentials
    :param acc_domain: account domain
    :param application_id: ID of the application
    :return: (list of dict) -> [{item_id: , name: }, ...]
    '''
    resp = requests.get(url_statuses.format(acc_domain, application_id), headers=get_standard_headers(token))
    if resp.status_code == 200:
        resp_json = resp.json()
        return [{var_names.item_id: x['ID'], var_names.name: x['Name']} for x in resp_json if x[var_is_active]]
    else:
        return None


def get_priorities(token, acc_domain, application_id):
    '''
    Get list of priorities associated with an application.
    :param token: token obtained with admin credentials
    :param acc_domain: account domain
    :param application_id: ID of the application
    :return: (list of dict) -> [{item_id: , name: }, ...]
    '''
    resp = requests.get(url_priorities.format(acc_domain, application_id), headers=get_standard_headers(token))
    if resp.status_code == 200:
        resp_json = resp.json()
        return [{var_names.item_id: x['ID'], var_names.name: x['Name']} for x in resp_json if x[var_is_active]]
    else:
        return None


def create_team_dynamix_ticket(conn, timestamp, org_id, integ_key, integ_info, org_instance_id, task_title,
                               text_msg, instance_state, urgency):
    '''
    Creates a TeamDynamix 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
    :param integ_info: additional info of this integration
    :param org_instance_id: organization instance ID
    :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)
    '''
    tm_dy_det = db_team_dynamix.get_team_dynamix_account_details(conn, timestamp, org_id, integration_key=integ_key)
    if len(tm_dy_det) == 0:
        raise LookupError(errors.err_integration_not_found)

    acc_domain = tm_dy_det[var_names.vendor_endpoint_name]
    acc_user = tm_dy_det[var_names.username]
    acc_pwd = tm_dy_det[var_names.password]
    acc_uid = tm_dy_det[var_names.user_object_id]

    token = get_jwt_token(acc_domain, acc_user, acc_pwd)

    account_id = integ_info[var_names.account_id]
    application_id = integ_info[var_names.application_id]
    tkt_type = integ_info[var_names.ticket_type]
    tkt_status_id = int(integ_info[var_names.status][instance_state])
    tkt_priority_id = int(integ_info[var_names.urgency_level][str(urgency)])

    inc_url = url_paths.web_incidents_details + '/' + str(org_instance_id)
    desc_link = '<a target="_blank" href="' + inc_url + '">TaskCall URL</a>'
    if text_msg is None:
        desc = '<div>' + desc_link + '</div>'
    else:
        desc = '<div>' + desc_link + ' <p>' + text_msg + '</p>' + '</div>'

    body = {
        'Title': task_title,
        'Description': desc,
        'IsRichHtml': True,
        'AccountID': account_id,
        'TypeID': tkt_type,
        'StatusID': tkt_status_id,
        'PriorityID': tkt_priority_id,
        'RequestorUid': acc_uid
    }
    ticket_id, exe_status, exe_output = None, 400, errors.err_processing_failed
    try:
        response = requests.post(url_create_ticket.format(acc_domain, application_id),
                                 headers=get_standard_headers(token), data=json.dumps(body))
        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 TeamDynamix ticket')
            logging.exception(exe_output)
    except Exception as e:
        logging.exception('Failed to create TeamDynamix ticket')
        logging.exception(str(e))
    finally:
        return ticket_id, exe_status, exe_output


def update_team_dynamix_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 TeamDynamix 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
    :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
    '''
    tm_dy_det = db_team_dynamix.get_team_dynamix_account_details(conn, timestamp, org_id, integration_key=integ_key)
    if len(tm_dy_det) == 0:
        raise LookupError(errors.err_integration_not_found)

    acc_domain = tm_dy_det[var_names.vendor_endpoint_name]
    acc_user = tm_dy_det[var_names.username]
    acc_pwd = tm_dy_det[var_names.password]
    application_id = integ_info[var_names.application_id]

    token = get_jwt_token(acc_domain, acc_user, acc_pwd)
    headers = get_standard_headers(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 voice_url is not None:
        tkt_resp = requests.get(url_ticket.format(acc_domain, application_id, ticket_id), headers=headers)
        if tkt_resp.status_code != 200:
            raise PermissionError

        body = tkt_resp.json()
        if to_sync_status:
            body['StatusID'] = int(integ_info[var_names.status][new_state])
        if to_sync_urgency:
            body['PriorityID'] = int(integ_info[var_names.urgency_level][str(new_urgency)])
        if voice_url is not None:
            curr_desc = body['Description']
            body['Description'] = curr_desc + '<br/><br/><audio controls><source src="' + voice_url + '"></audio>'
            body['IsRichHtml'] = True
        try:
            response = requests.post(url_ticket.format(acc_domain, application_id, ticket_id), headers=headers,
                                     data=json.dumps(body))
            if response.status_code not in [200, 201]:
                logging.error('Failed - TeamDynamix ticket ' + ticket_id + ' update - ' + str(body))
                logging.exception(response.json())
        except Exception as e:
            logging.exception('Failed to update TeamDynamix ticket')
            logging.exception(str(e))

    if to_sync_notes:
        body = {'Comments': new_note, 'IsPrivate': True}
        try:
            response = requests.post(url_update_ticket.format(acc_domain, application_id, ticket_id), headers=headers,
                                     data=json.dumps(body))
            if response.status_code not in [200, 201]:
                logging.error('Failed - TeamDynamix ticket ' + ticket_id + ' add note - ' + str(body))
                logging.exception(response.json())
        except Exception as e:
            logging.exception('Failed to add note to TeamDynamix ticket')
            logging.exception(str(e))
