import pytest

from aiobotocore.session import get_session
from botocore.stub import Stubber, ANY
from botocore.exceptions import ClientError

from aws_xray_sdk.core import patch
from aws_xray_sdk.core.async_context import AsyncContext
from aws_xray_sdk.core import xray_recorder

patch(('aiobotocore',))


@pytest.fixture(scope='function')
def recorder(event_loop):
    """
    Clean up before and after each test run
    """
    xray_recorder.configure(
        service='test', sampling=False, context=AsyncContext(loop=event_loop)
    )
    xray_recorder.clear_trace_entities()
    yield xray_recorder
    xray_recorder.clear_trace_entities()


async def test_describe_table(event_loop, recorder):
    segment = recorder.begin_segment('name')

    req_id = '1234'
    response = {'ResponseMetadata': {'RequestId': req_id, 'HTTPStatusCode': 403}}

    session = get_session()
    async with session.create_client('dynamodb', region_name='eu-west-2') as client:
        with Stubber(client) as stubber:
            stubber.add_response('describe_table', response, {'TableName': 'mytable'})
            await client.describe_table(TableName='mytable')

    subsegment = segment.subsegments[0]
    assert subsegment.error
    assert subsegment.http['response']['status'] == 403

    aws_meta = subsegment.aws
    assert aws_meta['table_name'] == 'mytable'
    assert aws_meta['request_id'] == req_id
    assert aws_meta['region'] == 'eu-west-2'
    assert aws_meta['operation'] == 'DescribeTable'


async def test_s3_parameter_capture(event_loop, recorder):
    segment = recorder.begin_segment('name')

    bucket_name = 'mybucket'
    key = 'mykey'
    version_id = 'myversionid'
    response = {'ResponseMetadata': {'RequestId': '1234', 'HTTPStatusCode': 200}}

    session = get_session()
    async with session.create_client('s3', region_name='eu-west-2') as client:
        with Stubber(client) as stubber:
            stubber.add_response('get_object', response,
                                 {'Bucket': bucket_name, 'Key': key, 'VersionId': version_id})
            await client.get_object(Bucket=bucket_name, Key=key,
                                    VersionId=version_id)

    subsegment = segment.subsegments[0]
    aws_meta = subsegment.aws

    assert aws_meta['bucket_name'] == bucket_name
    assert aws_meta['key'] == key
    assert aws_meta['version_id'] == version_id
    assert aws_meta['operation'] == 'GetObject'


async def test_list_parameter_counting(event_loop, recorder):
    """
    Test special parameters that have shape of list are recorded
    as count based on `para_whitelist.json`
    """
    segment = recorder.begin_segment('name')

    queue_urls = ['url1', 'url2']
    queue_name_prefix = 'url'
    response = {
        'QueueUrls': queue_urls,
        'ResponseMetadata': {
            'RequestId': '1234',
            'HTTPStatusCode': 200,
        }
    }

    session = get_session()
    async with session.create_client('sqs', region_name='eu-west-2') as client:
        with Stubber(client) as stubber:
            stubber.add_response('list_queues', response, {'QueueNamePrefix': queue_name_prefix})
            await client.list_queues(QueueNamePrefix='url')

    subsegment = segment.subsegments[0]
    assert subsegment.http['response']['status'] == 200

    aws_meta = subsegment.aws
    assert aws_meta['queue_count'] == len(queue_urls)
    # all whitelisted input parameters will be converted to snake case
    # unless there is an explicit 'rename_to' attribute in json key
    assert aws_meta['queue_name_prefix'] == queue_name_prefix


async def test_map_parameter_grouping(event_loop, recorder):
    """
    Test special parameters that have shape of map are recorded
    as a list of keys based on `para_whitelist.json`
    """
    segment = recorder.begin_segment('name')

    response = {
        'ResponseMetadata': {
            'RequestId': '1234',
            'HTTPStatusCode': 500,
        }
    }

    session = get_session()
    async with session.create_client('dynamodb', region_name='eu-west-2') as client:
        with Stubber(client) as stubber:
            stubber.add_response('batch_write_item', response, {'RequestItems': ANY})
            await client.batch_write_item(RequestItems={'table1': [{}], 'table2': [{}]})

    subsegment = segment.subsegments[0]
    assert subsegment.fault
    assert subsegment.http['response']['status'] == 500

    aws_meta = subsegment.aws
    assert sorted(aws_meta['table_names']) == ['table1', 'table2']


async def test_context_missing_not_swallow_return(event_loop, recorder):
    xray_recorder.configure(service='test', sampling=False,
                            context=AsyncContext(loop=event_loop),
                            context_missing='LOG_ERROR')

    response = {'ResponseMetadata': {'RequestId': '1234', 'HTTPStatusCode': 403}}

    session = get_session()
    async with session.create_client('dynamodb', region_name='eu-west-2') as client:
        with Stubber(client) as stubber:
            stubber.add_response('describe_table', response, {'TableName': 'mytable'})
            actual_resp = await client.describe_table(TableName='mytable')

    assert actual_resp == response


async def test_context_missing_not_suppress_exception(event_loop, recorder):
    xray_recorder.configure(service='test', sampling=False,
                            context=AsyncContext(loop=event_loop),
                            context_missing='LOG_ERROR')

    session = get_session()
    async with session.create_client('dynamodb', region_name='eu-west-2') as client:
        with Stubber(client) as stubber:
            stubber.add_client_error('describe_table', expected_params={'TableName': ANY})
            with pytest.raises(ClientError):
                await client.describe_table(TableName='mytable')
