# 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 datetime
import logging
from timeit import default_timer as timer

import pyfaaster.aws.tools as faaster_aws_tools

import czc.sentry as sentry
from czc.constants import FEATURE_NAME, NAMESPACE, PROJECT_NAME

logger = faaster_aws_tools.setup_logging(PROJECT_NAME)


class SlaTimer:
    def __init__(self, name, sla_seconds: float, notify_sentry: bool, level=logging.INFO):
        self.name = name
        self.level = level
        self.sla_seconds = sla_seconds
        self.notify_sentry = notify_sentry

    def __enter__(self):
        self.start_time = timer()

    def __exit__(self, type, value, traceback):
        end = timer()
        elapsed_time = datetime.timedelta(seconds=end - self.start_time)
        logger.log(self.level, f"Elapsed time for {self.name}: {elapsed_time}s")
        if elapsed_time.seconds > self.sla_seconds:
            logger.warn(f"Elapsed time for {self.name} ({elapsed_time}s) exceeds SLA of {self.sla_seconds}s")
            if self.notify_sentry:
                try:
                    sentry.send(message={
                                    'error': 'Performance SLA exceeded',
                                    'sla_seconds': datetime.timedelta(seconds=self.sla_seconds),
                                    'elapsed_time': elapsed_time,
                                },
                                tags={
                                    'alarm_name': 'performance-sla-exceeded',
                                    'namespace': NAMESPACE,
                                    'environment': NAMESPACE,
                                    'feature': FEATURE_NAME,
                                    'func': self.name
                                })
                except Exception as exc:
                    logger.error(f"Failed to send the performance SLA failure to Sentry: {exc}")


def check(sla_seconds: float, notify_sentry=True, log_level=logging.INFO):
    def perf_sla_wrapper(func):
        def func_wrapper(*kargs, **kwargs):
            with SlaTimer(f"{func.__name__}()", sla_seconds, notify_sentry, level=log_level):
                result = func(*kargs, **kwargs)
                return result
        return func_wrapper
    return perf_sla_wrapper
