""" The below python code is for a Lambda script to accept values from
SecurityHub events(Inspector findings) and send to ServicenNow" """

import json
from os import environ
import logging
import boto3
import requests
import getsecrets

LOG_LEVEL = str(environ.get('LOG_LEVEL')).upper()
if LOG_LEVEL not in ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL']:
    LOG_LEVEL = 'INFO'

LOGGER = logging.getLogger()
LOGGER.setLevel(LOG_LEVEL)


def get_ddb_client():
    """ returns the dynamodb boto3 client """
    return boto3.client('dynamodb')


def sendto_servicenow(payload):
    """ This function extracts secrets from SecretsManager
    for ServiceNow http request"""

    secret_name = environ['SecretName']
    aws_region = environ['AWSRegion']
    secret = getsecrets.get_secret(secret_name, aws_region)
    user = secret["username"]
    pwd = secret["password"]
    url = environ["ServiceNowUrlVuln"]

    headers = {"Content-Type": "application/json", "Accept": "application/json"}
    resp = requests.post(url, auth=(user, pwd), headers=headers, data=payload.encode('utf8'))
    return resp


def capture_err(resources_id, vuln_id, err, event, err_table_name):
    """ This function will capture empty event values and store in ddb """
    """ This logic works best if more fields are added to the payload logic"""

    ddbclient = get_ddb_client()
    ddb_response = ddbclient.put_item(
        TableName=err_table_name, Item={
            'instance-id' : {'S': resources_id},
            'vulnerability': {'S': vuln_id},
            'status_code': {'S': "999"},
            'status': {'S': str(err)},
            'event': {'S': str(event)}})
    return ddb_response


def capture_servicenow_fallout(fallout, event, payload, err_table_name):
    """ This function sends ServiceNow fallouts to ddb"""

    event_detail = event['detail']['findings'][0]
    resources_id = event_detail['Resources'][0]['Id']
    vuln_id = event_detail['Vulnerabilities'][0]['Id']

    ddbclient = get_ddb_client()
    ddb_response = ddbclient.put_item(
        TableName=err_table_name, Item={
            'instance-id' : {'S': resources_id},
            'vulnerability': {'S': vuln_id},
            'status_code': {'S': fallout['status_code']},
            'status': {'S': fallout['status']},
            'event': {'S' : str(event)},
            'payload': {'S' : str(payload)}})
    return ddb_response


def create_payload(event): # pylint: disable=too-many-locals, too-many-statements
    """ Creates a payload for ServiceNow from event """

    # Declaring preset values (will change if error in payload)
    payload_err = False
    payload_err_message = ""

    LOGGER.info('Event from security hub %s', event)

    # using e_d for event datails shortcut
    e_d = event['detail']['findings'][0]

    # Mapping event values for payload
    try:
        vuln_id = e_d['Vulnerabilities'][0]['Id']
        resources_id = e_d['ProductFields']['attributes/INSTANCE_ID']

        try:
            ev0 = e_d['ProductFields']['attributes/CVSS2_SCORE']
            cvss_score_2 = str(e_d['ProductFields']['attributes/CVSS2_SCORE'])
        except:
            cvss_score_2 = ""

        # Create paylod for ServiceNow API
        payload = {"u_cvss2_score": cvss_score_2,
                   "u_cve_id": vuln_id,
                   "u_instance_id": resources_id}

        payload = json.dumps(payload)
        LOGGER.info('Payload for servcie now is %s', payload)

    except IndexError as err:
        # Triggered when AWS sends an empty value that breaks payload
        # The CVE ID and ARN are recorded instead as stand alone

        payload = {"u_rscrs_id": resources_id, "u_vuln_id":vuln_id}
        payload = json.dumps(payload)
        LOGGER.warning('Compressed payload sent %s', payload)
        payload_err = True
        payload_err_message = err

    return payload, payload_err, payload_err_message


def lambda_handler(event, _):
    """ Initial entry"""
    # Global value for error tracking

    event_detail = event['detail']['findings'][0]
    resources_id = event_detail['Resources'][0]['Id']
    vuln_id = event_detail['Vulnerabilities'][0]['Id']
    err_table_name = environ['ddbTableName']

    # Create payload request
    payload = create_payload(event)

    # Check for error in payload creation and record in ddb
    if payload[1] is True:
        capture_err(resources_id, vuln_id, payload[2], event, err_table_name)

    # Send payload to ServiceNow
    response = sendto_servicenow(payload[0])
    # Check for HTTP codes other than 201 -insert
    if response.status_code != 201:

        # Convert http response to dictionary for ddb input
        fallout = {}
        fallout["status_code"] = str(response.status_code)
        fallout["status"] = str(response.json())

        # Records fallout in dynamodb
        capture_servicenow_fallout(fallout, event, payload, err_table_name)

        status = {
            'statusCode': response.status_code,
            'body': response.json()
        }
    else:
        status = {
            'statusCode': response.status_code,
            'body': json.dumps('Successfully Uploaded to ServiceNow')
        }

    return status
