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

from dbqueries import db_integrations
from taskcallrest import settings
from utils import constants, integration_type_names as intt, logging, s3, var_names
import base64
import datetime
import requests


# webex s3 credential file variables
zoom_s3_bucket = 'taskcall-prod-data'
zoom_s3_key = 'credentials/zoom_credentials.json'

# webex url paths
zoom_invitees_path = 'https://webexapis.com/v1/meetingInvitees/bulkInsert'
zoom_meetings_path = 'https://api.zoom.us/v2/users/me/meetings'
zoom_token_retrieval_path = 'https://zoom.us/oauth/token'

# webex variables
var_id = 'id'
var_join_url = 'join_url'


def get_zoom_credentials():
    '''
    Get the credentials needed for handling and making API calls to Zoom.
    :return: (dict) of credentials
    '''
    creds = s3.read_json(zoom_s3_bucket, zoom_s3_key)
    if settings.TEST_SERVER:
        return creds['test']
    else:
        return creds['prod']


def get_zoom_standard_headers(access_token):
    '''
    Get the standard headers that need to be sent with Zoom API calls.
    :param access_token: Zoom access token
    :return: (dict) of headers
    '''
    return {"Authorization": "Bearer " + access_token, "Accept": "application/json"}


def zoom_refresh_token(conn, timestamp, org_id, zoom_org_id, refresh_token):
    '''
    Refresh a Zoom token.
    :param conn: db connection
    :param timestamp: timestamp when this request is being made
    :param org_id: organization ID
    :param zoom_org_id: unique ID of the organization in Zoom
    :param refresh_token: current refresh token
    :return: (str) new access token
    '''
    try:
        new_acc_tok = None
        zoom_creds = get_zoom_credentials()
        auth_key = "{}".format(base64.b64encode(
            bytes("{}:{}".format(
                zoom_creds[var_names.client_id], zoom_creds[var_names.client_secret]
            ).encode("utf-8"))).decode("utf-8"))
        headers = {'Authorization': 'Basic ' + auth_key}
        body = {
            'grant_type': 'refresh_token',
            'refresh_token': refresh_token,
        }
        response = requests.post(zoom_token_retrieval_path, headers=headers, data=body)
        status = response.status_code
        output = response.json()
        if status in [200, 201]:
            new_acc_tok = output[var_names.access_token]
            external_info = {
                var_names.access_token: new_acc_tok,
                var_names.refresh_token: output[var_names.refresh_token]
            }
            db_integrations.check_and_update_organization_integration_details(
                conn, timestamp, org_id, intt.zoom, zoom_org_id, external_info
            )
        return new_acc_tok
    except (ConnectionRefusedError, ConnectionError) as e:
        logging.exception('Failed to connect with Zoom')
        logging.exception(str(e))
        raise ConnectionRefusedError
    except Exception as e:
        logging.exception('Zoom API request failed...')
        logging.exception(str(e))
        raise ConnectionRefusedError


def create_meeting(conn, timestamp, org_id, zm_org_id, zm_acc_tok, zm_ref_tok, title, assignee_emails):
    '''
    Create a Zoom meeting.
    :param conn: db connection
    :param timestamp: timestamp when this request is being made
    :param org_id: ID of the organization
    :param zm_org_id: Zoom organization ID
    :param zm_acc_tok: Zoom access token
    :param zm_ref_tok: Zoom refresh token
    :param title: title to set for the meeting (should be the title of the incident)
    :param assignee_emails: (list) of email addresses of assignees
    '''
    meeting_id, conf_url = None, None
    if len(assignee_emails) > 0:
        invitees = [{'email': item} for item in assignee_emails]
        if len(invitees) > 0:
            body = {
                'agenda': title,
                'settings': {
                    'type': 2,
                    'waiting_room': False,
                    'join_before_host': True,
                    'start_time': (timestamp + datetime.timedelta(minutes=1)).strftime(constants.timestamp_format),
                    'invitees': invitees
                }
            }

            resp = requests.post(zoom_meetings_path, json=body, headers=get_zoom_standard_headers(zm_acc_tok))
            resp_body = resp.json()
            if resp.status_code in (200, 201):
                meeting_id, conf_url = resp_body[var_id], resp_body[var_join_url]
            elif resp.status_code == 401 and resp_body['code'] == 124:
                new_acc_tok = zoom_refresh_token(conn, timestamp, org_id, zm_org_id, zm_ref_tok)
                if new_acc_tok is not None:
                    resp = requests.post(zoom_meetings_path, json=body,
                                         headers=get_zoom_standard_headers(new_acc_tok))
                    resp_body = resp.json()
                    if resp.status_code in (200, 201) and 'code' not in resp_body:
                        meeting_id, conf_url = resp_body[var_id], resp_body[var_join_url]

    return meeting_id, conf_url
