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

# Copyright (C) 2018 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 copy
import unittest
import inspect

from hamcrest import assert_that, equal_to, is_
from mock import Mock, patch

from collections import namedtuple
from xivo_dao.alchemy.userfeatures import UserFeatures
from xivo_dao.alchemy.agentfeatures import AgentFeatures
from xivo_dao.helpers.exception import InputError

from xivo_confd.plugins.device.update import DeviceUpdater
from xivo_confd.plugins.user.service import UserService

UserfeaturesAndAgentnumber = namedtuple('UserfeaturesAndAgentnumber', ['UserFeatures', 'agent_number', 'agent_group_id'])

class TestUserService(unittest.TestCase):

    def setUp(self):
        self.user_dao = Mock()
        self.validator = Mock()
        self.notifier = Mock()
        self.device_updater = Mock(DeviceUpdater)

        self.service = UserService(self.user_dao,
                                    self.validator,
                                    self.notifier,
                                    self.device_updater)

        self.uf = UserFeatures(firstname='foo')

    @patch('xivo_dao.user_dao.get_user_with_agent_number')
    def test_get_returns_user_with_agentnumber(self, mock_get_user_wagent):
        ufa = UserfeaturesAndAgentnumber(self.uf, '8000', 123)
        mock_get_user_wagent.return_value = ufa

        res = self.service.get_with_agent(1)

        assert_that(res, equal_to(ufa))

    @patch('xivo_dao.user_dao.get_user_with_agent_number')
    def test_get_returns_user_with_no_agentnumber(self, mock_get_user_wagent):
        ufa = UserfeaturesAndAgentnumber(self.uf, None, None)
        mock_get_user_wagent.return_value = ufa

        res = self.service.get_with_agent(1)

        assert_that(res, equal_to(ufa))

    @patch('xivo_dao.resources.func_key.persistor.DestinationPersistor.create_agent_destination')
    @patch('xivo_dao.helpers.db_manager.Session.add')
    def test_create_user_with_agent(self, mock_session_add, mockDestinationPersistor):
        def se_add_agent(a):
            if type(a) is AgentFeatures:
                a.id = 456

        def se_create_user(u):
            v = copy.copy(u)
            v.id=123
            return v

        agent = AgentFeatures(firstname='foo',
                              lastname='bar',
                              numgroup=12,
                              number="2001",
                              passwd='',
                              context='default',
                              language='',
                              description='',
                              id=456)
        ufnoid = UserFeatures(firstname='foo', lastname='bar')
        ufid = se_create_user(ufnoid)
        ufid.agentid = 456
        
        self.user_dao.create.side_effect = se_create_user
        mock_session_add.side_effect = se_add_agent

        res = self.service.create_with_agent(ufnoid, '2001', 12)

        assert mock_session_add.call_count == 1
        args, _ = mock_session_add.call_args
        assert_that(args[0].todict(), is_(agent.todict()))

        assert mockDestinationPersistor.called
        dest_args, _ = mockDestinationPersistor.call_args
        assert_that(dest_args[0].todict(), is_(agent.todict()))

        self.notifier.created_agent.assert_called()
        (agent_arg, user_arg) = self.notifier.created_agent.call_args[0]
        assert_that(agent_arg.todict(), is_(agent.todict()))
        assert_that(user_arg.todict(), is_(ufid.todict()))
        
        assert self.user_dao.create.called
        assert res.agentid == 456


    @patch('xivo_dao.resources.func_key.persistor.DestinationPersistor.create_agent_destination')
    @patch('xivo_dao.helpers.db_manager.Session.add')
    def test_create_user_with_agent_number_and_agentid_should_fail(self, mock_session_add, mockDestinationPersistor):
        def se_add_agent(a):
            if type(a) is AgentFeatures:
                a.id = 456

        def se_create_user(u):
            v = copy.copy(u)
            v.id=123
            return v
        
        uf_wagentid = UserFeatures(firstname='foo', lastname='bar', agentid=456)
        
        self.user_dao.create.side_effect = se_create_user
        mock_session_add.side_effect = se_add_agent

        with self.assertRaises(InputError):
            self.service.create_with_agent(uf_wagentid, '2001', 12)
        
    
    @patch('xivo_dao.helpers.db_manager.Session.add')
    def test_create_user_without_agent(self, mock_session_add):
        def se_create_user(u):
            v = copy.copy(u)
            v.id=123
            return v

        ufnoid = UserFeatures(firstname='foo', lastname='bar')
        self.user_dao.create.side_effect = se_create_user

        res = self.service.create_with_agent(ufnoid, None, None)

        assert mock_session_add.call_count == 0
        assert self.user_dao.create.called
        assert res.agentid is None

    @patch('xivo_dao.resources.func_key.persistor.DestinationPersistor.create_agent_destination')
    @patch('xivo_dao.user_dao.get_user_with_agent_number')
    @patch('xivo_dao.helpers.db_manager.Session.add')
    def test_edit_user_add_agent(self, mock_session_add, mock_get_user_wagent, mockDestinationPersistor):
        def se_add_agent(a):
            if type(a) is AgentFeatures:
                a.id = 456

        agent = AgentFeatures(firstname='foo',
                              lastname='bar',
                              numgroup=1,
                              number="2001",
                              passwd='',
                              context='default',
                              language='',
                              description='',
                              id=456)
        user = UserFeatures(firstname='foo', lastname='bar')
        mock_session_add.side_effect = se_add_agent
        ufa = UserfeaturesAndAgentnumber(user, None, None)
        mock_get_user_wagent.return_value = ufa

        self.service.edit_with_agent(user, '2001', None)

        mock_session_add.assert_called_once()
        args, _ = mock_session_add.call_args
        assert_that(args[0].todict(), is_(agent.todict()))

        mockDestinationPersistor.assert_called()
        dest_args, _ = mockDestinationPersistor.call_args
        assert_that(dest_args[0].todict(), is_(agent.todict()))

        self.notifier.created_agent.assert_called()
        (agent_arg, user_arg) = self.notifier.created_agent.call_args[0]
        assert_that(agent_arg.todict(), is_(agent.todict()))
        assert_that(user_arg.todict(), is_(user.todict()))

    @patch('xivo_dao.helpers.db_manager.Session.query')
    @patch('xivo_dao.user_dao.get_user_with_agent_number')
    @patch('xivo_dao.helpers.db_manager.Session.add')
    def test_edit_user_add_agent_and_group(self, mock_session_add, mock_get_user_wagent, mock_session_query):
        def se_add_agent(a):
            if type(a) is AgentFeatures:
                a.id = 456

        agent = AgentFeatures(firstname='foo',
                              lastname='bar',
                              numgroup=12,
                              number="2001",
                              passwd='',
                              context='default',
                              language='',
                              description='',
                              id=456)
        user = UserFeatures(firstname='foo', lastname='bar')
        mock_session_add.side_effect = se_add_agent
        ufa = UserfeaturesAndAgentnumber(user, None, None)
        mock_get_user_wagent.return_value = ufa
        
        self.service.edit_with_agent(user, '2001', 12)

        assert mock_session_add.call_count == 1
        args, _ = mock_session_add.call_args
        assert_that(args[0].todict(), is_(agent.todict()))

        self.notifier.created_agent.assert_called()
        notify_args = self.notifier.created_agent.call_args[0][0]
        assert_that(notify_args.todict(), is_(agent.todict()))

    @patch('xivo_dao.user_dao.get_user_with_agent_number')
    def test_edit_user_update_agent(self, mock_get_user_wagent):
        user = UserFeatures(firstname='foo', lastname='bar', agentid=456)
        ufa = UserfeaturesAndAgentnumber(user, '2000', 1)
        mock_get_user_wagent.return_value = ufa

        with self.assertRaises(InputError):
            self.service.edit_with_agent(user, '2001', None)

        # Check no exception is thrown when agent_number is not changed
        self.service.edit_with_agent(user, '2000', None)



    @patch('xivo_dao.helpers.db_manager.Session.query')
    @patch('xivo_dao.agent_dao.get')
    @patch('xivo_dao.user_dao.get_user_with_agent_number')
    def test_edit_user_update_agent_change_group(self, mock_get_user_wagent, mock_get_agent, mock_session_query):
        user = UserFeatures(firstname='foo', lastname='bar', agentid=456)
        ufa = UserfeaturesAndAgentnumber(user, '2000', 1)
        agent = AgentFeatures(firstname='foot', lastname='bar', id=456)
        mock_get_user_wagent.return_value = ufa
        mock_get_agent.return_value = agent

        self.service.edit_with_agent(user, '2000', 2)

        self.notifier.edited_agent.assert_called()
        notify_args = self.notifier.edited_agent.call_args[0][0]
        assert_that(notify_args.todict(), is_(agent.todict()))

        
    @patch('xivo_dao.agent_dao.get')
    @patch('xivo_dao.user_dao.get_user_with_agent_number')
    @patch('xivo_dao.helpers.db_manager.Session.add')
    def test_edit_user_remove_agent(self, mock_session_add, mock_get_user_wagent, mock_get_agent):
        user = UserFeatures(firstname='foo', lastname='bar', agentid=456)
        ufa = UserfeaturesAndAgentnumber(user, "2000", 1)
        agent = AgentFeatures(firstname='foot', lastname='bar', id=456)
        mock_get_user_wagent.return_value = ufa
        mock_get_agent.return_value = agent

        self.service.edit_with_agent(user, None, None)

        assert mock_session_add.call_count == 0
        assert user.agentid is None

        self.notifier.edited_agent.assert_called()
        notify_args = self.notifier.edited_agent.call_args[0][0]
        assert_that(notify_args.todict(), is_(agent.todict()))
