# This file contains helper functions that can be used throughout the project.

from constants import api_paths, component_names as cnm, pages, static_vars, var_names
from django.http import HttpResponse
from django.template.loader import get_template
from icalendar import Calendar, Event, Timezone, TimezoneStandard
from io import BytesIO
from pytz import timezone
from taskcallweb import settings
from urllib.parse import urlencode
from utils import file_storage, logging, s3
from validations import request_validator, string_validator
from xhtml2pdf import pisa
import configuration
import datetime
import json
import jwt
import pytz
import random
import re
import requests
import string
import uuid


def get_api_request(url, client_request=None, get_status_only=False, lang=static_vars.lang_en):
    '''
    Makes a GET request to the api and returns the response.
    :param url: api url
    :param client_request: the original request made by the client; we will be able to
            get the access token, refresh token and expiry time from the session
    :param get_status_only: option to specify if only the status code is wanted or not
    :param lang: the language to get the response in
    :return: [status code, response]
    '''
    try:
        if client_request is not None and request_validator.user_in_session(client_request):
            authorization_token = client_request.session[var_names.token]
        else:
            authorization_token = None

        status, output = send_get_request(url, authorization_token, get_status_only, lang=lang)

        if status == 401:
            new_access_token = try_to_refresh_token(client_request)
            if new_access_token is not None:
                status, output = send_get_request(url, new_access_token, get_status_only, lang=lang)

        if get_status_only:
            return status

        return [status, output]
    except Exception as e:
        logging.exception('Could not fetch data from api...')
        logging.exception(str(e))
        raise


def post_api_request(url, body, client_request=None, get_status_only=False,
                     content_type=static_vars.content_type_json, lang=static_vars.lang_en):
    '''
    Makes a POST request to the api and returns the response.
    :param url: api url
    :param body: the body/params of the request
    :param client_request: the original request made by the client; we will be able to
            get the access token, refresh token and expiry time from the session
    :param get_status_only: option to specify if only the status code is wanted or not
    :param content_type: ContentType of the request -> 'application/json', 'application/x-www-form-urlencoded', etc
    :param lang: the language to get the response in
    :return: [status code, response]
    '''
    try:
        if client_request is not None and request_validator.user_in_session(client_request):
            authorization_token = client_request.session[var_names.token]
        else:
            authorization_token = None

        status, output = send_post_request(url, body, authorization_token, get_status_only, content_type, lang=lang)

        if status == 401:
            new_access_token = try_to_refresh_token(client_request)
            if new_access_token is not None:
                status, output = send_post_request(url, body, new_access_token, get_status_only,
                                                   content_type, lang=lang)

        if get_status_only:
            return status

        return [status, output]
    except Exception as e:
        logging.exception('Could not fetch data from api...')
        logging.exception(str(e))
        raise


def send_get_request(url, authorization_token=None, get_status_only=False, lang=static_vars.lang_en):
    '''
    Makes a GET request to the api and returns the response.
    :param url: api url
    :param authorization_token: the token that can be used to verify the request
    :param get_status_only: option to specify if only the status code is wanted or not
    :param lang: the language to get the response in
    :return: [status code, response]
    '''
    try:
        header_params = {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
            'Accept-Language': lang
        }
        if authorization_token is not None:
            header_params[static_vars.authorization_attribute] = var_names.token + " " + authorization_token

        response = requests.get(url, headers=header_params)

        if get_status_only:
            return response.status_code

        return [response.status_code, response.json()]
    except Exception as e:
        logging.exception('Could not fetch data from api...')
        logging.exception(str(e))
        raise


def send_post_request(url, body, authorization_token=None, get_status_only=False,
                      content_type=static_vars.content_type_json, lang=static_vars.lang_en,
                      accept_type=static_vars.content_type_json):
    '''
    Makes a POST request to the api and returns the response.
    :param url: api url
    :param body: the body/params of the request
    :param authorization_token: the token that can be used to verify the request
    :param get_status_only: option to specify if only the status code is wanted or not
    :param content_type: ContentType of the request -> 'application/json', 'application/x-www-form-urlencoded', etc
    :param lang: the language to get the response in
    :param accept_type: AcceptType of the request -> 'application/json', 'application/x-www-form-urlencoded', etc
    :return: [status code, response]
    '''
    try:
        header_params = {
            'Accept': accept_type,
            'Content-Type': content_type,
            'Accept-Language': lang
        }
        if authorization_token is not None:
            header_params[static_vars.authorization_attribute] = var_names.token + " " + authorization_token

        response = requests.post(url, headers=header_params,
                                 data=json.dumps(body) if content_type == static_vars.content_type_json else body)

        if get_status_only:
            return response.status_code

        return [response.status_code, response.json()]
    except Exception as e:
        logging.exception('Could not fetch data from api...')
        logging.exception(str(e))
        raise


def try_to_refresh_token(client_request):
    '''
    Tries to refresh an expired access token. If the refresh attempt works,
    then save the new access token and its expiry time.
    :param client_request: Http request originating from the client
    :return: (str) -> new access token if refresh works; None otherwise
    '''
    if client_request is not None and request_validator.user_in_session(client_request) and \
            get_current_timestamp() >= get_timestamp_from_string(client_request.session[var_names.expires_on]):

        authorization_token = client_request.session[var_names.token]
        refresh_token = client_request.session[var_names.refresh_token]
        refresh_request_body = {var_names.refresh_token: refresh_token,
                                var_names.session_id: client_request.session.session_key}

        refresh_status, refresh_output = send_post_request(
            api_paths.user_token_refresh, refresh_request_body, authorization_token
        )
        if refresh_status == 200:
            new_access_token = refresh_output[var_names.access_token]
            new_token_expiry = refresh_output[var_names.expires_on]
            client_request.session[var_names.token] = new_access_token
            client_request.session[var_names.expires_on] = timestamp_to_string(new_token_expiry)
            return new_access_token
        elif refresh_status == 401:
            clear_session_authorization_tokens(client_request)

    return None


def get_current_timestamp(region='UTC'):
    '''
    Gets the current timestamp of a region. By default returns UTC if no region is provided.
    :param region: timezone
    :return: datetime object of the current timestamp
    '''
    timestamp = datetime.datetime.now(timezone(region)).replace(tzinfo=None)
    return timestamp


def get_time_from_string(time_str):
    '''
    Convert a time str in the format HH:MM to a datetime.time object.
    :param time_str: (str) string time in the format HH::MM
    :return: (datetime.time) object
    '''
    if isinstance(time_str, datetime.time):
        return time_str
    elif isinstance(time_str, str):
        if len(time_str) == 5:
            return datetime.datetime.strptime(time_str, static_vars.time_format).time()
        else:
            return datetime.datetime.strptime(time_str, static_vars.json_time_format).time()
    else:
        raise TypeError('Expected datetime.time or str. Got - ' + str(type(time_str)))


def get_date_from_string(date_str):
    '''
    Converts a date string to a datetime.date object.
    :param date_str: the object that is supposed to be a string date
    :return: a datetime.date object
    '''
    if isinstance(date_str, datetime.date):
        return datetime.datetime.combine(date_str, datetime.datetime.min.time()).date()
    elif isinstance(date_str, str):
        return datetime.datetime.strptime(date_str, static_vars.date_format).date()
    else:
        raise TypeError('Expected datetime.date or str. Got - ' + type(date_str))


def get_timestamp_from_string(timestamp_str):
    '''
    Converts a timestamp string to a datetime.datetime object.
    :param timestamp_str: the object that is supposed to be a string date
    :return: a datetime.datetime object
    '''
    if isinstance(timestamp_str, str):
        # timestamp_str = timestamp_str.split('.')[0]

        if string_validator.is_timestamp_with_milliseconds(timestamp_str):
            return datetime.datetime.strptime(timestamp_str, static_vars.timestamp_milli_format)

        elif string_validator.is_json_timestamp_with_milliseconds(timestamp_str):
            return datetime.datetime.strptime(timestamp_str, static_vars.json_timestamp_milli_format)

        elif string_validator.is_timestamp(timestamp_str):
            return datetime.datetime.strptime(timestamp_str, static_vars.timestamp_format)

        elif string_validator.is_json_timestamp(timestamp_str):
            return datetime.datetime.strptime(timestamp_str, static_vars.json_timestamp_format)

        else:
            raise TypeError('Unknown str format - ' + timestamp_str)

    elif isinstance(timestamp_str, datetime.datetime):
        return timestamp_str
    else:
        raise TypeError('Expected datetime.date or str. Got - ' + str(type(timestamp_str)) +
                        '. Value - ' + str(timestamp_str))


def set_session_authorization_tokens(request, output):
    '''
    Set the authorization tokens for the session.
    :param request: Http request object
    :param output: the output of authorization details received from the backend
    '''
    request.session[var_names.token] = output[var_names.access_token]
    request.session[var_names.expires_on] = timestamp_to_string(output[var_names.expires_on])
    request.session[var_names.refresh_token] = output[var_names.refresh_token]
    request.session[var_names.language] = output[var_names.language]
    request.session[var_names.nav_bar_components] = output[var_names.nav_bar_components]
    request.session[var_names.component_features] = output[var_names.component_features]
    request.session[var_names.edit_permissions] = output[var_names.edit_permissions]
    request.session[var_names.has_team_permission] = output[var_names.has_team_permission]
    request.session[var_names.display_name] = output[var_names.display_name]
    request.session[var_names.profile_picture] = output[var_names.profile_picture]


def clear_session_authorization_tokens(request):
    '''
    Clear all authorization tokens of the session.
    :param request: Http request object
    '''
    del request.session[var_names.token]
    del request.session[var_names.expires_on]
    del request.session[var_names.refresh_token]
    del request.session[var_names.language]
    del request.session[var_names.nav_bar_components]
    del request.session[var_names.component_features]
    del request.session[var_names.edit_permissions]
    del request.session[var_names.has_team_permission]
    del request.session[var_names.display_name]


def set_mobile_app_login_variables(request):
    '''
    Sets the redirect_url and push_token variables for requests generated for mobile app login
    in both the session and cookies.
    :param request: Http request object
    '''
    if var_names.redirect_url in request.GET and var_names.push_token in request.GET:
        # store in the session
        request.session[var_names.mobile_app_redirect_url] = request.GET.get(var_names.redirect_url)
        request.session[var_names.mobile_app_push_token] = request.GET.get(var_names.push_token)


def clear_mobile_app_login_variables(request):
    '''
    Clears the redirect_url and push_token saved for mobile app login from both the session and cookies.
    :param request: Http request object
    '''
    if var_names.mobile_app_redirect_url in request.session and var_names.mobile_app_push_token in request.session:
        del request.session[var_names.mobile_app_redirect_url]
        del request.session[var_names.mobile_app_push_token]


def is_mobile_app_login(request):
    '''
    Checks if mobile app login variables exist in session or cookies.
    :param request: Http request object
    '''
    if var_names.mobile_app_redirect_url in request.session and var_names.mobile_app_push_token in request.session:
        return True
    return False


def get_mobile_app_push_token(request):
    '''
    Get the push token that was sent with the original mobile app login request from the session or the cookies.
    :param request: Http request
    :return: (str) --> push token  |  None if it is not found
    '''
    if var_names.mobile_app_push_token in request.session:
        return request.session[var_names.mobile_app_push_token]
    return None


def get_mobile_app_full_redirect_url(request, environment_data, redirect_url=None):
    '''
    Get the full redirect url for mobile app login deep link. The environment data for the app is sent through this url.
    :param request: Http request
    :param environment_data: (dict) of environment variables for the mobile app
    :param redirect_url: (str) the base redirect url
    :return: (str) -> redirect url
    '''
    if request is not None and var_names.mobile_app_redirect_url in request.session:
        redirect_url = request.session[var_names.mobile_app_redirect_url]

    if redirect_url is not None:
        return redirect_url + urlencode(environment_data)

    return redirect_url


def get_redirect_url_for_switching_region(request, new_region, page_name):
    '''
    Get the URL to redirect to for switching region. This function handles mobile app login as well.
    :param request: Http request
    :param new_region: region to redirect to
    :param page_name: the name of the page to redirect to
    :return: redirect url  |  None if invalid data is provided
    '''
    rdr_url = None
    if new_region in configuration.ALLOWED_REGIONS and page_name in configuration.ALLOWED_REGION_SWITCH_PATHS:
        if settings.TEST_MODE:
            rdr_url = static_vars.regional_test_mode_urls[new_region][var_names.redirect_base] \
                      + '/' + page_name
        elif settings.TEST_SERVER:
            rdr_url = static_vars.regional_test_server_urls[new_region][var_names.redirect_base] \
                      + '/' + page_name
        else:
            rdr_url = static_vars.regional_urls[new_region][var_names.redirect_base] + '/' + page_name

        if is_mobile_app_login(request):
            rdr_url = rdr_url + '?' + \
                      var_names.redirect_url + '=' + request.session[var_names.mobile_app_redirect_url] + '&' + \
                      var_names.push_token + '=' + request.session[var_names.mobile_app_push_token]

    return rdr_url


def set_session_integration_variables(request, integration_name, integration_type_id, policy_id, request_id=None):
    '''
    Sets the session variables related to a new integration process.
    :param request: the Http request for the integration
    :param integration_name: name of the integration
    :param integration_type_id: the id of the integration type
    :param policy_id: the policy id to integrate with
    :param request_id: the request id that uniquely identifies callbacks made to the vendor being integrated with
    '''
    request.session[var_names.integration_name] = integration_name
    request.session[var_names.integration_type_id] = integration_type_id
    request.session[var_names.policy_id] = policy_id

    if request_id is not None:
        request.session[var_names.integration_request_id] = request_id


def set_session_redirect_page(request):
    '''
    Set a redirect page in the request session.
    :param request: Http request
    '''
    request.session[var_names.redirect_page] = request.get_full_path()


def get_redirect_page(request):
    '''
    Gets the page that the request should be redirected to.
    :param request: Http request
    :return: (str) -> page name
    '''
    if var_names.redirect_page in request.session:
        redirect_page = request.session[var_names.redirect_page]
        del request.session[var_names.redirect_page]
        return redirect_page
    else:
        return pages.incidents_url


def has_only_stakeholder_rights(output, redirect_url):
    '''
    Checks, given the authorization details, whether a user only has stakeholder rights or not.
    :param output: authorization details received from the backend
    :param redirect_url: this is the redirect url (retrieved from the session if there is one)
            to send the user to after login
    :return: (boolean) -> True if it is; False otherwise
    '''
    if cnm.dis_com_incidents not in output[var_names.nav_bar_components] \
            and cnm.dis_com_status_dashboard in output[var_names.nav_bar_components] \
            and redirect_url == pages.incidents_url:
        return True
    return False


def get_info_from_ip_address(ip_address: str, attribute: str):
    '''
    Gets the ISO country code of an ip address
    :param ip_address: (str) ip address to check for
    :param attribute: (str) the attribute that is being checked for
    :return: requested attribute
    :errors: KeyError
    '''
    full_address = '/'.join([static_vars.ip_library_site + ip_address])
    response = requests.get(full_address).json()
    if attribute in response:
        return response[attribute]
    else:
        if attribute == static_vars.ip_timezone_attribute:
            return static_vars.standard_timezone
        elif attribute == static_vars.ip_country_code_attribute:
            return static_vars.standard_country_code
        raise KeyError('Unknown attribute - ' + attribute)


def contextualize_postmortem(report_details, incident_details):
    '''
    Puts report and incident details together in one dictionary
    that can be passed to the postmortem download template.
    :param report_details: (dict) postmortem report details
    :param incident_details: (dict) incident details
    :return: (dict) standardized details for the template
    '''
    context = report_details

    rep_tmsp = get_timestamp_from_string(report_details[var_names.report_timestamp])
    inc_tmsp = get_timestamp_from_string(incident_details[var_names.instance_timestamp])
    com_by = datetime.datetime.strptime(report_details[var_names.complete_by], "%Y-%m-%d")

    reviewers = ', '.join([item[1] for item in report_details[var_names.reviewers] if item[2] == static_vars.reviewer])
    collaborators = ', '.join([item[1] for item in report_details[var_names.reviewers]
                               if item[2] == static_vars.collaborator])

    context[var_names.incident] = incident_details
    context[var_names.reviewers] = reviewers
    context[var_names.collaborators] = collaborators
    context[var_names.report_timestamp] = rep_tmsp.strftime("%d %B %Y %I:%M %p") + " (UTC)"
    context[var_names.incident][var_names.instance_timestamp] = inc_tmsp.strftime("%d %B %Y %I:%M %p") + " (UTC)"
    context[var_names.complete_by] = com_by.strftime("%d %B %Y") + " (UTC)"
    return context


def render_to_pdf(template_src, context_dict: dict):
    '''
    Generates a PDF file given an html template and a dictionary of data.
    :param template_src: (html file name) template
    :param context_dict: (dict) details to populate the PDF with
    :return: (HttpResponse) -> of the PDF file
    '''
    template = get_template(template_src)
    html = template.render(context_dict)
    result = BytesIO()
    pdf = pisa.pisaDocument(BytesIO(html.encode("ISO-8859-1")), result)
    if not pdf.err:
        return HttpResponse(result.getvalue(), content_type='application/pdf')
    return None


def generate_random_string_key(length=None):
    '''
    Generate a random string that can be used as a key
    :param length: the size of the string
    :return: (str) random string
    '''
    if length is None:
        length = random.randint(50, 100)
    return ''.join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(length))


def make_cache_key(prefix: str, identifier: str):
    '''
    Makes a cache key given a prefix and identifier.
    :param prefix: the first part of the cache
    :param identifier: the unique key
    :return: (str) cache key
    '''
    return prefix + static_vars.cache_delimiter + identifier


def extract_token_from_header(request):
    '''
    Extracts token from a http request header.
    :param request: Http request
    :return: (str) -> token
    '''
    if static_vars.authorization_attribute in request.headers:
        header = request.headers.get(static_vars.authorization_attribute).split(' ')
        if len(header) == 2:
            if header[0] == var_names.token:
                token = header[1]
                return token
    raise None


def get_email_leads():
    '''
    Get all the email leads from a s3 file.
    :return: (dict) -> {email: timestamp, ...}
    '''
    leads = s3.read_json(file_storage.S3_BUCKET_TASKCALL_PROD_DATA, file_storage.S3_KEY_EMAIL_LEADS)
    return leads


def update_email_leads(upd_leads):
    '''
    Update the email leads file.
    :param upd_leads: (dict) updated email leads
    '''
    s3.update_json(file_storage.S3_BUCKET_TASKCALL_PROD_DATA, file_storage.S3_KEY_EMAIL_LEADS, upd_leads)


def create_org_registration_switch_region_token(email, subdomain):
    '''
    Creates a JWT token to pass to another region and switch the registration process.
    :param email: email of the owner
    :param subdomain: organization subdomain
    :return: JWT token
    '''
    access_key = s3.read_json(file_storage.S3_BUCKET_TASKCALL_PROD_DATA,
                              file_storage.S3_KEY_ORG_REGISTRATION_REGION_SWITCH)[var_names.access_key]
    payload = [email, subdomain]
    expiry_date = datetime.datetime.now() + datetime.timedelta(seconds=30)
    token = jwt.encode({var_names.payload: payload, var_names.exp: expiry_date}, access_key, algorithm='HS256')
    return token


def read_org_registration_switch_region_token(token: str):
    '''
    Read a registration switch region token and retrieve its hidden values.
    :param token: (str) the token to read
    :return: (list) -> [email, subdomain]
    :errors: jwt.ExpiredSignatureError, jwt.InvalidSignatureError
    '''
    try:
        access_key = s3.read_json(file_storage.S3_BUCKET_TASKCALL_PROD_DATA,
                                  file_storage.S3_KEY_ORG_REGISTRATION_REGION_SWITCH)[var_names.access_key]
        token_layer = jwt.decode(token, access_key, algorithms='HS256')
        token = token_layer[var_names.payload]
        return token
    except jwt.ExpiredSignatureError:
        raise
    except (jwt.InvalidSignatureError, jwt.DecodeError, KeyError):
        raise jwt.InvalidSignatureError


def create_public_request_token(subdomain, account_number):
    '''
    Creates a JWT token to pass to the REST API for public pages/requests.
    :param subdomain: organization subdomain
    :param account_number: account number of the organization
    :return: JWT token
    '''
    access_key = s3.read_json(file_storage.S3_BUCKET_TASKCALL_PROD_DATA,
                              file_storage.S3_KEY_PUBLIC_REQUEST_VERIFIER)[var_names.access_key]
    payload = [subdomain, account_number]
    expiry_date = datetime.datetime.now() + datetime.timedelta(seconds=30)
    token = jwt.encode({var_names.payload: payload, var_names.exp: expiry_date}, access_key, algorithm='HS256')
    return token


def create_public_itsm_request_token(page_url, life_days=30):
    '''
    Creates a JWT token to pass to the REST API for accessing public itsm pages.
    :param page_url: organization subdomain
    :param life_days: number of days this token will be valid for
    :return: JWT token
    '''
    access_key = s3.read_json(file_storage.S3_BUCKET_TASKCALL_PROD_DATA,
                              file_storage.S3_KEY_PUBLIC_REQUEST_VERIFIER)[var_names.access_key]
    payload = [page_url]
    expiry_date = datetime.datetime.now() + datetime.timedelta(days=life_days)
    token = jwt.encode({var_names.payload: payload, var_names.exp: expiry_date}, access_key, algorithm='HS256')
    return token


def prepare_calendar(data, time_zone, display_name):
    tz_obj = timezone(time_zone)

    cal = Calendar()
    cal.add("prodid", "//TaskCall//On-Call Shifts")
    cal.add("version", "2.0")
    cal.add("method", "PUBLISH")
    cal.add("x-wr-timezone", time_zone)

    tz_seg = Timezone()
    tz_seg.add('tzid', tz_obj)
    tz_seg.add('x-lic-location', tz_obj)

    tz_std = TimezoneStandard()
    tz_std.add('TZNAME', tz_obj)
    tz_std.add('DTSTART', datetime.datetime(1970, 3, 29))
    tz_std.add('RDATE', datetime.datetime(1970, 3, 29))

    tz_seg.add_component(tz_std)
    cal.add_component(tz_seg)

    current_timestamp = get_current_timestamp()
    for shift in data:
        for item in shift:
            shift_assignment = item[var_names.on_call][0][0] + ' | ' + display_name
            start_timestamp = get_timestamp_from_string(item[var_names.rotation_start]).replace(
                tzinfo=pytz.timezone(time_zone))
            end_timestamp = get_timestamp_from_string(item[var_names.rotation_end]).replace(
                tzinfo=pytz.timezone(time_zone))
            unique_id = '-'.join([shift_assignment, start_timestamp.strftime(static_vars.json_timestamp_format),
                                  end_timestamp.strftime(static_vars.json_timestamp_format)]).replace(' ', '')

            event = Event()
            event.add('summary', shift_assignment)
            event.add('description', shift_assignment)
            event.add('dtstart', start_timestamp)
            event.add('dtend', end_timestamp)
            event.add('dtstamp', current_timestamp)
            event.add('uid', unique_id + '@taskcallapp.com')
            cal.add_component(event)

    return cal.to_ical()


def get_twilio_authentication_details(test=False):
    '''
    Gets the credentials to connect to the Twilio account.
    :return: account SID, auth token
    '''
    str_live, str_test = 'live', 'test'
    str_sid = 'account_sid'
    str_token = 'auth_token'
    try:
        data = s3.read_json(file_storage.S3_BUCKET_TASKCALL_PROD_DATA, file_storage.S3_KEY_TWILIO_CREDENTIALS)
        if not test or not settings.TEST_MODE:
            creds = data[str_live]
        else:
            creds = data[str_test]
        return creds[str_sid], creds[str_token]
    except (OSError, IOError) as e:
        err = 'Could not read Twilio credentials file' + '\n' + str(e)
        raise OSError(err)


def timestamp_to_string(dt_object):
    '''
    Converts a datetime.datetime object to string in the json timestamp mill format.
    :returns: string timestamp
    '''
    if isinstance(dt_object, datetime.datetime):
        return dt_object.strftime(static_vars.json_timestamp_milli_format)
    return dt_object


def jsonify_unserializable(obj):
    '''
    This function should be used when dumping dict to json. datetime.datetime objects are not json serializable.
    So, we have to correct them using this function along with any other data types that are not serializable.
    :param obj: any type of object
    :return: (object) -> if the object is a datetime.datetime object then the format is corrected;
        otherwise returns the object as it is
    '''
    if isinstance(obj, datetime.datetime):
        obj_iso = obj.isoformat()
        if len(obj_iso) > 25 and obj_iso[-1] == '0':
            obj_iso = obj_iso[:-1]
        return obj_iso
    elif isinstance(obj, datetime.date) or isinstance(obj, datetime.time):
        return obj.isoformat()
    elif isinstance(obj, uuid.UUID):
        return str(obj)
    else:
        return obj


def get_itsm_token_name(page_url):
    '''
    Get the label to use for status page token. Each token is expected to be unique.
    Otherwise, access restriction will not work across multiple status pages on the same domain.
    :param page_url: url of the main page (without the sub paths)
    :return: (str) token name
    '''
    return '_'.join([var_names.token] + page_url.replace('https://', '').split('/'))


def get_status_page_domain_from_url(page_url):
    '''
    Get the domain of a status page from its url.
    :return: (str) domain of the page
    '''
    pattern = r"^https://(.*)/status(?:/.+){0,1}/{0,1}$"
    domain = None
    found = re.search(pattern, page_url)
    if found is not None:
        try:
            domain = found.group(1)
        except Exception:
            domain = None
    return domain
