import logging
from flask import abort, current_app, request, Response

class HttpLimit():
    """Flask Http Request Limit extension

    Methods:
        init_app
    """
    def __init__(self, app, uid_provider, rules, filters=None, logger=None):
        """
        Initialize extension.
        
        Arguments:
            app: flask application object
            uid_provider: unique id provider
            rules: array of rules to be applied            
            filters: list of filters to evaluate if rules should apply. If empty, aplies rules.
            logger: logger instance of python's standard logging library
        """
        self.app = app
        self.uid_provider = uid_provider
        self.rules = rules
        self.filters = filters or []
        self.logger = logger or logging.getLogger(__name__)

        if app is not None:
            self.init_app(app)

    def init_app(self, app):
        """
        Initializes app, to be used in multi Flask application scenarios.
        """
        self.logger.debug("init_app called")
        
        app.before_request(self._limit_request)
        
        self.logger.debug("init_app finished")

    def _limit_request(self):
        """
        Check filters and execute the limit rules passing the uid generated by the provider if filters apply.
        """
        self.logger.debug("_limit_request called")
        app = self._get_app()
        with app.app_context():
            should_apply_rules = True
            uid = self.uid_provider.get_uid(request)

            for filter in self.filters:
                if filter.ignore(request):
                    should_apply_rules = False
                    break

            self.logger.debug("should_apply_rules [{uid}]: {should_apply_rules}".format(uid=uid, should_apply_rules=should_apply_rules))

            if should_apply_rules:
                for rule in self.rules:
                    try:
                        rule.apply(uid)
                        self.logger.debug("_limit_request finished")

                    except HttpLimitError as ex:
                        self.logger.error("HttpLimitError raised, aborting request with status code {status}".format(status=ex.status_code))
                        abort(ex.status_code, ex.message)

    def _get_app(self):
        """
        Get the current running flask app.
        """
        if self.app:
            return self.app

        return current_app

class HttpLimitError(Exception):
    """
    Error to report the limit defined by the rule has been reached.
    """
    def __init__(self, status_code, message):
        """
        Initializes error.
        
        Arguments:
            status_code: http status code
            msmessageg: error message to be returned
        """
        self.status_code = status_code
        self.message = message
        super(HttpLimitError, self).__init__(self.message)