# By: Riasat Ullah
# This file contains Monday.com integration specific variables.

from dbqueries.integrations import db_monday
from utils import logging, var_names
import json
import requests


# monday.com API url path
monday_api_url = 'https://api.monday.com/v2'

# monday.com webhook types
monday_event_create_item = 'create_item'
monday_event_create_pulse = 'create_pulse'
monday_event_change_specific_column_value = 'change_specific_column_value'
monday_event_update_column_value = 'update_column_value'
monday_event_when_date_arrived = 'when_date_arrived'

# monday.com string variables
var_challenge = 'challenge'
var_column_id = 'columnId'
var_columnTitle = 'columnTitle'
var_columnType = 'columnType'
var_event = 'event'
var_groupName = 'groupName'
var_pulse_id = 'pulseId'
var_pulse_name = 'pulseName'
var_subscription_id = 'subscriptionId'


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


def get_monday_boards(access_token):
    '''
    Get the available Monday.com boards.
    :param access_token: (str) account access token
    :return: (list) of boards
    '''
    response = requests.post(
        url=monday_api_url, headers=monday_header_params(access_token),
        json={"query": "{ boards { id name groups { id title } columns { id type title settings } }}"},
    )
    boards = []
    if response.status_code == 200:
        output = response.json()
        boards = output['data']['boards']
    return boards


def create_monday_item(conn, timestamp, org_id, integ_key, integ_info, task_title, text_msg, instance_state, urgency):
    '''
    Creates a new monday item.
    :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
    :return: (str) -> item ID (Monday returns an int, but we will convert it to a str as per our database requirements)
    '''
    mon_integ_det = db_monday.get_monday_request_acceptance_details(conn, timestamp, org_id, integration_key=integ_key)
    access_token = mon_integ_det[var_names.access_token]
    board_id = integ_info[var_names.monday_board_id]
    group_id = integ_info[var_names.monday_group_id]
    field_map = integ_info[var_names.field_mapping]

    column_values = dict()
    if var_names.description in field_map:
        column_values[field_map[var_names.description]] = text_msg
    if var_names.urgency_level in field_map:
        column_values[field_map[var_names.urgency_level]] = integ_info[var_names.urgency_level][str(urgency)]

    mutation_query = '''
        mutation ($boardId: ID!, $groupId: String!, $itemName: String!, $columnVals: JSON) {
            create_item (
                board_id: $boardId,
                group_id: $groupId,
                item_name: $itemName,
                column_values: $columnVals
            )
            { id }
        }
    '''
    variables = {'boardId': int(board_id), 'groupId': group_id, 'itemName': task_title,
                 'columnVals': json.dumps(column_values) if len(column_values) > 0 else None}

    response = requests.post(url=monday_api_url, headers=monday_header_params(access_token),
                             json={"query": mutation_query, "variables": variables})
    status, output = response.status_code, None
    new_item_id = None
    if status == 200:
        output = response.json()
        if 'data' in output and 'create_item' in output['data'] and output['data']['create_item'] is not None:
            new_item_id = output['data']['create_item']['id']

            # We do this because the instance_custom_actions table expects the vendor ID to be a string.
            new_item_id = str(new_item_id)

            update_monday_status(conn, timestamp, org_id, instance_state, new_item_id, integ_info,
                                 access_token=access_token, board_id=board_id, field_map=field_map)
    return new_item_id, status, output


def add_monday_update(conn, timestamp, org_id, integ_key, integ_info, item_id, comment):
    '''
    Add a note to a monday.com item.
    :param conn: db connection
    :param timestamp: timestamp when this request was made
    :param org_id: the organization ID
    :param integ_key: (optional) the key for the integration; for retrieving the access token if it is not provided
    :param integ_info: (optional) the additional_info of the integration
    :param item_id: monday.com item ID
    :param comment: the note to add to the item
    '''
    if integ_info[var_names.to_sync_notes]:
        mon_integ_det = db_monday.get_monday_request_acceptance_details(conn, timestamp, org_id,
                                                                        integration_key=integ_key)
        access_token = mon_integ_det[var_names.access_token]

        query = '''
            mutation ($itemId: ID!, $body: String!) {
                create_update (
                    item_id: $itemId,
                    body: $body
                )
                { id }
            }
        '''
        variables = {'itemId': int(item_id), 'body': comment}
        response = requests.post(url=monday_api_url, headers=monday_header_params(access_token),
                                 json={"query": query, "variables": variables})

        if response.status_code == 200:
            output = response.json()
            if 'data' in output and 'create_update' in output['data'] and output['data']['create_update'] is not None:
                return
        logging.error('Failed - Monday.com ' + str(item_id) + ' syncing notes ' + str(org_id))
        logging.error(response.json())


def update_monday_status(conn, timestamp, org_id, instance_state, item_id, integ_info, integ_key=None,
                         access_token=None, board_id=None, field_map=None):
    '''
    Update the status of a monday.com item.
    :param conn: db connection
    :param timestamp: timestamp when this request was made
    :param org_id: the organization ID
    :param instance_state: the status to update to
    :param item_id: monday.com item ID
    :param integ_info: (optional) the additional_info of the integration
    :param integ_key: (optional) the key for the integration; for retrieving the access token if it is not provided
    :param access_token: (optional) access token for monday.com API
    :param board_id: (optional) ID of the monday.com board the item belongs to
    :param field_map: (optional) the way TaskCall and monday.com attributes are mapped
    '''
    if access_token is None:
        mon_integ_det = db_monday.get_monday_request_acceptance_details(conn, timestamp, org_id,
                                                                        integration_key=integ_key)
        access_token = mon_integ_det[var_names.access_token]
        board_id = integ_info[var_names.monday_board_id]
        field_map = integ_info[var_names.field_mapping]

    if var_names.status in field_map:
        query = '''
            mutation ($boardId: ID!, $itemId: ID! $columnVals: JSON!) {
                change_multiple_column_values (
                    board_id: $boardId,
                    item_id: $itemId,
                    column_values: $columnVals
                )
                { id }
            }
        '''
        column_values = {field_map[var_names.status]: integ_info[var_names.status][instance_state]}
        variables = {'boardId': int(board_id), 'itemId': int(item_id), 'columnVals': json.dumps(column_values)}

        response = requests.post(url=monday_api_url, headers=monday_header_params(access_token),
                                 json={"query": query, "variables": variables})

        if response.status_code == 200:
            output = response.json()
            if 'data' in output and 'change_multiple_column_values' in output['data']\
                    and output['data']['change_multiple_column_values'] is not None:
                return

        logging.error('Failed - Monday.com ' + str(item_id) + ' status update for organization ' + str(org_id) +
                      ' - ' + integ_info[var_names.status][instance_state])
        logging.error(response.json())


def update_monday_priority(conn, timestamp, org_id, integ_key, integ_info, urgency, item_id):
    '''
    Updates the priority of a monday.com item.
    :param conn: db connection
    :param timestamp: timestamp when this request is being made
    :param org_id: organization ID
    :param integ_key: key for this integration
    :param integ_info: additional_info attribute of the integration
    :param urgency: the urgency of the instance
    :param item_id: monday.com item ID
    '''
    mon_integ_det = db_monday.get_monday_request_acceptance_details(conn, timestamp, org_id, integration_key=integ_key)
    access_token = mon_integ_det[var_names.access_token]
    board_id = integ_info[var_names.monday_board_id]
    field_map = integ_info[var_names.field_mapping]

    if var_names.status in field_map:
        query = '''
            mutation ($boardId: ID!, $itemId: ID! $columnVals: JSON!) {
                change_multiple_column_values (
                    board_id: $boardId,
                    item_id: $itemId,
                    column_values: $columnVals
                )
                { id }
            }
        '''
        column_values = {field_map[var_names.urgency_level]: integ_info[var_names.urgency_level][str(urgency)]}
        variables = {'boardId': int(board_id), 'itemId': int(item_id), 'columnVals': json.dumps(column_values)}

        response = requests.post(url=monday_api_url, headers=monday_header_params(access_token),
                                 json={"query": query, "variables": variables})

        if response.status_code == 200:
            output = response.json()
            if 'data' in output and 'change_multiple_column_values' in output['data']\
                    and output['data']['change_multiple_column_values'] is not None:
                return

        logging.error('Failed - Monday.com ' + str(item_id) + ' priority update for organization ' + str(org_id) +
                      ' - ' + integ_info[var_names.urgency_level][str(urgency)])
        logging.error(response.json())


def create_monday_webhook(conn, timestamp, org_id, integ_url, board_id, webhook_details, access_token=None,
                          integ_key=None):
    '''
    Create a new monday.com webhook.
    :param conn: db connection
    :param timestamp: timestamp when this request is being made
    :param org_id: organization ID
    :param integ_url: the url the webhook will send payloads to
    :param board_id: monday.com board ID
    :param webhook_details: (list) -> [{webhook type: , column: , value: }, ...]
    :param access_token: (optional) access token
    :param integ_key: (optional) the integration key
    :return: (int) webhook ID (None if the webhook is not created)
    '''
    if access_token is None:
        mon_integ_det = db_monday.get_monday_request_acceptance_details(conn, timestamp, org_id,
                                                                        integration_key=integ_key)
        access_token = mon_integ_det[var_names.access_token]

    query = '''
        mutation ($boardId: ID!, $url: String!, $event: WebhookEventType!, $config: JSON) {
            create_webhook (
                board_id: $boardId,
                url: $url,
                event: $event,
                config: $config
            )
            { id }
        }
    '''
    webhook_ids = []
    for item in webhook_details:
        webhook_type = item[var_names.monday_webhook_type]
        column_values = None
        if webhook_type == monday_event_change_specific_column_value:
            column_values = {'columnId': item[var_names.monday_column_id],
                             'value': item[var_names.monday_column_value]}

        variables = {'boardId': int(board_id), 'url': integ_url, 'event': webhook_type,
                     'config': json.dumps(column_values) if column_values is not None else None}
        response = requests.post(url=monday_api_url, headers=monday_header_params(access_token),
                                 json={"query": query, "variables": variables})

        if response.status_code == 200:
            output = response.json()
            if 'data' in output and 'create_webhook' in output['data'] and output['data']['create_webhook'] is not None:
                webhook_ids.append(int(output['data']['create_webhook']['id']))
            else:
                webhook_ids.append(None)
                logging.error('Failed - monday.com create webhook for organization - ' + str(org_id))
                logging.error(response.json())
        else:
            webhook_ids.append(None)
            logging.error('Failed - monday.com create webhook for organization - ' + str(org_id))
            logging.error(response.json())

    return webhook_ids


def delete_monday_webhook(conn, timestamp, org_id, integ_key, webhook_id):
    '''
    Delete a monday.com webhook
    :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 webhook_id: (int) the ID of the webhook to delete
    :return: (boolean) True if the webhook has been deleted; False otherwise
    '''
    mon_integ_det = db_monday.get_monday_request_acceptance_details(conn, timestamp, org_id, integration_key=integ_key)
    access_token = mon_integ_det[var_names.access_token]

    query = '''
        mutation ($id: ID!) {
            delete_webhook (
                id: $id
            )
            { id }
        }
    '''
    variables = {'id': int(webhook_id)}
    response = requests.post(url=monday_api_url, headers=monday_header_params(access_token),
                             json={"query": query, "variables": variables})

    if response.status_code == 200:
        output = response.json()
        if 'data' in output and 'delete_webhook' in output['data'] and output['data']['delete_webhook'] is not None:
            return True

    logging.error('Failed - monday.com delete webhook for organization - ' + str(org_id))
    logging.error(response.json())
    return False
