# This file is part of the CERN Indico plugins.
# Copyright (C) 2014 - 2021 CERN
#
# The CERN Indico plugins are free software; you can redistribute
# them and/or modify them under the terms of the MIT License; see
# the LICENSE file for more details.

import dateutil.parser
import pytz
from flask import flash, request, session
from flask_pluginengine import render_plugin_template, url_for_plugin

from indico.core import signals
from indico.core.config import config
from indico.core.plugins import IndicoPlugin
from indico.core.settings.converters import ModelListConverter
from indico.modules.events.requests.models.requests import Request, RequestState
from indico.modules.events.requests.views import WPRequestsEventManagement
from indico.modules.rb.models.rooms import Room
from indico.modules.users import User
from indico.util.string import natural_sort_key
from indico.web.forms.base import IndicoForm
from indico.web.forms.fields import EmailListField, IndicoQuerySelectMultipleField, PrincipalListField
from indico.web.menu import TopMenuItem

from indico_room_assistance import _
from indico_room_assistance.blueprint import blueprint
from indico_room_assistance.definition import RoomAssistanceRequest
from indico_room_assistance.util import (can_request_assistance_for_event, event_has_room_with_support_attached,
                                         is_room_assistance_support)


def _order_func(object_list):
    return sorted(object_list, key=lambda r: natural_sort_key(r[1].full_name))


class RoomAssistanceForm(IndicoForm):
    _fieldsets = [
        ('Startup assistance emails', ['room_assistance_recipients', 'rooms_with_assistance',
                                       'room_assistance_support']),
    ]

    room_assistance_recipients = EmailListField(_('Recipients'),
                                                description=_('Notifications about room assistance requests are sent '
                                                              'to these email addresses (one per line)'))
    rooms_with_assistance = IndicoQuerySelectMultipleField('Rooms',
                                                           query_factory=lambda: Room.query,
                                                           description=_('Rooms for which users can request startup '
                                                                         'assistance'),
                                                           get_label='full_name', collection_class=set,
                                                           render_kw={'size': 20}, modify_object_list=_order_func)
    room_assistance_support = PrincipalListField(_('Room assistance support'), allow_groups=True,
                                                 description=_('List of users who can view the list of events with '
                                                               'room startup assistance.'))


class RoomAssistancePlugin(IndicoPlugin):
    """Room assistance request

    This plugin lets users request assistance for meeting rooms.
    """

    configurable = True
    settings_form = RoomAssistanceForm
    settings_converters = {
        'rooms_with_assistance': ModelListConverter(Room)
    }
    acl_settings = {'room_assistance_support'}
    default_settings = {
        'room_assistance_recipients': [],
        'rooms_with_assistance': [],
    }

    def init(self):
        super().init()
        self.inject_bundle('main.css', WPRequestsEventManagement, subclasses=False,
                           condition=lambda: request.view_args.get('type') == RoomAssistanceRequest.name)
        self.template_hook('event-actions', self._room_assistance_action)
        self.connect(signals.menu.items, self._extend_services_menu, sender='top-menu')
        self.connect(signals.plugin.get_event_request_definitions, self._get_room_assistance_request)
        self.connect(signals.event.updated, self._on_event_update)

    def get_blueprints(self):
        return blueprint

    def _room_assistance_action(self, event, **kwargs):
        return render_plugin_template('room_assistance_action.html', event=event,
                                      can_request_assistance=can_request_assistance_for_event(event))

    def _extend_services_menu(self, reservation, **kwargs):
        if not session.user or not is_room_assistance_support(session.user):
            return

        return TopMenuItem('services-cern-room-assistance', _('Room assistance'),
                           url_for_plugin('room_assistance.request_list'), section='services')

    def _get_room_assistance_request(self, sender, **kwargs):
        return RoomAssistanceRequest

    def _on_event_update(self, event, **kwargs):
        changes = kwargs['changes']
        if not changes.keys() & {'location_data', 'start_dt', 'end_dt'}:
            return

        request = Request.find_latest_for_event(event, RoomAssistanceRequest.name)
        if not request or request.state != RequestState.accepted:
            return

        if 'location_data' in changes and not event_has_room_with_support_attached(event):
            request.definition.reject(request, {'comment': render_plugin_template('auto_reject_no_supported_room.txt')},
                                      User.get_system_user())
            request.data = dict(request.data, occurrences=[])
            flash(_("The new event location is not in the list of the rooms supported by the room assistance team. "
                    "Room assistance request has been rejected and support will not be provided."), 'warning')
        if changes.keys() & {'start_dt', 'end_dt'}:
            tz = pytz.timezone(config.DEFAULT_TIMEZONE)
            occurrences = {dateutil.parser.parse(occ).astimezone(tz) for occ in request.data['occurrences']}
            req_dates = {occ.date() for occ in occurrences}
            event_dates = set(event.iter_days())
            old_dates = req_dates - event_dates
            has_overlapping_dates = req_dates & event_dates

            if not has_overlapping_dates:
                request.definition.reject(request,
                                          {'comment': render_plugin_template('auto_reject_no_overlapping_dates.txt')},
                                          User.get_system_user())
                request.data = dict(request.data, occurrences=[])
                flash(_("The new event dates don't overlap with the existing room assistance request for this event. "
                        "Room assistance request has been rejected and support will not be provided."), 'warning')
            elif old_dates and has_overlapping_dates:
                new_data = dict(request.data)
                new_data['occurrences'] = [occ.astimezone(pytz.utc).isoformat() for occ in occurrences
                                           if occ.date() in req_dates & event_dates]
                request.data = new_data
                flash(_("Room assistance had been requested for days that are not between the updated start/end "
                        "dates. Support will not be provided on these days anymore."), 'warning')

[((43, 48, 43, 63), 'indico_room_assistance._', '_', ({(43, 50, 43, 62): '"""Recipients"""'}, {}), "('Recipients')", False, 'from indico_room_assistance import _\n'), ((52, 49, 52, 77), 'indico_room_assistance._', '_', ({(52, 51, 52, 76): '"""Room assistance support"""'}, {}), "('Room assistance support')", False, 'from indico_room_assistance import _\n'), ((66, 33, 66, 57), 'indico.core.settings.converters.ModelListConverter', 'ModelListConverter', ({(66, 52, 66, 56): 'Room'}, {}), '(Room)', False, 'from indico.core.settings.converters import ModelListConverter\n'), ((105, 18, 105, 82), 'indico.modules.events.requests.models.requests.Request.find_latest_for_event', 'Request.find_latest_for_event', ({(105, 48, 105, 53): 'event', (105, 55, 105, 81): 'RoomAssistanceRequest.name'}, {}), '(event, RoomAssistanceRequest.name)', False, 'from indico.modules.events.requests.models.requests import Request, RequestState\n'), ((44, 60, 45, 104), 'indico_room_assistance._', '_', ({(44, 62, 45, 103): '"""Notifications about room assistance requests are sent to these email addresses (one per line)"""'}, {}), "('Notifications about room assistance requests are sent to these email addresses (one per line)'\n    )", False, 'from indico_room_assistance import _\n'), ((48, 71, 49, 86), 'indico_room_assistance._', '_', ({(48, 73, 49, 85): '"""Rooms for which users can request startup assistance"""'}, {}), "('Rooms for which users can request startup assistance')", False, 'from indico_room_assistance import _\n'), ((53, 61, 54, 90), 'indico_room_assistance._', '_', ({(53, 63, 54, 89): '"""List of users who can view the list of events with room startup assistance."""'}, {}), "('List of users who can view the list of events with room startup assistance.'\n    )", False, 'from indico_room_assistance import _\n'), ((94, 60, 94, 80), 'indico_room_assistance._', '_', ({(94, 62, 94, 79): '"""Room assistance"""'}, {}), "('Room assistance')", False, 'from indico_room_assistance import _\n'), ((95, 27, 95, 73), 'flask_pluginengine.url_for_plugin', 'url_for_plugin', ({(95, 42, 95, 72): '"""room_assistance.request_list"""'}, {}), "('room_assistance.request_list')", False, 'from flask_pluginengine import render_plugin_template, url_for_plugin\n'), ((116, 17, 116, 55), 'pytz.timezone', 'pytz.timezone', ({(116, 31, 116, 54): 'config.DEFAULT_TIMEZONE'}, {}), '(config.DEFAULT_TIMEZONE)', False, 'import pytz\n'), ((34, 45, 34, 77), 'indico.util.string.natural_sort_key', 'natural_sort_key', ({(34, 62, 34, 76): 'r[1].full_name'}, {}), '(r[1].full_name)', False, 'from indico.util.string import natural_sort_key\n'), ((88, 61, 88, 100), 'indico_room_assistance.util.can_request_assistance_for_event', 'can_request_assistance_for_event', ({(88, 94, 88, 99): 'event'}, {}), '(event)', False, 'from indico_room_assistance.util import can_request_assistance_for_event, event_has_room_with_support_attached, is_room_assistance_support\n'), ((91, 35, 91, 75), 'indico_room_assistance.util.is_room_assistance_support', 'is_room_assistance_support', ({(91, 62, 91, 74): 'session.user'}, {}), '(session.user)', False, 'from indico_room_assistance.util import can_request_assistance_for_event, event_has_room_with_support_attached, is_room_assistance_support\n'), ((109, 46, 109, 89), 'indico_room_assistance.util.event_has_room_with_support_attached', 'event_has_room_with_support_attached', ({(109, 83, 109, 88): 'event'}, {}), '(event)', False, 'from indico_room_assistance.util import can_request_assistance_for_event, event_has_room_with_support_attached, is_room_assistance_support\n'), ((111, 38, 111, 60), 'indico.modules.users.User.get_system_user', 'User.get_system_user', ({}, {}), '()', False, 'from indico.modules.users import User\n'), ((113, 18, 114, 98), 'indico_room_assistance._', '_', ({(113, 20, 114, 97): '"""The new event location is not in the list of the rooms supported by the room assistance team. Room assistance request has been rejected and support will not be provided."""'}, {}), "('The new event location is not in the list of the rooms supported by the room assistance team. Room assistance request has been rejected and support will not be provided.'\n    )", False, 'from indico_room_assistance import _\n'), ((110, 59, 110, 118), 'flask_pluginengine.render_plugin_template', 'render_plugin_template', ({(110, 82, 110, 117): '"""auto_reject_no_supported_room.txt"""'}, {}), "('auto_reject_no_supported_room.txt')", False, 'from flask_pluginengine import render_plugin_template, url_for_plugin\n'), ((126, 42, 126, 64), 'indico.modules.users.User.get_system_user', 'User.get_system_user', ({}, {}), '()', False, 'from indico.modules.users import User\n'), ((128, 22, 129, 102), 'indico_room_assistance._', '_', ({(128, 24, 129, 101): '"""The new event dates don\'t overlap with the existing room assistance request for this event. Room assistance request has been rejected and support will not be provided."""'}, {}), '("The new event dates don\'t overlap with the existing room assistance request for this event. Room assistance request has been rejected and support will not be provided."\n    )', False, 'from indico_room_assistance import _\n'), ((125, 54, 125, 116), 'flask_pluginengine.render_plugin_template', 'render_plugin_template', ({(125, 77, 125, 115): '"""auto_reject_no_overlapping_dates.txt"""'}, {}), "('auto_reject_no_overlapping_dates.txt')", False, 'from flask_pluginengine import render_plugin_template, url_for_plugin\n'), ((135, 22, 136, 85), 'indico_room_assistance._', '_', ({(135, 24, 136, 84): '"""Room assistance had been requested for days that are not between the updated start/end dates. Support will not be provided on these days anymore."""'}, {}), "('Room assistance had been requested for days that are not between the updated start/end dates. Support will not be provided on these days anymore.'\n    )", False, 'from indico_room_assistance import _\n')]