from __future__ import print_function, unicode_literals
from datetime import datetime
import boto3
import json


def invoke_check_runner(runner_input):
    """
    Simple function to invoke the next check_runner lambda with runner_input
    (dict containing {'sqs_url': <str>})
    """
    client = boto3.client('lambda')
    # InvocationType='Event' makes asynchronous
    # try/except while async invokes are problematic
    try:
        response = client.invoke(
            FunctionName=get_stage_info()['runner_name'],
            InvocationType='Event',
            Payload=json.dumps(runner_input)
        )
    except:
        response = client.invoke(
            FunctionName=get_stage_info()['runner_name'],
            Payload=json.dumps(runner_input)
        )
    return response


def delete_message_and_propogate(runner_input, receipt, propogate=True):
    """
    Delete the message with given receipt from sqs queue and invoke the next
    lambda runner.

    Args:
        runner_input (dict): runner info, should minimally have 'sqs_url'
        receipt (str): SQS message receipt
        propogate (bool): if True (default), invoke another check runner lambda

    Returns:
        None
    """
    sqs_url = runner_input.get('sqs_url')
    if not sqs_url or not receipt:
        return
    client = boto3.client('sqs')
    client.delete_message(
        QueueUrl=sqs_url,
        ReceiptHandle=receipt
    )
    if propogate is True:
        invoke_check_runner(runner_input)


def recover_message_and_propogate(runner_input, receipt, propogate=True):
    """
    Recover the message with given receipt to sqs queue and invoke the next
    lambda runner.

    Changing message VisibilityTimeout to 15 seconds means the message will be
    available to the queue in that much time. This is a slight lag to allow
    dependencies to process.
    NOTE: VisibilityTimeout should be less than WaitTimeSeconds in run_check_runner

    Args:
        runner_input (dict): runner info, should minimally have 'sqs_url'
        receipt (str): SQS message receipt
        propogate (bool): if True (default), invoke another check runner lambda

    Returns:
        None
    """
    sqs_url = runner_input.get('sqs_url')
    if not sqs_url or not receipt:
        return
    client = boto3.client('sqs')
    client.change_message_visibility(
        QueueUrl=sqs_url,
        ReceiptHandle=receipt,
        VisibilityTimeout=15
    )
    if propogate is True:
        invoke_check_runner(runner_input)


def get_sqs_queue():
    """
    Returns boto3 sqs resource
    """
    queue_name = get_stage_info()['queue_name']
    sqs = boto3.resource('sqs')
    try:
        queue = sqs.get_queue_by_name(QueueName=queue_name)
    except:
        queue = sqs.create_queue(
            QueueName=queue_name,
            Attributes={
                'VisibilityTimeout': '300',
                'MessageRetentionPeriod': '3600'
            }
        )
    return queue


def send_sqs_messages(queue, environ, check_vals, uuid=None):
    """
    Send messages to SQS queue. Check_vals are entries within a check_group.
    Optionally, provide a uuid that will be queued as the uuid for the run; if
    not provided, datetime.utcnow is used

    Args:
        queue: boto3 sqs resource (from get_sqs_queue)
        environ (str): foursight environment name
        check_vals (list): list of formatted check vals, like those from
            check_utils.CheckHandler.get_check_schedule
        uuid (str): optional string uuid

    Returns:
        str: uuid of queued messages
    """
    # uuid used as the MessageGroupId
    if not uuid:
        uuid = datetime.utcnow().isoformat()
    # append environ and uuid as first elements to all check_vals
    proc_vals = [[environ, uuid] + val for val in check_vals]
    for val in proc_vals:
        response = queue.send_message(MessageBody=json.dumps(val))
    return uuid


def get_sqs_attributes(sqs_url):
    """
    Returns a dict of the desired attributes form the queue with given url
    """
    backup = {
        'ApproximateNumberOfMessages': 'ERROR',
        'ApproximateNumberOfMessagesNotVisible': 'ERROR'
    }
    client = boto3.client('sqs')
    try:
        result = client.get_queue_attributes(
            QueueUrl=sqs_url,
            AttributeNames=[
                'ApproximateNumberOfMessages',
                'ApproximateNumberOfMessagesNotVisible'
            ]
        )
    except:
        return backup
    return result.get('Attributes', backup)
