#!/usr/bin/python
import urllib
from urllib.request import Request, urlopen
import json
import time
import sys
import os
import boto3

code_deploy = boto3.client('codedeploy')
ssm = boto3.client('ssm')


def http_get(url, headers={}):
    req = Request(url)

    for header_name, header_value in headers.items():
        req.add_header(header_name, header_value)

    response = urlopen(req)
    response_data = response.read().decode('utf-8')
    status_code = response.getcode()
    response.close()
    return response_data, status_code


def start_tests(runscope_trigger_url):
    response, status_code = http_get(runscope_trigger_url)

    # response = requests.get(runscope_trigger_url)
    assert status_code == 201, "Not 201 Created, response code: {}".format(str(response.status_code))
    test_start_data = json.loads(response)
    assert test_start_data['meta']['status'] == "success", "the request to run the tests did not succeed.  details: {}".format(response.text)

    print("started the runscope tests")

    tests = []
    for run in test_start_data['data']['runs']:
        test = {}
        test['url'] = "https://api.runscope.com/buckets/{}/tests/{}/results/{}".format(run['bucket_key'], run['test_id'], run['test_run_id'])
        test['status'] = 'outstanding'
        tests.append(test)
    return tests


def wait_for_tests_to_complete(tests, runscope_access_token):
    while True:
        get_outstanding_tests(tests, runscope_access_token)
        aggregate_status = check_status(tests)
        if aggregate_status == 'pass':
            print("the tests all passed")
            return "pass"
        elif aggregate_status == 'fail':
            print("at least one of the tests failed")
            return "fail"
        else:
            print("the tests are still running, sleeping for 10 second and trying again")
            time.sleep(10)


def check_status(tests):
    for test in tests:
        print("checking {}: {}".format(test['url'], test['status']))
        if test['status'] == "fail":
            return "fail"
        if test['status'] not in ["pass", "fail"]:
            return "outstanding"
    return "pass" #we managed to get here without finding a test that was failed or outstanding, so they must have all passed


def get_outstanding_tests(tests, runscope_access_token):
    for test in tests:
        if test['status'] not in ["pass", "fail"]:
            get_test(test, runscope_access_token)


def get_test(test, runscope_access_token):
    print("calling {}".format(test['url']))
    print(runscope_access_token)
    response, status_code = http_get(test['url'], headers={"Authorization": "Bearer {}".format(runscope_access_token)})
    data = json.loads(response)
    if data['data'].get('result'):
        test['status'] = data['data']['result']


def get_parameter_store_values(params_to_get):
    response = ssm.get_parameters(
        Names=params_to_get,
        WithDecryption=True
    )

    parameter_store_values = {}

    for param in response['Parameters']:
        parameter_store_values[param['Name']] = param['Value']

    return parameter_store_values



def run_tests(event, context):
    """Run Runscope tests for a CodeDeploy

    Args:
    event: The CodeDeploy json input
    context: lambda execution context

    """
    # read the deployment ID from the event payload
    deployment_id = event['DeploymentId']

    # Read the LifecycleEventHookExecutionId from the event payload
    event_hook_id = event['LifecycleEventHookExecutionId']

    try:
        # Get the runscope access token and trigger url from the parameter store
        app_name = os.environ['HANDEL_APP_NAME']
        env = os.environ['HANDEL_ENVIRONMENT_NAME']
        prefix = app_name + '.' + env + '.'
        access_token_param_name = prefix + 'runscope-access-token'
        trigger_url_param_name = prefix + 'runscope-trigger-url'
        params_to_get = [
            access_token_param_name,
            trigger_url_param_name
        ]

        parameter_store_values = get_parameter_store_values(params_to_get)
        runscope_access_token = parameter_store_values[access_token_param_name]
        runscope_trigger_url = parameter_store_values[trigger_url_param_name]

        # trigger the runscope tests
        tests = start_tests(runscope_trigger_url)
        aggregate_status = wait_for_tests_to_complete(tests, runscope_access_token)
        if aggregate_status == "pass":
            code_deploy.put_lifecycle_event_hook_execution_status(
                deploymentId=deployment_id,
                lifecycleEventHookExecutionId=event_hook_id,
                status='Succeeded'
            )
        else:
            code_deploy.put_lifecycle_event_hook_execution_status(
                deploymentId=deployment_id,
                lifecycleEventHookExecutionId=event_hook_id,
                status='Failed'
            )
    except Exception as e:
        print(e)
        code_deploy.put_lifecycle_event_hook_execution_status(
            deploymentId=deployment_id,
            lifecycleEventHookExecutionId=event_hook_id,
            status='Failed'
        )
