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

# Copyright (C) 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, patch, call

from xivo_agid.modules import mobile_app_wake_up

class TestWakeUp(unittest.TestCase):

    def setUp(self):
        self.client = Mock()
        self.XIVO_TMP_CONTACTS = "contact1,contact2,contact3"
        self.XIVO_MOBILE_PEER_EXPIRE_BEFORE_PUSH_LOCAL = 10
        self.ua_mobile_app = "UA Mobile App"

    def test_finds_the_correct_contact_among_a_list(self):
        expected_ua = "UA Mobile App"
        contact1 = mobile_app_wake_up.Contact("UA WebAPP", "contact1@127.0.0.1:80", 50)
        contact2 = mobile_app_wake_up.Contact(expected_ua, "contact2@192.168.56.1:8090", 20)
        contact3 = mobile_app_wake_up.Contact("Another", "contact3@10.181.12.2:443", 20)

        def get_variable(value):
            if value == f"PJSIP_CONTACT(contact1,expiration_time)":
                return str(contact1.expiration_time) # agi.get returns a str
            if value == f"PJSIP_CONTACT(contact1,uri)":
                return contact1.uri
            if value == f"PJSIP_CONTACT(contact1,user_agent)":
                return contact1.user_agent
            if value == f"PJSIP_CONTACT(contact2,expiration_time)":
                return str(contact2.expiration_time) # agi.get returns a str
            if value == f"PJSIP_CONTACT(contact2,uri)":
                return contact2.uri
            if value == f"PJSIP_CONTACT(contact2,user_agent)":
                return contact2.user_agent
            if value == f"PJSIP_CONTACT(contact3,expiration_time)":
                return str(contact3.expiration_time) # agi.get returns a str
            if value == f"PJSIP_CONTACT(contact3,uri)":
                return contact3.uri
            if value == f"PJSIP_CONTACT(contact3,user_agent)":
                return contact3.user_agent
            if value == 'X_EDGE_IP':
                return ''

        agi = Mock()
        agi.get_variable = Mock(side_effect=get_variable)
        agi.set_variable = Mock()

        result = mobile_app_wake_up.find_contact(agi, self.XIVO_TMP_CONTACTS, expected_ua, self.XIVO_MOBILE_PEER_EXPIRE_BEFORE_PUSH_LOCAL)

        agi.set_variable.assert_has_calls(
                                [call('X_EDGE_IP', '127.0.0.1'),
                                call('X_EDGE_IP', '192.168.56.1')])
        self.assertTrue(result.user_agent == contact2.user_agent)
        self.assertTrue(result.uri == contact2.uri)
        self.assertTrue(result.expiration_time == contact2.expiration_time)

    def test_finds_the_correct_contact_when_only_one(self):
        expected_ua = "UA Mobile App"
        contact2 = mobile_app_wake_up.Contact(expected_ua, "contact2@192.168.56.1:8090", 20)

        def get_variable(value):
            if value == f"PJSIP_CONTACT(contact2,expiration_time)":
                return str(contact2.expiration_time)
            if value == f"PJSIP_CONTACT(contact2,uri)":
                return contact2.uri
            if value == f"PJSIP_CONTACT(contact2,user_agent)":
                return contact2.user_agent
            if value == 'X_EDGE_IP':
                return ''

        agi = Mock()
        agi.get_variable = Mock(side_effect=get_variable)
        agi.set_variable = Mock()

        result = mobile_app_wake_up.find_contact(agi, 'contact2', expected_ua, self.XIVO_MOBILE_PEER_EXPIRE_BEFORE_PUSH_LOCAL)

        agi.set_variable.assert_called_once_with('X_EDGE_IP', '192.168.56.1')
        self.assertTrue(result.user_agent == contact2.user_agent)
        self.assertTrue(result.uri == contact2.uri)
        self.assertTrue(result.expiration_time == contact2.expiration_time)

    def test_finds_the_contact_with_expire_up_to_date(self):
        expected_ua = "UA Mobile App"
        contact1 = mobile_app_wake_up.Contact(expected_ua, "contact1@127.0.0.1:80", 9)
        contact2 = mobile_app_wake_up.Contact("Another", "contact2@192.168.56.1:8090", 20)
        contact3 = mobile_app_wake_up.Contact(expected_ua, "contact3@10.181.12.2:443", 50)

        def get_variable(value):
            if value == f"PJSIP_CONTACT(contact1,expiration_time)":
                return str(contact1.expiration_time)
            if value == f"PJSIP_CONTACT(contact1,uri)":
                return contact1.uri
            if value == f"PJSIP_CONTACT(contact1,user_agent)":
                return contact1.user_agent
            if value == f"PJSIP_CONTACT(contact2,expiration_time)":
                return str(contact2.expiration_time)
            if value == f"PJSIP_CONTACT(contact2,uri)":
                return contact2.uri
            if value == f"PJSIP_CONTACT(contact2,user_agent)":
                return contact2.user_agent
            if value == f"PJSIP_CONTACT(contact3,expiration_time)":
                return str(contact3.expiration_time)
            if value == f"PJSIP_CONTACT(contact3,uri)":
                return contact3.uri
            if value == f"PJSIP_CONTACT(contact3,user_agent)":
                return contact3.user_agent
            if value == 'X_EDGE_IP':
                return ''

        agi = Mock()
        agi.get_variable = Mock(side_effect=get_variable)
        agi.set_variable = Mock()

        result = mobile_app_wake_up.find_contact(agi, self.XIVO_TMP_CONTACTS, expected_ua, self.XIVO_MOBILE_PEER_EXPIRE_BEFORE_PUSH_LOCAL)


        agi.set_variable.assert_has_calls(
                                [call('X_EDGE_IP', '127.0.0.1'),
                                call('X_EDGE_IP', '192.168.56.1'),
                                call('X_EDGE_IP', '10.181.12.2')])
        self.assertEqual(result.user_agent, contact3.user_agent)
        self.assertEqual(result.uri, contact3.uri)
        self.assertEqual(result.expiration_time, contact3.expiration_time)

    def test_returns_none_when_found_contact_is_expired(self):
        expected_ua = "UA Mobile App"
        contact1 = mobile_app_wake_up.Contact(expected_ua, "contact1@127.0.0.1:80", 8)
        contact2 = mobile_app_wake_up.Contact("Another", "contact2@192.168.56.1:8090", 20)
        contact3 = mobile_app_wake_up.Contact(expected_ua, "contact3@10.181.12.2:443", 9)

        def get_variable(value):
            if value == f"PJSIP_CONTACT(contact1,expiration_time)":
                return str(contact1.expiration_time)
            if value == f"PJSIP_CONTACT(contact1,uri)":
                return contact1.uri
            if value == f"PJSIP_CONTACT(contact1,user_agent)":
                return contact1.user_agent
            if value == f"PJSIP_CONTACT(contact2,expiration_time)":
                return str(contact2.expiration_time)
            if value == f"PJSIP_CONTACT(contact2,uri)":
                return contact2.uri
            if value == f"PJSIP_CONTACT(contact2,user_agent)":
                return contact2.user_agent
            if value == f"PJSIP_CONTACT(contact3,expiration_time)":
                return str(contact3.expiration_time)
            if value == f"PJSIP_CONTACT(contact3,uri)":
                return contact3.uri
            if value == f"PJSIP_CONTACT(contact3,user_agent)":
                return contact3.user_agent

        agi = Mock()
        agi.get_variable = Mock(side_effect=get_variable)

        result = mobile_app_wake_up.find_contact(agi, self.XIVO_TMP_CONTACTS, expected_ua, self.XIVO_MOBILE_PEER_EXPIRE_BEFORE_PUSH_LOCAL)
        self.assertEqual(result, None)

    def test_returns_none_when_no_contact_with_correct_ua(self):
        expected_ua = "UA Mobile App"
        contact1 = mobile_app_wake_up.Contact("contact1", "contact1@127.0.0.1:80", 50)
        contact2 = mobile_app_wake_up.Contact("contact2", "contact2@192.168.56.1:8090", 20)
        contact3 = mobile_app_wake_up.Contact("contact3", "contact3@10.181.12.2:443", 9)

        def get_variable(value):
            if value == f"PJSIP_CONTACT(contact1,expiration_time)":
                return str(contact1.expiration_time)
            if value == f"PJSIP_CONTACT(contact1,uri)":
                return contact1.uri
            if value == f"PJSIP_CONTACT(contact1,user_agent)":
                return contact1.user_agent
            if value == f"PJSIP_CONTACT(contact2,expiration_time)":
                return str(contact2.expiration_time)
            if value == f"PJSIP_CONTACT(contact2,uri)":
                return contact2.uri
            if value == f"PJSIP_CONTACT(contact2,user_agent)":
                return contact2.user_agent
            if value == f"PJSIP_CONTACT(contact3,expiration_time)":
                return str(contact3.expiration_time)
            if value == f"PJSIP_CONTACT(contact3,uri)":
                return contact3.uri
            if value == f"PJSIP_CONTACT(contact3,user_agent)":
                return contact3.user_agent

        agi = Mock()
        agi.get_variable = Mock(side_effect=get_variable)
        result = mobile_app_wake_up.find_contact(agi, self.XIVO_TMP_CONTACTS, expected_ua, self.XIVO_MOBILE_PEER_EXPIRE_BEFORE_PUSH_LOCAL)
        self.assertEqual(result, None)

    @patch('time.sleep')
    @patch('xivo_agid.modules.mobile_app_wake_up.find_contact')
    def test_wait_for_wake_up_finds_new_contact(self, find_contact, time_sleep):
        peer_name = "mypeer"
        initial_contact_list = "mypeer;@1234"
        updated_contact_list = "mypeer;@1234,mypeer;@4567"

        expected_ua = "UA Mobile App"
        expected_contact = mobile_app_wake_up.Contact(expected_ua, "mypeer@10.181.12.2:443", 100)

        agi = Mock()
        agi.get_variable.side_effect = [initial_contact_list, updated_contact_list]
        find_contact.side_effect = [None, expected_contact]
        time_sleep.return_value = None

        result = mobile_app_wake_up.wait_for_wake_up(agi, 3, peer_name, "UA Mobile App", 10)

        find_contact.assert_has_calls(
                            [call(agi, initial_contact_list, expected_ua, 10),
                             call(agi, updated_contact_list, expected_ua, 10)])

        time_sleep.assert_has_calls(
                            [call(mobile_app_wake_up.SLEEP_SECONDS)])

        self.assertEqual(result.user_agent, expected_contact.user_agent)
        self.assertEqual(result.uri, expected_contact.uri)
        self.assertEqual(result.expiration_time, expected_contact.expiration_time)

    @patch('time.sleep')
    @patch('xivo_agid.modules.mobile_app_wake_up.find_contact')
    def test_wait_for_wake_up_returns_none_if_no_new_contact(self, find_contact, time_sleep):
        peer_name = "mypeer"
        initial_contact_list = "mypeer;@1234"

        expected_ua = "UA Mobile App"

        agi = Mock()
        agi.get_variable.return_value = initial_contact_list
        find_contact.side_effect = [None, None, None]
        time_sleep.return_value = None

        result = mobile_app_wake_up.wait_for_wake_up(agi, 3, peer_name, "UA Mobile App", 10)

        find_contact.assert_has_calls(
                            [call(agi, initial_contact_list, expected_ua, 10),
                             call(agi, initial_contact_list, expected_ua, 10),
                             call(agi, initial_contact_list, expected_ua, 10)])

        time_sleep.assert_has_calls(
                            [call(mobile_app_wake_up.SLEEP_SECONDS),
                             call(mobile_app_wake_up.SLEEP_SECONDS),
                             call(mobile_app_wake_up.SLEEP_SECONDS)])

        self.assertEqual(result, None)

    @patch('time.sleep')
    @patch('xivo_agid.modules.mobile_app_wake_up.find_contact')
    def test_wait_for_wake_up_loops_until_given_nb_loops(self, find_contact, time_sleep):
        peer_name = "mypeer"
        initial_contact_list = "mypeer;@1234"

        expected_ua = "UA Mobile App"
        expected_nb_loops = 5

        agi = Mock()
        agi.get_variable.return_value = initial_contact_list
        find_contact.side_effect = [None ] * expected_nb_loops
        time_sleep.return_value = None

        result = mobile_app_wake_up.wait_for_wake_up(agi, 5, peer_name, "UA Mobile App", 10)

        find_contact_calls = [call(agi, initial_contact_list, expected_ua, 10)] * expected_nb_loops
        find_contact.assert_has_calls(find_contact_calls)

        time_sleep_calls = [call(mobile_app_wake_up.SLEEP_SECONDS)] * expected_nb_loops
        time_sleep.assert_has_calls(time_sleep_calls)

        self.assertEqual(result, None)

    @patch('xivo_agid.modules.mobile_app_wake_up.get_number_of_loops')
    @patch('xivo_agid.modules.mobile_app_wake_up.wait_for_wake_up')
    @patch('xivo_agid.modules.mobile_app_wake_up.find_contact')
    @patch('xivo_agid.modules.mobile_push_notification.mobile_push_notification')
    def test_play_msg_wait_with_music(self, mobile_push_notification, find_contact, wait_for_wake_up, get_number_of_loops):
        play_msg = 'True'
        wait_with_music = 'True'

        contact = mobile_app_wake_up.Contact("contact1", "contact1@127.0.0.1:80", 50)

        def get_variable(value):
            if value == f"XIVO_INTERFACE":
                return "PJSIP/contact1"
            if value == f"PJSIP_AOR(contact1,contact)":
                return self.XIVO_TMP_CONTACTS
            if value == f"PJSIP_CONTACT(contact1,expiration_time)":
                return str(contact.expiration_time)
            if value == f"PJSIP_CONTACT(contact1,uri)":
                return contact.uri
            if value == f"PJSIP_CONTACT(contact1,user_agent)":
                return contact.user_agent

        def appexec(value, args=None):
            if value == 'PlayTones':
                return 'PlayTones'
            elif value == 'Playback':
                return 'Playback'
            elif value == 'StopPlayTones':
                return 'StopPlayTones'

        def set_music(state):
            if state == 'ON':
                return 'setting music'
            else :
                return 'stoping music'

        agi = Mock()
        agi.get_variable = Mock(side_effect=get_variable)
        agi.appexec = Mock(side_effect=appexec)
        agi.set_music = Mock(side_effect=set_music)

        find_contact.return_value = contact
        mobile_push_notification.return_value = None
        wait_for_wake_up.return_value = contact
        get_number_of_loops.return_value = 3

        mobile_app_wake_up.mobile_app_wake_up(agi, None, [play_msg, wait_with_music, ''])

        agi.appexec.assert_called_once_with('Playback', 'mobileapp-try-reach-text')
        agi.set_music.assert_has_calls([call('ON'), call('OFF')])
        get_number_of_loops.assert_called_once_with(agi, '')
        wait_for_wake_up.assert_called_once()
        mobile_push_notification.assert_called_once()

    @patch('xivo_agid.modules.mobile_app_wake_up.get_number_of_loops')
    @patch('xivo_agid.modules.mobile_app_wake_up.wait_for_wake_up')
    @patch('xivo_agid.modules.mobile_app_wake_up.find_contact')
    @patch('xivo_agid.modules.mobile_push_notification.mobile_push_notification')
    def test_play_msg_wait_with_ring(self, mobile_push_notification, find_contact, wait_for_wake_up, get_number_of_loops):
        play_msg = 'True'
        wait_with_music = 'False'

        contact = mobile_app_wake_up.Contact("contact1", "contact1@127.0.0.1:80", 50)

        def get_variable(value):
            if value == f"XIVO_INTERFACE":
                return "PJSIP/contact1"
            if value == f"PJSIP_AOR(contact1,contact)":
                return self.XIVO_TMP_CONTACTS
            if value == f"PJSIP_CONTACT(contact1,expiration_time)":
                return str(contact.expiration_time)
            if value == f"PJSIP_CONTACT(contact1,uri)":
                return contact.uri
            if value == f"PJSIP_CONTACT(contact1,user_agent)":
                return contact.user_agent

        def appexec(value, args=None):
            if value == 'PlayTones':
                return 'PlayTones'
            elif value == 'Playback':
                return 'Playback'
            elif value == 'StopPlayTones':
                return 'StopPlayTones'

        def set_music(state):
            if state == 'ON':
                return 'setting music'
            else :
                return 'stoping music'

        agi = Mock()
        agi.get_variable = Mock(side_effect=get_variable)
        agi.appexec = Mock(side_effect=appexec)
        agi.set_music = Mock(side_effect=set_music)

        find_contact.return_value = contact
        mobile_push_notification.return_value = None
        wait_for_wake_up.return_value = contact
        get_number_of_loops.return_value = 3

        mobile_app_wake_up.mobile_app_wake_up(agi, None, [play_msg, wait_with_music, ''])

        agi.appexec.assert_has_calls(
                            [call('Playback', 'mobileapp-try-reach-text'),
                             call('PlayTones', 'ring'),
                             call('StopPlayTones')])
        agi.set_music.assert_not_called()
        get_number_of_loops.assert_called_once_with(agi, '')
        wait_for_wake_up.assert_called_once()
        mobile_push_notification.assert_called_once()

    @patch('xivo_agid.modules.mobile_app_wake_up.get_number_of_loops')
    @patch('xivo_agid.modules.mobile_app_wake_up.wait_for_wake_up')
    @patch('xivo_agid.modules.mobile_app_wake_up.find_contact')
    @patch('xivo_agid.modules.mobile_push_notification.mobile_push_notification')
    def test_dont_play_msg(self, mobile_push_notification, find_contact, wait_for_wake_up, get_number_of_loops):
        play_msg = 'False'
        contact = mobile_app_wake_up.Contact("contact1", "contact1@127.0.0.1:80", 50)

        def get_variable(value):
            if value == f"XIVO_INTERFACE":
                return "PJSIP/contact1"
            if value == f"PJSIP_AOR(contact1,contact)":
                return self.XIVO_TMP_CONTACTS
            if value == f"PJSIP_CONTACT(contact1,expiration_time)":
                return str(contact.expiration_time)
            if value == f"PJSIP_CONTACT(contact1,uri)":
                return contact.uri
            if value == f"PJSIP_CONTACT(contact1,user_agent)":
                return contact.user_agent

        def appexec(value, args=None):
            if value == 'PlayTones':
                return 'PlayTones'
            elif value == 'Playback':
                return 'Playback'
            elif value == 'StopPlayTones':
                return 'StopPlayTones'

        def set_music(state):
            if state == 'ON':
                return 'setting music'
            else :
                return 'stoping music'

        agi = Mock()
        agi.get_variable = Mock(side_effect=get_variable)
        agi.appexec = Mock(side_effect=appexec)
        agi.set_music = Mock(side_effect=set_music)

        find_contact.return_value = contact
        mobile_push_notification.return_value = None
        wait_for_wake_up.return_value = contact
        get_number_of_loops.return_value = 3

        mobile_app_wake_up.mobile_app_wake_up(agi, None, [play_msg, '', 'joe'])

        agi.appexec.assert_not_called()
        agi.set_music.assert_not_called()
        get_number_of_loops.assert_called_once_with(agi, 'joe')
        wait_for_wake_up.assert_called_once()
        mobile_push_notification.assert_called_once()

    @patch('xivo_agid.modules.mobile_app_wake_up.wait_for_wake_up')
    @patch('xivo_agid.modules.mobile_app_wake_up.find_contact')
    @patch('xivo_agid.modules.mobile_push_notification.mobile_push_notification')
    def test_wake_up(self, mobile_push_notification, find_contact, wait_for_wake_up):
        play_msg = True

        contact = mobile_app_wake_up.Contact("contact1", "contact1@127.0.0.1:80", 50)
        def get_variable(value):
            if value == f"XIVO_INTERFACE":
                return "PJSIP/contact1"
            if value == f"PJSIP_AOR(contact1,contact)":
                return self.XIVO_TMP_CONTACTS
            if value == f"PJSIP_CONTACT(contact1,expiration_time)":
                return str(contact.expiration_time)
            if value == f"PJSIP_CONTACT(contact1,uri)":
                return contact.uri
            if value == f"PJSIP_CONTACT(contact1,user_agent)":
                return contact.user_agent

        agi = Mock()
        agi.get_variable = Mock(side_effect=get_variable)
        find_contact.return_value = contact
        mobile_push_notification.return_value = None
        wait_for_wake_up.return_value = contact

        result = mobile_app_wake_up.mobile_app_wake_up(agi, None, [play_msg, '', ''])

        find_contact.assert_called()
        mobile_push_notification.assert_called()
        wait_for_wake_up.assert_called_once()
        self.assertEqual(result, 'Mobile app contact %s woke up in time' % contact.user_agent)

    def test_get_number_of_loops_returns_default(self):
        expected_loops = 3

        def get_variable(value):
            if value == "XIVO_MAPP_LOOPS_MOBILEAPP":
                return ""
            if value == "XIVO_MAPP_LOOPS_WEBAPPANDMOBILEAPP":
                return ""

        agi = Mock()
        agi.get_variable = Mock(side_effect=get_variable)

        loops_mapp = mobile_app_wake_up.get_number_of_loops(agi, "MobileApp")
        loops_wandmapp = mobile_app_wake_up.get_number_of_loops(agi, "WebAppAndMobileApp")

        self.assertEqual(loops_mapp, expected_loops)
        self.assertEqual(loops_wandmapp, expected_loops)

    def test_get_number_of_loops_returns_mobile_value_for_mobileapp_preferred_device(self):
        expected_loops = 4

        def get_variable(value):
            if value == "XIVO_MAPP_LOOPS_MOBILEAPP":
                return "4"
            if value == "XIVO_MAPP_LOOPS_WEBAPPANDMOBILEAPP":
                return "2"

        agi = Mock()
        agi.get_variable = Mock(side_effect=get_variable)

        loops = mobile_app_wake_up.get_number_of_loops(agi, "MobileApp")

        self.assertEqual(loops, expected_loops)

    def test_get_number_of_loops_returns_webandmobile_value_for_webappandmobileapp_preferred_device(self):
        expected_loops = 2

        def get_variable(value):
            if value == "XIVO_MAPP_LOOPS_MOBILEAPP":
                return "4"
            if value == "XIVO_MAPP_LOOPS_WEBAPPANDMOBILEAPP":
                return "2"

        agi = Mock()
        agi.get_variable = Mock(side_effect=get_variable)

        loops = mobile_app_wake_up.get_number_of_loops(agi, "WebAppAndMobileApp")

        self.assertEqual(loops, expected_loops)

    def test_get_number_of_loops_returns_default_for_unsupported_values(self):
        expected_loops = 3

        agi = Mock()
        agi.get_variable.side_effect = ["-1", "a", "0"]

        loop1 = mobile_app_wake_up.get_number_of_loops(agi, "MobileApp")
        self.assertEqual(loop1, expected_loops)
        loop2 = mobile_app_wake_up.get_number_of_loops(agi, "WebAppAndMobileApp")
        self.assertEqual(loop2, expected_loops)
        loop3 = mobile_app_wake_up.get_number_of_loops(agi, "WebAppAndMobileApp")
        self.assertEqual(loop3, expected_loops)
        