# -*- coding: utf-8 -*-

# Copyright 2010 - 2017 RhodeCode GmbH and the AppEnlight project authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import logging
import urllib.parse

import sqlalchemy as sa
from pyramid.threadlocal import get_current_request

from appenlight.lib.cache_regions import get_region
from appenlight.lib.enums import ReportType
from appenlight.models import get_db_session
from appenlight.models.report_group import ReportGroup
from appenlight.models.application import Application
from appenlight.models.event import Event
from appenlight.models.services.base import BaseService
from appenlight.models.services.event import EventService

log = logging.getLogger(__name__)


class ApplicationService(BaseService):
    @classmethod
    def all(cls, db_session=None):
        db_session = get_db_session(db_session)
        q = db_session.query(Application)
        return q

    @classmethod
    def by_api_key(cls, api_key, db_session=None):
        db_session = get_db_session(db_session)
        q = db_session.query(Application)
        q = q.filter(Application.api_key == api_key)
        q = q.options(sa.orm.eagerload(Application.owner))
        return q.first()

    @classmethod
    def by_api_key_cached(cls, db_session=None):
        db_session = get_db_session(db_session)
        cache_region = get_region("redis_min_1")

        @cache_region.cache_on_arguments("ApplicationService.by_api_key")
        def cached(*args, **kwargs):
            app = cls.by_api_key(*args, db_session=db_session, **kwargs)
            if app:
                db_session.expunge(app)
            return app

        return cached

    @classmethod
    def by_public_api_key(
        cls, api_key, db_session=None, from_cache=False, request=None
    ):
        db_session = get_db_session(db_session)
        cache_region = get_region("redis_min_1")

        def uncached(api_key):
            q = db_session.query(Application)
            q = q.filter(Application.public_key == api_key)
            q = q.options(sa.orm.eagerload(Application.owner))
            return q.first()

        if from_cache:

            @cache_region.cache_on_arguments("ApplicationService.by_public_api_key")
            def cached(api_key):
                app = uncached(api_key)
                if app:
                    db_session.expunge(app)
                return app

            app = cached(api_key)
        else:
            app = uncached(api_key)
        return app

    @classmethod
    def by_id(cls, db_id, db_session=None):
        db_session = get_db_session(db_session)
        q = db_session.query(Application)
        q = q.filter(Application.resource_id == db_id)
        return q.first()

    @classmethod
    def by_id_cached(cls, db_session=None):
        db_session = get_db_session(db_session)
        cache_region = get_region("redis_min_1")

        @cache_region.cache_on_arguments("ApplicationService.by_id")
        def cached(*args, **kwargs):
            app = cls.by_id(*args, db_session=db_session, **kwargs)
            if app:
                db_session.expunge(app)
            return app

        return cached

    @classmethod
    def by_ids(cls, db_ids, db_session=None):
        db_session = get_db_session(db_session)
        query = db_session.query(Application)
        query = query.filter(Application.resource_id.in_(db_ids))
        return query

    @classmethod
    def by_http_referer(cls, referer_string, db_session=None):
        db_session = get_db_session(db_session)
        domain = urllib.parse.urlsplit(referer_string, allow_fragments=False).netloc
        if domain:
            if domain.startswith("www."):
                domain = domain[4:]
        q = db_session.query(Application).filter(Application.domain == domain)
        return q.first()

    @classmethod
    def last_updated(cls, since_when, exclude_status=None, db_session=None):
        db_session = get_db_session(db_session)
        q = db_session.query(Application)
        q2 = ReportGroup.last_updated(
            since_when, exclude_status=exclude_status, db_session=db_session
        )
        q2 = q2.from_self(ReportGroup.resource_id)
        q2 = q2.group_by(ReportGroup.resource_id)
        q = q.filter(Application.resource_id.in_(q2))
        return q

    @classmethod
    def check_for_groups_alert(cls, resource, event_type, *args, **kwargs):
        """ Check for open alerts depending on group type.
        Create new one if nothing is found and send alerts """
        db_session = get_db_session(kwargs.get("db_session"))
        request = get_current_request()
        report_groups = kwargs["report_groups"]
        occurence_dict = kwargs["occurence_dict"]

        error_reports = 0
        slow_reports = 0
        for group in report_groups:
            occurences = occurence_dict.get(group.id, 1)
            if group.get_report().report_type == ReportType.error:
                error_reports += occurences
            elif group.get_report().report_type == ReportType.slow:
                slow_reports += occurences

        log_msg = "LIMIT INFO: %s : %s error reports. %s slow_reports" % (
            resource,
            error_reports,
            slow_reports,
        )
        logging.warning(log_msg)
        threshold = 10
        for event_type in ["error_report_alert", "slow_report_alert"]:
            if (
                error_reports < resource.error_report_threshold
                and event_type == "error_report_alert"
            ):
                continue
            elif (
                slow_reports <= resource.slow_report_threshold
                and event_type == "slow_report_alert"
            ):
                continue
            if event_type == "error_report_alert":
                amount = error_reports
                threshold = resource.error_report_threshold
            elif event_type == "slow_report_alert":
                amount = slow_reports
                threshold = resource.slow_report_threshold

            event = EventService.for_resource(
                [resource.resource_id],
                event_type=Event.types[event_type],
                status=Event.statuses["active"],
            )
            if event.first():
                log.info("ALERT: PROGRESS: %s %s" % (event_type, resource))
            else:
                log.warning("ALERT: OPEN: %s %s" % (event_type, resource))
                new_event = Event(
                    resource_id=resource.resource_id,
                    event_type=Event.types[event_type],
                    status=Event.statuses["active"],
                    values={"reports": amount, "threshold": threshold},
                )
                db_session.add(new_event)
                new_event.send_alerts(request=request, resource=resource)
