# -*- 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

from datetime import datetime, timedelta
from pyramid.view import view_config
from pyramid.httpexceptions import HTTPUnprocessableEntity

from ziggurat_foundations.models.services.resource import ResourceService
from ziggurat_foundations.models.services.user import UserService

from appenlight.models import DBSession
from appenlight.models.user import User
from appenlight.models.report_comment import ReportComment
from appenlight.models.report_assignment import ReportAssignment
from appenlight.models.services.user import UserService
from appenlight.models.services.report_group import ReportGroupService
from appenlight import forms
from appenlight.lib.enums import ReportType
from appenlight.lib.helpers import gen_pagination_headers
from appenlight.lib.utils import build_filter_settings_from_query_dict
from appenlight.validators import (
    ReportSearchSchema,
    TagListSchema,
    accepted_search_params,
)
from webob.multidict import MultiDict

_ = str

log = logging.getLogger(__name__)

section_filters_key = "appenlight:reports:filter:%s"


@view_config(route_name="reports", renderer="json", permission="authenticated")
@view_config(route_name="slow_reports", renderer="json", permission="authenticated")
def index(request):
    """
    Returns list of report groups based on user search query
    """
    if request.user:
        request.user.last_login_date = datetime.utcnow()

    applications = UserService.resources_with_perms(
        request.user, ["view"], resource_types=["application"]
    )

    search_params = request.GET.mixed()

    all_possible_app_ids = set([app.resource_id for app in applications])
    schema = ReportSearchSchema().bind(resources=all_possible_app_ids)
    tag_schema = TagListSchema()
    filter_settings = schema.deserialize(search_params)
    tag_list = [
        {"name": k, "value": v}
        for k, v in filter_settings.items()
        if k not in accepted_search_params
    ]
    tags = tag_schema.deserialize(tag_list)
    filter_settings["tags"] = tags
    if request.matched_route.name == "slow_reports":
        filter_settings["report_type"] = [ReportType.slow]
    else:
        filter_settings["report_type"] = [ReportType.error]

    reports_paginator = ReportGroupService.get_paginator_by_app_ids(
        app_ids=filter_settings["resource"],
        page=filter_settings["page"],
        filter_settings=filter_settings,
    )
    reports = []
    include_keys = (
        "id",
        "http_status",
        "report_type",
        "resource_name",
        "front_url",
        "resource_id",
        "error",
        "url_path",
        "tags",
        "duration",
    )
    for report in reports_paginator.sa_items:
        reports.append(report.get_dict(request, include_keys=include_keys))
    headers = gen_pagination_headers(request, reports_paginator)
    request.response.headers.update(headers)
    return reports


@view_config(
    route_name="report_groups", renderer="json", permission="view", request_method="GET"
)
def view_report(request):
    """
    Show individual detailed report group along with latest report
    """
    report_group = request.context.report_group
    if not report_group.read:
        report_group.read = True

    report_id = request.params.get("reportId", request.params.get("report_id"))
    report_dict = report_group.get_report(report_id).get_dict(request, details=True)
    # disallow browsing other occurences by anonymous
    if not request.user:
        report_dict.pop("group_next_report", None)
        report_dict.pop("group_previous_report", None)
    return report_dict


@view_config(
    route_name="report_groups",
    renderer="json",
    permission="update_reports",
    request_method="DELETE",
)
def remove(request):
    """
    Used to remove reourt groups from database
    """
    report = request.context.report_group
    form = forms.ReactorForm(request.POST, csrf_context=request)
    form.validate()
    DBSession.delete(report)
    return True


@view_config(
    route_name="report_groups_property",
    match_param="key=comments",
    renderer="json",
    permission="view",
    request_method="POST",
)
def comment_create(request):
    """
    Creates user comments for report group, sends email notifications
    of said comments
    """
    report_group = request.context.report_group
    application = request.context.resource
    form = forms.CommentForm(MultiDict(request.unsafe_json_body), csrf_context=request)
    if request.method == "POST" and form.validate():
        comment = ReportComment(
            owner_id=request.user.id, report_time=report_group.first_timestamp
        )
        form.populate_obj(comment)
        report_group.comments.append(comment)
        perm_list = ResourceService.users_for_perm(application, "view")
        uids_to_notify = []
        users_to_notify = []
        for perm in perm_list:
            user = perm.user
            if (
                "@{}".format(user.user_name) in comment.body
                and user.id not in uids_to_notify
            ):
                uids_to_notify.append(user.id)
                users_to_notify.append(user)

        commenters = ReportGroupService.users_commenting(
            report_group, exclude_user_id=request.user.id
        )
        for user in commenters:
            if user.id not in uids_to_notify:
                uids_to_notify.append(user.id)
                users_to_notify.append(user)

        for user in users_to_notify:
            email_vars = {
                "user": user,
                "commenting_user": request.user,
                "request": request,
                "application": application,
                "report_group": report_group,
                "comment": comment,
                "email_title": "AppEnlight :: New comment",
            }
            UserService.send_email(
                request,
                recipients=[user.email],
                variables=email_vars,
                template="/email_templates/new_comment_report.jinja2",
            )
        request.session.flash(_("Your comment was created"))
        return comment.get_dict()
    else:
        return form.errors


@view_config(
    route_name="report_groups_property",
    match_param="key=assigned_users",
    renderer="json",
    permission="update_reports",
    request_method="GET",
)
def assigned_users(request):
    """
    Returns list of users a specific report group is assigned for review
    """
    report_group = request.context.report_group
    application = request.context.resource
    users = set([p.user for p in ResourceService.users_for_perm(application, "view")])
    currently_assigned = [u.user_name for u in report_group.assigned_users]
    user_status = {"assigned": [], "unassigned": []}
    # handle users
    for user in users:
        user_dict = {
            "user_name": user.user_name,
            "gravatar_url": UserService.gravatar_url(user),
            "name": "%s %s" % (user.first_name, user.last_name),
        }
        if user.user_name in currently_assigned:
            user_status["assigned"].append(user_dict)
        elif user_dict not in user_status["unassigned"]:
            user_status["unassigned"].append(user_dict)
    return user_status


@view_config(
    route_name="report_groups_property",
    match_param="key=assigned_users",
    renderer="json",
    permission="update_reports",
    request_method="PATCH",
)
def assign_users(request):
    """
    Assigns specific report group to user for review - send email notification
    """
    report_group = request.context.report_group
    application = request.context.resource
    currently_assigned = [u.user_name for u in report_group.assigned_users]
    new_assigns = request.unsafe_json_body

    # first unassign old users
    for user_name in new_assigns["unassigned"]:
        if user_name in currently_assigned:
            user = UserService.by_user_name(user_name)
            report_group.assigned_users.remove(user)
            comment = ReportComment(
                owner_id=request.user.id, report_time=report_group.first_timestamp
            )
            comment.body = "Unassigned group from @%s" % user_name
            report_group.comments.append(comment)

    # assign new users
    for user_name in new_assigns["assigned"]:
        if user_name not in currently_assigned:
            user = UserService.by_user_name(user_name)
            if user in report_group.assigned_users:
                report_group.assigned_users.remove(user)
            DBSession.flush()
            assignment = ReportAssignment(
                owner_id=user.id,
                report_time=report_group.first_timestamp,
                group_id=report_group.id,
            )
            DBSession.add(assignment)

            comment = ReportComment(
                owner_id=request.user.id, report_time=report_group.first_timestamp
            )
            comment.body = "Assigned report_group to @%s" % user_name
            report_group.comments.append(comment)

            email_vars = {
                "user": user,
                "request": request,
                "application": application,
                "report_group": report_group,
                "email_title": "AppEnlight :: Assigned Report",
            }
            UserService.send_email(
                request,
                recipients=[user.email],
                variables=email_vars,
                template="/email_templates/assigned_report.jinja2",
            )

    return True


@view_config(
    route_name="report_groups_property",
    match_param="key=history",
    renderer="json",
    permission="view",
)
def history(request):
    """ Separate error graph or similar graph"""
    report_group = request.context.report_group
    query_params = request.GET.mixed()
    query_params["resource"] = (report_group.resource_id,)

    filter_settings = build_filter_settings_from_query_dict(request, query_params)
    if not filter_settings.get("end_date"):
        end_date = datetime.utcnow().replace(microsecond=0, second=0)
        filter_settings["end_date"] = end_date

    if not filter_settings.get("start_date"):
        delta = timedelta(days=30)
        filter_settings["start_date"] = filter_settings["end_date"] - delta

    filter_settings["group_id"] = report_group.id

    result = ReportGroupService.get_report_stats(request, filter_settings)

    plot_data = []
    for row in result:
        point = {
            "x": row["x"],
            "reports": row["report"] + row["slow_report"] + row["not_found"],
        }
        plot_data.append(point)

    return plot_data


@view_config(
    route_name="report_groups",
    renderer="json",
    permission="update_reports",
    request_method="PATCH",
)
def report_groups_PATCH(request):
    """
    Used to update the report group fixed status
    """
    report_group = request.context.report_group
    allowed_keys = ["public", "fixed"]
    for k, v in request.unsafe_json_body.items():
        if k in allowed_keys:
            setattr(report_group, k, v)
        else:
            return HTTPUnprocessableEntity()
    return report_group.get_dict(request)
