# -*- 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, see <http://www.gnu.org/licenses/>

import unittest
from mock import Mock, patch, call
from xivo_agid.modules import incoming_meetme_set_features
from xivo_agid.objects import MeetMe
from xivo_agid.tests.test_objects import MeetMeBuilder, AgiMock


class TestAuthentication(unittest.TestCase):

    def setUp(self):
        self.agi = AgiMock()

    def test_meetme_with_no_pin(self):
        meetme = (MeetMeBuilder()
                    .build())

        (auth, role, pin) = incoming_meetme_set_features.meetme_authenticate(self.agi,
                                                                             meetme)

        self.assertEqual((auth, role, pin), (True, 'USER', None))

    def test_meetme_wrong_pin(self):
        meetme = (MeetMeBuilder()
                    .withUserPin('1234')
                    .build())

        pin_entered = '0000'
        self.agi.set_variable('PIN', pin_entered)

        (auth, role, pin) = incoming_meetme_set_features.meetme_authenticate(self.agi,
                                                                             meetme)

        self.assertEqual((auth, role, pin), (False, None, pin_entered))

    def test_meetme_with_userpin(self):
        meetme = (MeetMeBuilder()
                    .withUserPin('1234')
                    .build())

        pin_entered = '1234'
        self.agi.set_variable('PIN', pin_entered)

        (auth, role, pin) = incoming_meetme_set_features.meetme_authenticate(self.agi,
                                                                             meetme)

        self.assertEqual((auth, role, pin), (True, 'USER', pin_entered))

    def test_meetme_with_adminpin(self):
        meetme = (MeetMeBuilder()
                    .withUserPin('1234')
                    .withAdminPin('4321')
                    .build())

        pin_entered = '4321'
        self.agi.set_variable('PIN', pin_entered)

        (auth, role, pin) = incoming_meetme_set_features.meetme_authenticate(self.agi,
                                                                             meetme)

        self.assertEqual((auth, role, pin), (True, 'ADMIN', pin_entered))

    def test_meetme_with_samepin(self):
        '''
        Having same pin for user and admin should not happen. But in case, we
        check that we then give everyone USER role
        '''
        meetme = (MeetMeBuilder()
                    .withUserPin('1234')
                    .withAdminPin('1234')
                    .build())

        pin_entered = '1234'
        self.agi.set_variable('PIN', pin_entered)

        (auth, role, pin) = incoming_meetme_set_features.meetme_authenticate(self.agi,
                                                                             meetme)

        self.assertEqual((auth, role, pin), (True, 'USER', pin_entered))

class TestAuthenticationFromMobile(unittest.TestCase):

    def setUp(self):
        self.agi = AgiMock()
        self.agi.set_variable("XUC_CALLSOURCE", "MOBILE_APPLICATION")
        self.user_pin = '1234'
        self.admin_pin = '4321'

    def test_meetme_with_userpin(self):
        meetme = (MeetMeBuilder()
                    .withUserPin(self.user_pin)
                    .build())
    
        (auth, role, pin) = incoming_meetme_set_features.meetme_authenticate(self.agi,
                                                                             meetme)

        self.assertEqual((auth, role, pin), (True, 'USER', self.user_pin))

    def test_meetme_with_adminpin(self):
        meetme = (MeetMeBuilder()
                    .withUserPin(self.user_pin)
                    .withAdminPin(self.admin_pin)
                    .build())
        
        (auth, role, pin) = incoming_meetme_set_features.meetme_authenticate(self.agi,
                                                                             meetme)

        self.assertEqual((auth, role, pin), (True, 'ADMIN', self.admin_pin))


class TestMaxNumber(unittest.TestCase):

    def setUp(self):
        self.agi = AgiMock()

        self.meetme = (MeetMeBuilder()
                        .withConfNo('8000')
                        .withMaxUsers(8)
                        .build())

    def test_above_max_number_as_user(self):
        users_count = '8'
        tested_role = 'USER'

        self.agi.set_variable('MEETMECOUNT', users_count)

        res = incoming_meetme_set_features.meetme_exceed_max_number(self.agi,
                                                                    self.meetme,
                                                                    tested_role)
        self.assertTrue(res)

    def test_below_max_number_as_user(self):
        users_count = '7'
        tested_role = 'USER'

        self.agi.set_variable('MEETMECOUNT', users_count)

        res = incoming_meetme_set_features.meetme_exceed_max_number(self.agi,
                                                                    self.meetme,
                                                                    tested_role)
        self.assertFalse(res)

    def test_above_max_number_as_admin(self):
        '''
        If caller's role is ADMIN, it should go in conf room even if maxusers
        is reached
        '''
        users_count = '8'
        tested_role = 'ADMIN'

        self.agi.set_variable('MEETMECOUNT', users_count)

        res = incoming_meetme_set_features.meetme_exceed_max_number(self.agi,
                                                                    self.meetme,
                                                                    tested_role)
        self.assertFalse(res)


class TestSetOptions(unittest.TestCase):

    def setUp(self):
        self.agi = Mock()

    def test_waitingroom_is_not_set(self):
        meetme = (MeetMeBuilder()
                    .withAdminPin('2000')
                    .build())

        tested_role = 'WHATEVER'
        res = incoming_meetme_set_features.meetme_set_options(self.agi,
                                                              meetme,
                                                              tested_role)
        self.assertNotIn('w', res)

    def test_waitingroom_is_set_for_user(self):
        meetme = (MeetMeBuilder()
                    .withAdminPin('2000')
                    .withWaitingRoom()
                    .build())

        tested_role = 'USER'
        res = incoming_meetme_set_features.meetme_set_options(self.agi,
                                                              meetme,
                                                              tested_role)
        self.assertIn('w', res)

    def test_waitingroom_is_not_set_for_admin(self):
        meetme = (MeetMeBuilder()
                    .withAdminPin('2000')
                    .withWaitingRoom()
                    .build())

        tested_role = 'ADMIN'
        res = incoming_meetme_set_features.meetme_set_options(self.agi,
                                                              meetme,
                                                              tested_role)
        self.assertNotIn('w', res)

    def test_musiconhold_class_is_set(self):
        meetme = MeetMeBuilder().withMusicOnHold('jazz').build()

        tested_role = 'WHATEVER'
        res = incoming_meetme_set_features.meetme_set_options(self.agi,
                                                              meetme,
                                                              tested_role)

        self.assertIn('M(jazz)', res)

    def test_simple_options_are_set(self):
        meetme = (MeetMeBuilder()
                    .withNoPlaysMsgFirstEnter()
                    .withAnnounceUserCount()
                    .withQuiet()
                    .withRecord()
                    .build())

        tested_role = 'WHATEVER'
        expected_options = ['T', '1', 'c', 'q', 'r']

        res = incoming_meetme_set_features.meetme_set_options(self.agi,
                                                              meetme,
                                                              tested_role)
        
        for opt in expected_options:
            self.assertIn(opt, res)
        expected_options_tostring = ''.join(expected_options)
        self.assertEqual(len(res), len(expected_options_tostring))

    def test_announcejoinleave_if_user(self):
        values_and_opts = {'yes': 'i',
                           'noreview': 'I'
                          }

        tested_role = 'USER'

        for val, opt in values_and_opts.iteritems():

            meetme = (MeetMeBuilder()
                        .withAnnounceJoinLeave(val)
                        .build())
        
            res = incoming_meetme_set_features.meetme_set_options(self.agi,
                                                                  meetme,
                                                                  tested_role)

            self.assertIn(opt, res)

    def test_announcejoinleave_if_admin(self):
        values = ['yes', 'noreview']

        tested_role = 'ADMIN'

        for val in values:

            meetme = (MeetMeBuilder()
                        .withAnnounceJoinLeave(val)
                        .build())
        
            res = incoming_meetme_set_features.meetme_set_options(self.agi,
                                                                  meetme,
                                                                  tested_role)

            self.assertNotIn('i', res)
            self.assertNotIn('I', res)

    def test_admin_mode_is_set_if_admin(self):
        meetme = (MeetMeBuilder()
                    .withAdminPin('2000')
                    .build())

        tested_role = 'ADMIN'

        res = incoming_meetme_set_features.meetme_set_options(self.agi,
                                                              meetme,
                                                              tested_role)

        self.assertIn('a', res)

    def test_admin_and_marked_mode_are_set_for_admin_if_waitingroom(self):
        meetme = (MeetMeBuilder()
                    .withAdminPin('2000')
                    .withWaitingRoom()
                    .build())

        tested_role = 'ADMIN'

        expected_options = ['T', 'a', 'A']

        res = incoming_meetme_set_features.meetme_set_options(self.agi,
                                                              meetme,
                                                              tested_role)

        for opt in expected_options:
            self.assertIn(opt, res)
        expected_options_tostring = ''.join(expected_options)
        self.assertEqual(len(res), len(expected_options_tostring))

    def test_admin_or_marked_mode_never_set_for_user(self):
        meetme = (MeetMeBuilder()
                    .withAdminPin('2000')
                    .withUserPin('1000')
                    .withWaitingRoom()
                    .build())

        tested_role = 'USER'

        res = incoming_meetme_set_features.meetme_set_options(self.agi,
                                                              meetme,
                                                              tested_role)

        self.assertNotIn('a', res)
        self.assertNotIn('A', res)

    def _configure_meetme_for_combined_options_tests(self):
        '''
        Called by test_combined_* tests
        '''
        return (MeetMeBuilder()
                  .withUserPin('1000')
                  .withAdminPin('2000')
                  .withWaitingRoom()
                  .withAnnounceUserCount()
                  .withAnnounceJoinLeave('noreview')
                  .withMusicOnHold('samba')
                  .build())

    def test_combined_options_user(self):
        
        meetme = self._configure_meetme_for_combined_options_tests()

        expected_options_user = ['T', 'c', 'I', 'M(samba)', 'w']
        tested_role = 'USER'

        # User role
        res = incoming_meetme_set_features.meetme_set_options(self.agi,
                                                              meetme,
                                                              tested_role)

        for opt in expected_options_user:
            self.assertIn(opt, res)
        expected_options_user_to_string = ''.join(expected_options_user)
        self.assertEqual(len(res), len(expected_options_user_to_string))

    def test_combined_options_admin(self):

        meetme = self._configure_meetme_for_combined_options_tests()

        expected_options_admin = ['T', 'c', 'M(samba)', 'a', 'A']
        tested_role = 'ADMIN'

        # Admin role
        res = incoming_meetme_set_features.meetme_set_options(self.agi,
                                                              meetme,
                                                              tested_role)

        for opt in expected_options_admin:
            self.assertIn(opt, res)
        expected_options_admin_to_string = ''.join(expected_options_admin)
        self.assertEqual(len(res), len(expected_options_admin_to_string))

    def test_talkerdetection_always_set(self):
        meetme = (MeetMeBuilder()
                    .build())

        tested_role = 'WHATEVER'

        res = incoming_meetme_set_features.meetme_set_options(self.agi,
                                                              meetme,
                                                              tested_role)

        self.assertIn('T', res)


class TestSetDialplanVar(unittest.TestCase):

    def setUp(self):
        self.agi = Mock()


    @patch('time.strftime')
    def test_basic_meetme(self, mocked_time):
        
        meetme = (MeetMeBuilder()
                    .withConfNo('8000')
                    .withContext('default')
                    .withName('R&D Conf')
                    .build())
        pin = ''
        role = 'USER'
        options = ''

        expected_calls = [
            call('MEETME_RECORDINGFILE',
                 '/var/lib/asterisk/sounds/meetme/meetme-' + meetme.confno
                 + '-20180228-135653'),
            call('XIVO_REAL_NUMBER', meetme.confno),
            call('XIVO_REAL_CONTEXT', meetme.context),
            call('XIVO_MEETMECONFNO', meetme.confno),
            call('XIVO_MEETMENAME', meetme.name),
            call('XIVO_MEETMENUMBER', meetme.confno),
            call('XIVO_MEETMEPIN', ''),
            call('XIVO_MEETMEOPTIONS', options),
            call('XIVO_MEETMEROLE', 'USER'),
            call('XIVO_MEETMEPREPROCESS_SUBROUTINE', '')
            ]

        mocked_time.return_value = "20180228-135653"
        incoming_meetme_set_features.meetme_set_diaplan_vars(self.agi,
                                                             meetme,
                                                             pin,
                                                             role,
                                                             options)

        
        self.assertEqual(self.agi.set_variable.call_args_list, expected_calls)
    
    def test_meetme_with_musiconhold(self):
        
        meetme = (MeetMeBuilder()
                    .withConfNo('8000')
                    .withContext('default')
                    .withName('R&D Conf')
                    .withMusicOnHold('jazz')
                    .build())
        pin = ''
        role = ''
        options = 'M(jazz)'

        expected_calls = [
            call('CHANNEL(musicclass)', 'jazz'),
            call('XIVO_MEETMEOPTIONS', options),
            ]

        incoming_meetme_set_features.meetme_set_diaplan_vars(self.agi,
                                                             meetme,
                                                             pin,
                                                             role,
                                                             options)

        self.agi.set_variable.assert_has_calls(expected_calls, any_order=True)

    def test_meetme_with_subroutine(self):
        
        meetme = (MeetMeBuilder()
                    .withConfNo('8000')
                    .withContext('default')
                    .withName('R&D Conf')
                    .withSubroutine('my-meetme-subr')
                    .build())
        pin = ''
        role = ''
        options = ''

        expected_calls = [
            call('XIVO_MEETMEPREPROCESS_SUBROUTINE', 'my-meetme-subr'),
            ]

        incoming_meetme_set_features.meetme_set_diaplan_vars(self.agi,
                                                             meetme,
                                                             pin,
                                                             role,
                                                             options)

        self.agi.set_variable.assert_has_calls(expected_calls, any_order=True)

    def test_meetme_with_pin_as_user(self):
        
        meetme = (MeetMeBuilder()
                    .withConfNo('8000')
                    .withContext('default')
                    .withName('R&D Conf')
                    .withUserPin('8000')
                    .withAdminPin('9486')
                    .build())
        
        pin = '8000'
        role = 'USER'
        options = ''

        expected_calls = [
            call('XIVO_MEETMEPIN', '8000'),
            call('XIVO_MEETMEROLE', 'USER')
            ]

        incoming_meetme_set_features.meetme_set_diaplan_vars(self.agi,
                                                            meetme,
                                                            pin,
                                                            role,
                                                            options)

        self.agi.set_variable.assert_has_calls(expected_calls, any_order=True)

    def test_meetme_with_pin_as_admin(self):
        
        meetme = (MeetMeBuilder()
                    .withConfNo('8000')
                    .withContext('default')
                    .withName('R&D Conf')
                    .withUserPin('8000')
                    .withAdminPin('9486')
                    .build())
        
        pin = '9486'
        role = 'ADMIN'
        options = ''

        expected_calls = [
            call('XIVO_MEETMEPIN', '9486'),
            call('XIVO_MEETMEROLE', 'ADMIN')
            ]

        incoming_meetme_set_features.meetme_set_diaplan_vars(self.agi,
                                                            meetme,
                                                            pin,
                                                            role,
                                                            options)

        self.agi.set_variable.assert_has_calls(expected_calls, any_order=True)

