# -*- coding: UTF-8 -*-

# Copyright (C) 2015-2016 Avencall
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

import unittest

from mock import Mock, call
from xivo_bus.resources.agent.event import (CreateAgentEvent,
                                            EditAgentEvent)
from xivo_bus.resources.ipbx.event import ReloadIpbxEvent
from xivo_bus.resources.user.event import (CreateUserEvent,
                                           EditUserEvent,
                                           DeleteUserEvent,
                                           EditUserServiceEvent,
                                           EditUserForwardEvent)
from xivo_dao.alchemy.agentfeatures import AgentFeatures
from xivo_dao.alchemy.userfeatures import UserFeatures as User

from xivo_confd.helpers.sysconfd_publisher import SysconfdPublisher
from xivo_confd.plugins.user.notifier import UserNotifier, UserServiceNotifier, UserForwardNotifier
from xivo_confd.plugins.user.resource_sub import (ServiceDNDSchema,
                                                  ServiceIncallFilterSchema,
                                                  ForwardBusySchema,
                                                  ForwardNoAnswerSchema,
                                                  ForwardUnconditionalSchema,
                                                  ForwardsSchema)


def sysconfd_handler(action, user_id):
    cti = 'xivo[user,{},{}]'.format(action, user_id)
    return {'ctibus': [cti],
            'agentbus': []}


class TestUserNotifier(unittest.TestCase):

    def setUp(self):
        self.sysconfd = Mock(SysconfdPublisher)
        self.bus = Mock()
        self.dao = Mock()
        self.user = Mock(User, id=1234)
        self.agent = Mock(AgentFeatures, id=421)
        self.reload_commands = ['dialplan reload',
                                'module reload chan_sccp.so',
                                'module reload app_queue.so',
                                'sip reload']
        self.expected_reload_event = ReloadIpbxEvent(self.reload_commands)

        self.notifier = UserNotifier(self.sysconfd, self.bus, self.dao)

    def reload_and_resource_call(self, resource_event):
        return [call(self.expected_reload_event, self.expected_reload_event.routing_key),
                call(resource_event, resource_event.routing_key)]

    def test_when_user_created_then_sysconfd_notified(self):
        self.notifier.created(self.user)

        handler = sysconfd_handler('add', self.user.id)
        self.sysconfd.exec_request_handlers.assert_called_once_with(handler)

    def test_when_user_created_and_live_reload_enabled_then_events_sent_on_bus(self):
        self.dao.is_live_reload_enabled.return_value = True
        expected_resource_event = CreateUserEvent(self.user.id, self.user.uuid)
        expected_calls = self.reload_and_resource_call(expected_resource_event)

        self.notifier.created(self.user)

        self.bus.send_bus_event.assert_has_calls(expected_calls)

    def test_when_user_created_and_live_reload_disabled_then_events_sent_on_bus(self):
        self.dao.is_live_reload_enabled.return_value = False
        expected_resource_event = CreateUserEvent(self.user.id, self.user.uuid)

        self.notifier.created(self.user)

        self.bus.send_bus_event.assert_called_once_with(expected_resource_event, expected_resource_event.routing_key)

    def test_when_user_edited_then_sysconfd_notified(self):
        self.notifier.edited(self.user)

        handler = sysconfd_handler('edit', self.user.id)
        self.sysconfd.exec_request_handlers.assert_called_once_with(handler)

    def test_when_user_edited_and_live_reload_enabled_then_events_sent_on_bus(self):
        self.dao.is_live_reload_enabled.return_value = True
        expected_resource_event = EditUserEvent(self.user.id, self.user.uuid)
        expected_calls = self.reload_and_resource_call(expected_resource_event)

        self.notifier.edited(self.user)

        self.bus.send_bus_event.assert_has_calls(expected_calls)

    def test_when_user_edited_and_live_reload_disabled_then_events_sent_on_bus(self):
        self.dao.is_live_reload_enabled.return_value = False
        expected_resource_event = EditUserEvent(self.user.id, self.user.uuid)

        self.notifier.edited(self.user)

        self.bus.send_bus_event.assert_called_once_with(expected_resource_event, expected_resource_event.routing_key)

    def test_when_user_deleted_then_sysconfd_notified(self):
        self.notifier.deleted(self.user)

        handler = sysconfd_handler('delete', self.user.id)
        self.sysconfd.exec_request_handlers.assert_called_once_with(handler)

    def test_when_user_deleted_and_live_reload_enabled_then_events_sent_on_bus(self):
        self.dao.is_live_reload_enabled.return_value = True
        expected_resource_event = DeleteUserEvent(self.user.id, self.user.uuid)
        expected_calls = self.reload_and_resource_call(expected_resource_event)

        self.notifier.deleted(self.user)

        self.bus.send_bus_event.assert_has_calls(expected_calls)

    def test_when_user_deleted_and_live_reload_disabled_then_events_sent_on_bus(self):
        self.dao.is_live_reload_enabled.return_value = False
        expected_resource_event = DeleteUserEvent(self.user.id, self.user.uuid)

        self.notifier.deleted(self.user)

        self.bus.send_bus_event.assert_called_once_with(expected_resource_event, expected_resource_event.routing_key)

    def test_when_agent_created(self):
        agent_handler = {'ctibus': ['xivo[agent,add,421]',
                                    'xivo[queuemember,update]',
                                    'xivo[user,edit,1234]'],
                         'agentbus': ['agent.add.421']}
        expected_event = CreateAgentEvent(self.agent.id)

        self.notifier.created_agent(self.agent, self.user)

        self.sysconfd.exec_request_handlers.assert_called_once_with(agent_handler)
        self.bus.send_bus_event.assert_called_once_with(expected_event,
                                                        expected_event.routing_key)

    def test_when_agent_edited(self):
        agent_handler = {'ctibus': ['xivo[agent,edit,421]',
                                    'xivo[queuemember,update]'],
                         'agentbus': ['agent.edit.421']}
        expected_event = EditAgentEvent(self.agent.id)

        self.notifier.edited_agent(self.agent)

        self.sysconfd.exec_request_handlers.assert_called_once_with(agent_handler)
        self.bus.send_bus_event.assert_called_once_with(expected_event,
                                                        expected_event.routing_key)


class TestUserServiceNotifier(unittest.TestCase):

    def setUp(self):
        self.bus = Mock()
        self.user = Mock(User, uuid='1234-abcd', dnd_enabled=True, incallfilter_enabled=True)

        self.notifier = UserServiceNotifier(self.bus)

    def test_when_user_service_dnd_edited_then_event_sent_on_bus(self):
        schema = ServiceDNDSchema()
        expected_event = EditUserServiceEvent(self.user.uuid,
                                              schema.types[0],
                                              self.user.dnd_enabled)

        self.notifier.edited(self.user, schema)

        self.bus.send_bus_event.assert_called_once_with(expected_event,
                                                        expected_event.routing_key)

    def test_when_user_service_incallfilter_edited_then_event_sent_on_bus(self):
        schema = ServiceIncallFilterSchema()
        expected_event = EditUserServiceEvent(self.user.uuid,
                                              schema.types[0],
                                              self.user.incallfilter_enabled)

        self.notifier.edited(self.user, schema)

        self.bus.send_bus_event.assert_called_once_with(expected_event,
                                                        expected_event.routing_key)


class TestUserForwardNotifier(unittest.TestCase):

    def setUp(self):
        self.bus = Mock()
        self.user = Mock(User, uuid='1234-abcd',
                         busy_enabled=True, busy_destination='123',
                         noanswer_enabled=False, noanswer_destination='456',
                         unconditional_enabled=True, unconditional_destination='789')

        self.notifier = UserForwardNotifier(self.bus)

    def test_when_user_forward_busy_edited_then_event_sent_on_bus(self):
        schema = ForwardBusySchema()
        expected_event = EditUserForwardEvent(self.user.uuid,
                                              'busy',
                                              self.user.busy_enabled,
                                              self.user.busy_destination)

        self.notifier.edited(self.user, schema)

        self.bus.send_bus_event.assert_called_once_with(expected_event,
                                                        expected_event.routing_key)

    def test_when_user_forward_noanswer_edited_then_event_sent_on_bus(self):
        schema = ForwardNoAnswerSchema()
        expected_event = EditUserForwardEvent(self.user.uuid,
                                              'noanswer',
                                              self.user.noanswer_enabled,
                                              self.user.noanswer_destination)

        self.notifier.edited(self.user, schema)

        self.bus.send_bus_event.assert_called_once_with(expected_event,
                                                        expected_event.routing_key)

    def test_when_user_forward_unconditional_edited_then_event_sent_on_bus(self):
        schema = ForwardUnconditionalSchema()
        expected_event = EditUserForwardEvent(self.user.uuid,
                                              'unconditional',
                                              self.user.unconditional_enabled,
                                              self.user.unconditional_destination)

        self.notifier.edited(self.user, schema)

        self.bus.send_bus_event.assert_called_once_with(expected_event,
                                                        expected_event.routing_key)

    def test_when_user_forwards_edited_then_event_sent_on_bus(self):
        schema = ForwardsSchema()
        self.notifier.edited(self.user, schema)

        expected_busy_event = EditUserForwardEvent(self.user.uuid,
                                                   'busy',
                                                   self.user.busy_enabled,
                                                   self.user.busy_destination)

        expected_noanswer_event = EditUserForwardEvent(self.user.uuid,
                                                       'noanswer',
                                                       self.user.noanswer_enabled,
                                                       self.user.noanswer_destination)
        expected_unconditional_event = EditUserForwardEvent(self.user.uuid,
                                                            'unconditional',
                                                            self.user.unconditional_enabled,
                                                            self.user.unconditional_destination)
        expected_calls = [call(expected_busy_event, expected_busy_event.routing_key),
                          call(expected_noanswer_event, expected_noanswer_event.routing_key),
                          call(expected_unconditional_event, expected_unconditional_event.routing_key)]
        self.bus.send_bus_event.assert_has_calls(expected_calls)
