# 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 czc.sentry as sentry
from czc.constants import FEATURE_NAME, NAMESPACE
from czc.logging import get_logger

logger = get_logger()


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):  # noqa: A002
        end = timer()
        elapsed_time = datetime.timedelta(seconds=end - self.start_time)
        logger.log(self.level, f'Elapsed time for {self.name}: {elapsed_time}s')  # type: ignore[attr-defined]
        if elapsed_time.seconds > self.sla_seconds:
            logger.warning(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):
                return func(*kargs, **kwargs)

        return func_wrapper

    return perf_sla_wrapper
