# Copyright (c) 2021 CloudZero - ALL RIGHTS RESERVED - PROPRIETARY AND CONFIDENTIAL
# Unauthorized copying of this file and/or project, via any medium is strictly prohibited.
# Direct all questions to legal@cloudzero.com

import time

import pytest

from czc.constants import FEATURE_NAME
from czt.unittest import Context, MockEx

from tests.sample_data import NAMESPACE, SENTRY_SECRET_STRING


def _create_test_secret(secrets, namespace):
    secrets.create_secret(Name=f'cz-{namespace}-feature-setup-secrets', SecretString=SENTRY_SECRET_STRING)


@pytest.fixture(scope='function')
def context(mocker, environ, mock_secretsmanager):
    import czc.performance_sla as performance_sla

    _create_test_secret(mock_secretsmanager, NAMESPACE)
    with Context(mocker, performance_sla, modules_to_mock=['sentry'], extra={'performance_sla': performance_sla}) as context:
        yield context
    mocker.stopall()


@pytest.mark.unit
@pytest.mark.parametrize('should_exceed', [True, False])
@pytest.mark.parametrize('should_notify', [True, False])
def test_performance_sla_decorator(context, should_exceed, should_notify):
    expect_notification = should_exceed and should_notify
    sla = 1
    elapsed_time = 2 if should_exceed else 1

    @context.performance_sla.check(sla, should_notify)
    def decorated_func():
        time.sleep(elapsed_time)

    decorated_func()

    if expect_notification:
        assert context.mock_sentry.send.call_count == 1
        send_mock = MockEx(context.mock_sentry.send)
        send_mock.was_called_once_with(
            message={
                'sla_seconds': sla,
                'elapsed_time': 2,
            },
            tags={
                'alarm_name': 'performance-sla-exceeded',
                'namespace': NAMESPACE,
                'environment': NAMESPACE,
                'feature': FEATURE_NAME,
                'func': decorated_func.__name__,
            },
        )
    else:
        assert context.mock_sentry.send.call_count == 0


@pytest.mark.unit
def test_performance_sla_decorator_works_with_exception(context):
    @context.performance_sla.check(1, True)
    def decorated_func():
        time.sleep(2)
        raise 'Failed!'

    with pytest.raises(Exception):
        decorated_func()

    assert context.mock_sentry.send.call_count == 1


@pytest.mark.unit
def test_performance_sla_decorator_failed_sentry_call_handled(context):
    context.mock_sentry.send.side_effect = Exception('Failed')

    @context.performance_sla.check(1, True)
    def decorated_func():
        time.sleep(2)

    decorated_func()

    assert context.mock_sentry.send.call_count == 1


@pytest.mark.unit
def test_performance_sla_decorator_works_with_any_params(context):
    @context.performance_sla.check(1, True)
    def decorated_func(arg1, arg2):
        assert arg1 == 10
        assert arg2 == 'test'

    decorated_func(10, 'test')


@pytest.mark.unit
def test_performance_sla_decorator_works_with_named_params(context):
    @context.performance_sla.check(1, True)
    def decorated_func(*, arg1, arg2):
        assert arg1 == 10
        assert arg2 == 'test'

    decorated_func(arg1=10, arg2='test')


@pytest.mark.unit
def test_performance_sla_decorator_works_with_reg_and_named_params(context):
    @context.performance_sla.check(1, True)
    def decorated_func(arg1, arg2, kwarg1, kwarg2):
        assert arg1 == 10
        assert arg2 == 'test'
        assert kwarg1 == 'something'
        assert kwarg2 == 50

    decorated_func(10, 'test', kwarg1='something', kwarg2=50)
