# -*- 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 pyramid_mailer
import pyramid.renderers
import sqlalchemy as sa

from collections import namedtuple
from datetime import datetime

from ziggurat_foundations.models.services.user import UserService

from appenlight.lib.rule import Rule
from appenlight.models import get_db_session
from appenlight.models.integrations import IntegrationException
from appenlight.models.report import REPORT_TYPE_MATRIX
from appenlight.models.user import User
from paginate_sqlalchemy import SqlalchemyOrmPage
from pyramid.threadlocal import get_current_registry

log = logging.getLogger(__name__)

GroupOccurence = namedtuple("GroupOccurence", ["occurences", "group"])


class UserService(UserService):
    @classmethod
    def all(cls, db_session=None):
        return get_db_session(db_session).query(User).order_by(User.user_name)

    @classmethod
    def send_email(
        cls, request, recipients, variables, template, immediately=False, silent=False
    ):
        html = pyramid.renderers.render(template, variables, request)
        title = variables.get("email_title", variables.get("title", "No Title"))
        title = title.replace("\r", "").replace("\n", "")
        sender = "{} <{}>".format(
            request.registry.settings["mailing.from_name"],
            request.registry.settings["mailing.from_email"],
        )
        message = pyramid_mailer.message.Message(
            subject=title, sender=sender, recipients=recipients, html=html
        )
        if immediately:
            try:
                request.registry.mailer.send_immediately(message)
            except Exception as e:
                log.warning("Exception %s" % e)
                if not silent:
                    raise
        else:
            request.registry.mailer.send(message)

    @classmethod
    def get_paginator(
        cls,
        page=1,
        item_count=None,
        items_per_page=50,
        order_by=None,
        filter_settings=None,
        exclude_columns=None,
        db_session=None,
    ):
        registry = get_current_registry()
        if not exclude_columns:
            exclude_columns = []
        if not filter_settings:
            filter_settings = {}
        db_session = get_db_session(db_session)
        q = db_session.query(User)
        if filter_settings.get("order_col"):
            order_col = filter_settings.get("order_col")
            if filter_settings.get("order_dir") == "dsc":
                sort_on = "desc"
            else:
                sort_on = "asc"
            q = q.order_by(getattr(sa, sort_on)(getattr(User, order_col)))
        else:
            q = q.order_by(sa.desc(User.registered_date))
            # remove urlgen or it never caches count
        cache_params = dict(filter_settings)
        cache_params.pop("url", None)
        cache_params.pop("url_maker", None)

        @registry.cache_regions.redis_min_5.cache_on_arguments()
        def estimate_users(cache_key):
            o_q = q.order_by(False)
            return o_q.count()

        item_count = estimate_users(cache_params)
        # if the number of pages is low we may want to invalidate the count to
        # provide 'real time' update - use case -
        # errors just started to flow in
        if item_count < 1000:
            item_count = estimate_users.refresh(cache_params)
        paginator = SqlalchemyOrmPage(
            q,
            page=page,
            item_count=item_count,
            items_per_page=items_per_page,
            **filter_settings
        )
        return paginator

    @classmethod
    def get_valid_channels(cls, user):
        return [channel for channel in user.alert_channels if channel.channel_validated]

    @classmethod
    def report_notify(
        cls, user, request, application, report_groups, occurence_dict, db_session=None
    ):
        db_session = get_db_session(db_session)
        if not report_groups:
            return True
        since_when = datetime.utcnow()
        for channel in cls.get_valid_channels(user):
            confirmed_groups = []

            for group in report_groups:
                occurences = occurence_dict.get(group.id, 1)
                for action in channel.channel_actions:
                    not_matched = (
                        action.resource_id
                        and action.resource_id != application.resource_id
                    )
                    if action.type != "report" or not_matched:
                        continue
                    should_notify = action.action == "always" or not group.notified
                    rule_obj = Rule(action.rule, REPORT_TYPE_MATRIX)
                    report_dict = group.get_report().get_dict(request)
                    if rule_obj.match(report_dict) and should_notify:
                        item = GroupOccurence(occurences, group)
                        if item not in confirmed_groups:
                            confirmed_groups.append(item)

            # send individual reports
            total_confirmed = len(confirmed_groups)
            if not total_confirmed:
                continue
            try:
                channel.notify_reports(
                    resource=application,
                    user=user,
                    request=request,
                    since_when=since_when,
                    reports=confirmed_groups,
                )
            except IntegrationException as e:
                log.warning("%s" % e)
