# -*- 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.


from hamcrest import assert_that, equal_to, has_entries, has_entry, has_item, has_items, has_length, \
    is_not, contains_exactly, none, not_none, greater_than
from test_api import associations as a
from test_api import confd
from test_api import config
from test_api import db
from test_api import errors as e
from test_api import fixtures
from test_api import mocks
from test_api import scenarios as s
from test_api.bus import BusClient

FULL_USER = {"firstname": "Jôhn",
             "lastname": "Smêth",
             "username": "jsmeth",
             "email": "jsmeth@smeth.com",
             "mobile_phone_number": "+4185551234*2",
             "userfield": "userfield",
             "caller_id": '"Jôhnny Smith" <4185551234>',
             "outgoing_caller_id": '"Johnny" <123>',
             "music_on_hold": "default",
             "language": "fr_FR",
             "timezone": "America/Montreal",
             "preprocess_subroutine": "preprocess_subroutine",
             "password": "password",
             "description": "John's description",
             "supervision_enabled": False,
             "call_transfer_enabled": False,
             "call_record_enabled": True,
             "online_call_record_enabled": True,
             "call_permission_password": '1234',
             "enabled": False,
             "ring_seconds": 60,
             "agent_number": None,
             "agent_group_id": None,
             "simultaneous_calls": 10,
             "labels": []}

AGENT_USER = {"firstname": "James",
              "lastname": "Bond",
              "username": "jbond",
              "email": "jbond@gov.uk",
              "mobile_phone_number": "+44123456789",
              "music_on_hold": "default",
              "language": "fr_FR",
              "timezone": "Europe/London",
              "password": "password",
              "agent_number": "2000",
              "agent_group_id": 2,
              "simultaneous_calls": 10}

NULL_USER = {"firstname": "Jôhn",
             "lastname": None,
             "username": None,
             "email": None,
             "mobile_phone_number": None,
             "userfield": None,
             "outgoing_caller_id": None,
             "music_on_hold": None,
             "language": None,
             "timezone": None,
             "preprocess_subroutine": None,
             "password": None,
             "description": None,
             "supervision_enabled": True,
             "call_transfer_enabled": True,
             "call_record_enabled": False,
             "online_call_record_enabled": False,
             "call_permission_password": None,
             "enabled": True,
             "ring_seconds": 20,
             "agent_number": None,
             "agent_group_id": None,
             "simultaneous_calls": 2,
             "labels": []}


def test_get_errors():
    fake_get = confd.users(999999).get
    yield s.check_resource_not_found, fake_get, 'User'


def test_post_errors():
    empty_post = confd.users.post
    user_post = confd.users(firstname="Jôhn").post

    yield s.check_missing_required_field_returns_error, empty_post, 'firstname'
    for check in error_checks(user_post):
        yield check


def error_checks(url):
    yield s.check_bogus_field_returns_error, url, 'firstname', 123
    yield s.check_bogus_field_returns_error, url, 'firstname', None
    yield s.check_bogus_field_returns_error, url, 'lastname', 123
    yield s.check_bogus_field_returns_error, url, 'email', 123
    yield s.check_bogus_field_returns_error, url, 'timezone', 123
    yield s.check_bogus_field_returns_error, url, 'language', 123
    yield s.check_bogus_field_returns_error, url, 'description', 123
    yield s.check_bogus_field_returns_error, url, 'caller_id', 123
    yield s.check_bogus_field_returns_error, url, 'outgoing_caller_id', 123
    yield s.check_bogus_field_returns_error, url, 'mobile_phone_number', 123
    yield s.check_bogus_field_returns_error, url, 'username', 123
    yield s.check_bogus_field_returns_error, url, 'password', 123
    yield s.check_bogus_field_returns_error, url, 'music_on_hold', 123
    yield s.check_bogus_field_returns_error, url, 'preprocess_subroutine', 123
    yield s.check_bogus_field_returns_error, url, 'userfield', 123
    yield s.check_bogus_field_returns_error, url, 'caller_id', 'callerid'
    yield s.check_bogus_field_returns_error, url, 'mobile_phone_number', '123abcd'
    yield s.check_bogus_field_returns_error, url, 'call_transfer_enabled', 'yeah'
    yield s.check_bogus_field_returns_error, url, 'call_record_enabled', 'yeah'
    yield s.check_bogus_field_returns_error, url, 'online_call_record_enabled', 'yeah'
    yield s.check_bogus_field_returns_error, url, 'supervision_enabled', 'yeah'
    yield s.check_bogus_field_returns_error, url, 'ring_seconds', 'ten'
    yield s.check_bogus_field_returns_error, url, 'simultaneous_calls', 'sixty'
    yield s.check_bogus_field_returns_error, url, 'simultaneous_calls', -1
    yield s.check_bogus_field_returns_error, url, 'simultaneous_calls', 21
    yield s.check_bogus_field_returns_error, url, 'username', 'ûsername',
    yield s.check_bogus_field_returns_error, url, 'ring_seconds', 6
    yield s.check_bogus_field_returns_error, url, 'ring_seconds', -1
    yield s.check_bogus_field_returns_error, url, 'ring_seconds', 65
    yield s.check_bogus_field_returns_error, url, 'language', 'klingon'
    yield s.check_bogus_field_returns_error, url, 'call_permission_password', 1234
    yield s.check_bogus_field_returns_error, url, 'call_permission_password', 'invalid_char'
    yield s.check_bogus_field_returns_error, url, 'enabled', 'yeah'


def put_error_checks(url):
    yield s.check_bogus_field_returns_error, url, 'caller_id', None
    yield s.check_bogus_field_returns_error, url, 'call_transfer_enabled', None
    yield s.check_bogus_field_returns_error, url, 'call_record_enabled', None
    yield s.check_bogus_field_returns_error, url, 'online_call_record_enabled', None
    yield s.check_bogus_field_returns_error, url, 'supervision_enabled', None
    yield s.check_bogus_field_returns_error, url, 'ring_seconds', None
    yield s.check_bogus_field_returns_error, url, 'simultaneous_calls', None
    yield s.check_bogus_field_returns_error, url, 'ring_seconds', None
    yield s.check_bogus_field_returns_error, url, 'enabled', None


@fixtures.user()
def test_put_errors(user):
    user_put = confd.users(user['id']).put

    for check in error_checks(user_put):
        yield check
    for check in put_error_checks(user_put):
        yield check


@fixtures.user(firstname='user1', username='unique_username', email='unique@email')
@fixtures.user()
def test_unique_errors(user1, user2):
    url = confd.users(user2['id']).put
    for check in unique_error_checks(url, user1):
        yield check

    required_body = {'firstname': 'user2'}
    url = confd.users.post
    for check in unique_error_checks(url, user1, required_body):
        yield check


def unique_error_checks(url, existing_resource, required_body=None):
    yield s.check_bogus_field_returns_error, url, 'username', existing_resource['username'], required_body
    yield s.check_bogus_field_returns_error, url, 'email', existing_resource['email'], required_body


@fixtures.user()
def test_delete_errors(user):
    user_url = confd.users(user['id'])
    user_url.delete()
    yield s.check_resource_not_found, user_url.get, 'User'


@fixtures.user(firstname='ÉricDir')
def test_that_the_directory_view_works_with_unicode_characters(user):
    response = confd.users.get(view='directory', search='éricdir')
    response.assert_ok()
    assert_that(response.items[0]['id'], equal_to(user['id']))

    response = confd.users.get(view='directory', search_ng='éricdir')
    response.assert_ok()
    assert_that(response.items[0]['id'], equal_to(user['id']))


@fixtures.user()
@fixtures.line()
@fixtures.sip()
@fixtures.extension()
def test_summary_view_on_sip_endpoint(user, line, sip, extension):
    expected = has_entries(id=user['id'],
                           uuid=user['uuid'],
                           firstname=user['firstname'],
                           lastname=user['lastname'],
                           provisioning_code=line['provisioning_code'],
                           extension=extension['exten'],
                           context=extension['context'],
                           entity=config.ENTITY_NAME,
                           enabled=True,
                           protocol='sip',
                           configregistrar='default',
                           line_type='sip')

    with a.line_endpoint_sip(line, sip), a.line_extension(line, extension), \
            a.user_line(user, line):
        response = confd.users.get(view='summary', id=user['id'])
        assert_that(response.items, contains_exactly(expected))


@fixtures.user()
@fixtures.line()
@fixtures.sip(options=[["webrtc", "yes"]])
@fixtures.extension()
def test_summary_view_on_sip_endpoint_with_ua(user, line, sip, extension):
    expected = has_entries(id=user['id'],
                           uuid=user['uuid'],
                           firstname=user['firstname'],
                           lastname=user['lastname'],
                           provisioning_code=line['provisioning_code'],
                           extension=extension['exten'],
                           context=extension['context'],
                           entity=config.ENTITY_NAME,
                           enabled=True,
                           protocol='sip',
                           configregistrar='default',
                           line_type='webrtc')

    with a.line_endpoint_sip(line, sip), a.line_extension(line, extension), \
            a.user_line(user, line):
        response = confd.users.get(view='summary', id=user['id'])
        assert_that(response.items, contains_exactly(expected))


@fixtures.user()
@fixtures.line()
@fixtures.sip(options=[["webrtc", "ua"]])
@fixtures.extension()
def test_summary_view_on_sip_endpoint_with_ua(user, line, sip, extension):
    expected = has_entries(id=user['id'],
                           uuid=user['uuid'],
                           firstname=user['firstname'],
                           lastname=user['lastname'],
                           provisioning_code=line['provisioning_code'],
                           extension=extension['exten'],
                           context=extension['context'],
                           entity=config.ENTITY_NAME,
                           enabled=True,
                           protocol='sip',
                           configregistrar='default',
                           line_type='ua')

    with a.line_endpoint_sip(line, sip), a.line_extension(line, extension), \
            a.user_line(user, line):
        response = confd.users.get(view='summary', id=user['id'])
        assert_that(response.items, contains_exactly(expected))


@fixtures.user()
@fixtures.line()
@fixtures.sip()
@fixtures.extension()
@fixtures.registrar()
def test_summary_view_on_sip_endpoint_with_registrar(user, line, sip, extension, registrar):
    expected = has_entries(provisioning_code='243546',
                           configregistrar=registrar['id'],
                           line_type='sip')

    with a.line_endpoint_sip(line, sip), a.line_extension(line, extension), \
            a.user_line(user, line):
        confd.lines(line['id']).put(registrar=registrar['id'], provisioning_code='243546')

        response = confd.users.get(view='summary', id=user['id'])
        assert_that(response.items, contains_exactly(expected))


@fixtures.user()
@fixtures.line()
@fixtures.sccp()
@fixtures.extension()
def test_summary_view_on_sccp_endpoint(user, line, sccp, extension):
    expected = has_entries(id=user['id'],
                           uuid=user['uuid'],
                           firstname=user['firstname'],
                           lastname=user['lastname'],
                           provisioning_code=none(),
                           extension=extension['exten'],
                           context=extension['context'],
                           entity=config.ENTITY_NAME,
                           enabled=True,
                           protocol='sccp',
                           line_type='sccp')

    with a.line_endpoint_sccp(line, sccp), a.line_extension(line, extension), \
            a.user_line(user, line):
        response = confd.users.get(view='summary', id=user['id'])
        assert_that(response.items, contains_exactly(expected))


@fixtures.user()
@fixtures.line()
@fixtures.custom()
@fixtures.extension()
def test_summary_view_on_custom_endpoint(user, line, custom, extension):
    expected = has_entries(id=user['id'],
                           uuid=user['uuid'],
                           firstname=user['firstname'],
                           lastname=user['lastname'],
                           provisioning_code=none(),
                           extension=extension['exten'],
                           context=extension['context'],
                           entity=config.ENTITY_NAME,
                           enabled=True,
                           protocol='custom',
                           line_type='custom')

    with a.line_endpoint_custom(line, custom), a.line_extension(line, extension), \
            a.user_line(user, line):
        response = confd.users.get(view='summary', id=user['id'])
        assert_that(response.items, contains_exactly(expected))


@fixtures.user()
def test_summary_view_on_user_without_line(user):
    expected = has_entries(id=user['id'],
                           uuid=user['uuid'],
                           firstname=user['firstname'],
                           lastname=user['lastname'],
                           provisioning_code=none(),
                           extension=none(),
                           context=none(),
                           entity=config.ENTITY_NAME,
                           enabled=True,
                           protocol=none(),
                           line_type=none())

    response = confd.users.get(view='summary', id=user['id'])
    assert_that(response.items, contains_exactly(expected))


@fixtures.user(firstname="Lègacy", lastname="Usér")
@fixtures.user(firstname="Hîde", lastname="Mé")
def test_search_using_legacy_parameter(user1, user2):
    expected_found = has_item(has_entries(firstname="Lègacy", lastname="Usér"))
    expected_hidden = has_item(has_entries(firstname="Hîde", lastname="Mé"))

    response = confd.users.get(q="lègacy usér")

    assert_that(response.items, expected_found)
    assert_that(response.items, is_not(expected_hidden))


@fixtures.user(firstname="Léeroy",
               lastname="Jénkins",
               email="jenkins@leeroy.com",
               outgoing_caller_id='"Mystery Man" <5551234567>',
               username="leeroyjenkins",
               music_on_hold="leeroy_music_on_hold",
               mobile_phone_number="5552423232",
               userfield="leeroy jenkins userfield",
               description="Léeroy Jénkin's bio",
               enabled=False,
               preprocess_subroutine="leeroy_preprocess")
def test_search_on_user_view(user):
    url = confd.users
    searches = {
        'firstname': 'léeroy',
        'lastname': 'jénkins',
        'email': 'jenkins@',
        'music_on_hold': 'leeroy_music',
        'outgoing_caller_id': '5551234567',
        'mobile_phone_number': '2423232',
        'userfield': 'jenkins userfield',
        'description': "jénkin's bio",
        'preprocess_subroutine': 'roy_preprocess',
        'enabled': False,
    }

    for field, term in searches.items():
        yield check_search, url, field, term, user[field]

    allowed = [
        'exten', 'firstname', 'lastname', 'userfield', 'email', 'description', 'mobile_phone_number',
        'voicemail_number'
    ]
    for field, term in [_ for _ in searches.items() if _ in allowed]:
        yield check_search_ng, url, field, term, user[field]


@fixtures.user(firstname='Tame', lastname='Tiger')
@fixtures.user(firstname='Wild', lastname='Tiger')
@fixtures.user(firstname='Wíld Bláck', lastname='Tígér')
@fixtures.user(firstname='Tame', lastname='Tager')
def test_that_inaccurate_search_returns_appropriate_result(*users):
    tiger1 = {'firstname': 'Tame', 'lastname': 'Tiger'}
    tiger2 = {'firstname': 'Wild', 'lastname': 'Tiger'}
    tiger3 = {'firstname': 'Wíld Bláck', 'lastname': 'Tígér'}
    tiger4 = {'firstname': 'Tame', 'lastname': 'Tager'}
    url = confd.users

    response = url.get(search_ng='TiGeR')
    assert_that(response.items, has_items(has_entries(tiger1), has_entries(tiger2), has_entries(tiger3)))

    response = url.get(search_ng='wild tiger')
    assert_that(response.items, has_items(has_entries(tiger2), has_entries(tiger3)))

    response = url.get(search_ng='ta ti')
    assert_that(response.items, has_item(has_entries(tiger1)))

    response = url.get(search_ng='tuger')
    assert_that(
        response.items, has_items(has_entries(tiger1), has_entries(tiger2), has_entries(tiger4)))


@fixtures.user(firstname="Môustapha",
               lastname="Bângoura",
               email="moustapha@bangoura.com",
               mobile_phone_number="+5559284759",
               userfield="Moustapha userfield",
               description="Moustapha the greatest dancer")
def test_search_on_directory_view(user):
    url = confd.users(view='directory')

    searches = {
        'firstname': 'môustapha',
        'lastname': 'bângoura',
        'email': 'moustapha@',
        'mobile_phone_number': '928475',
        'userfield': 'moustapha userfield',
        'description': "greatest dancer",
    }

    for field, term in searches.items():
        yield check_search, url, field, term, user[field]


@fixtures.user(firstname='John', lastname='Agent')
@fixtures.agent(number='8001', numgroup=1)
@fixtures.user(firstname='Jean-Michel', lastname='Not Agent')
@fixtures.user(firstname='Secret', lastname='Agent')
@fixtures.agent(number='8002', numgroup=2)
def test_list_users_with_agent(user1, agent1, user2, user3, agent3):
    with db.queries() as queries:
        queries.associate_user_agent(user_id=user1['id'], agent_id=agent1['id'])
        queries.associate_user_agent(user_id=user3['id'], agent_id=agent3['id'])

    expected_for_user1 = has_item(has_entries(id=user1['id'],
                                              uuid=user1['uuid'],
                                              firstname=user1['firstname'],
                                              lastname=user1['lastname'],
                                              agent_number=agent1['number'],
                                              agent_group_id=agent1['numgroup']))

    expected_for_user2 = has_item(has_entries(id=user2['id'],
                                              uuid=user2['uuid'],
                                              firstname=user2['firstname'],
                                              lastname=user2['lastname'],
                                              agent_number=None,
                                              agent_group_id=None))

    expected_for_user3 = has_item(has_entries(id=user3['id'],
                                              uuid=user3['uuid'],
                                              firstname=user3['firstname'],
                                              lastname=user3['lastname'],
                                              agent_number=agent3['number'],
                                              agent_group_id=agent3['numgroup']))

    response = confd.users.get()

    assert_that(response.items, expected_for_user1)
    assert_that(response.items, expected_for_user2)
    assert_that(response.items, expected_for_user3)


@fixtures.user(firstname='John', lastname='Agent')
@fixtures.agent(number='8001', numgroup=1)
@fixtures.label()
def test_list_users_with_label(user1, agent1, label1):
    with db.queries() as queries:
        queries.associate_user_agent(user_id=user1['id'], agent_id=agent1['id'])
        queries.associate_user_label(user_id=user1['id'], label_id=label1['id'])

    expected_for_user1 = has_item(has_entries(id=user1['id'],
                                              uuid=user1['uuid'],
                                              firstname=user1['firstname'],
                                              lastname=user1['lastname'],
                                              agent_number=agent1['number'],
                                              agent_group_id=agent1['numgroup'],
                                              labels=[label1['id']]))

    response = confd.users.get()

    assert_that(response.items, expected_for_user1)


@fixtures.user()
@fixtures.line_sip()
@fixtures.extension()
def test_search_on_users_extension(user, line, extension):
    with a.user_line(user, line), a.line_extension(line, extension):
        response = confd.users.get(search=extension['exten'], view='directory')
        assert_that(response.items, has_item(has_entry('exten', extension['exten'])))

        response = confd.users.get(search_ng=extension['exten'], view='directory')
        assert_that(response.items, has_item(has_entry('exten', extension['exten'])))


@fixtures.user(firstname='Alicé')
@fixtures.line_sip()
@fixtures.extension()
def test_search_on_users_with_context_filter(user, line, extension):
    with a.user_line(user, line), a.line_extension(line, extension):
        response = confd.users.get(search='alic', view='directory', context='default')
        assert_that(response.total, equal_to(1))
        assert_that(response.items, has_item(has_entry('exten', extension['exten'])))

        response = confd.users.get(search='alic', view='directory', context='other')
        assert_that(response.total, equal_to(0))

        response = confd.users.get(search_ng='alic', view='directory', context='default')
        assert_that(response.total, equal_to(1))
        assert_that(response.items, has_item(has_entry('exten', extension['exten'])))

        response = confd.users.get(search_ng='alic', view='directory', context='other')
        assert_that(response.total, equal_to(0))


@fixtures.user(firstname="Âboubacar",
               lastname="Manè",
               description="Âboubacar le grand danseur")
@fixtures.line()
@fixtures.sip()
@fixtures.extension()
def test_search_on_summary_view(user, line, sip, extension):
    url = confd.users(view='summary')

    with a.line_endpoint_sip(line, sip), a.user_line(user, line), a.line_extension(line, extension):
        yield check_search, url, 'firstname', 'âbou', user['firstname']
        yield check_search, url, 'lastname', 'man', user['lastname']
        yield check_search, url, 'provisioning_code', line['provisioning_code'], line['provisioning_code']
        yield check_search, url, 'extension', extension['exten'], extension['exten']
        yield check_search_ng, url, 'firstname', 'âbou', user['firstname']
        yield check_search_ng, url, 'lastname', 'man', user['lastname']
        yield check_search_ng, url, 'extension', extension['exten'], extension['exten']


def check_search(url, field, term, value):
    expected = has_item(has_entry(field, value))
    response = url.get(search=term)
    assert_that(response.items, expected)


def check_search_ng(url, field, term, value):
    expected = has_item(has_entry(field, value))
    response = url.get(search_ng=term)
    assert_that(response.items, expected)


@fixtures.user(**FULL_USER)
def test_get_user(user):
    response = confd.users(user['id']).get()
    assert_that(response.item, has_entries(FULL_USER))


@fixtures.user(firstname="Snôm", lastname="Whîte")
@fixtures.user()
@fixtures.user()
def test_that_get_works_with_a_uuid(user_1, user_2_, user_3):
    result = confd.users(user_1['uuid']).get()

    assert_that(result.item, has_entries(firstname='Snôm', lastname='Whîte'))


@fixtures.user(firstname="Snôw", lastname="Whîte", username='snow.white+dwarves@disney.example.com')
def test_that_the_username_can_be_an_email(user):
    result = confd.users(user['id']).get()

    assert_that(result.item, has_entries(firstname='Snôw',
                                         lastname='Whîte',
                                         username='snow.white+dwarves@disney.example.com'))


def test_create_minimal_parameters():
    response = confd.users.post(firstname="Roger")

    response.assert_created('users')
    assert_that(response.item, has_entry("firstname", "Roger"))


def test_create_with_null_parameters_fills_default_values():
    response = confd.users.post(firstname="Charlie")
    response.assert_created('users')

    assert_that(response.item, has_entries(caller_id='"Charlie"',
                                           call_transfer_enabled=False,
                                           call_record_enabled=False,
                                           online_call_record_enabled=False,
                                           supervision_enabled=True,
                                           ring_seconds=20,
                                           simultaneous_calls=2))


def test_create_user_with_all_parameters():
    response = confd.users.post(**FULL_USER)

    response.assert_created('users')
    assert_that(response.item, has_entries(FULL_USER))


def test_create_user_with_all_parameters_null():
    response = confd.users.post(**NULL_USER)
    assert_that(response.item, has_entries(NULL_USER))


def test_bus_reload_event_when_create_user():
    BusClient.listen_delayed_events('config.ipbx.reload')
    expected_reload_commands = ['dialplan reload',
                                'module reload chan_sccp.so',
                                'module reload app_queue.so',
                                'sip reload']

    confd.users.post(firstname='John', lastname='Smith')

    reload_events = BusClient.drain_events()

    assert_that(reload_events, has_length(1))
    assert_that(reload_events[0]['data']['commands'], equal_to(expected_reload_commands))


def assert_sysconfd_add_user_with_agent(sysconfd, user_id):
    with db.queries() as queries:
        agent_id = queries.get_agent_id_by_user_id(user_id)

    agent_handler = {'ctibus': ['xivo[queuemember,update]',
                                'xivo[user,add,{}]'.format(user_id),
                                'xivo[user,edit,{}]'.format(user_id),
                                'xivo[agent,add,{}]'.format(agent_id)],
                     'agentbus': ['agent.add.{}'.format(agent_id)]}

    sysconfd.assert_request('/exec_request_handlers',
                            method='POST',
                            body_entries=agent_handler)


def assert_sysconfd_update_user_with_agent(sysconfd, user_id):
    with db.queries() as queries:
        agent_id = queries.get_agent_id_by_user_id(user_id)

    agent_handler = {'ctibus': ['xivo[agent,add,{}]'.format(agent_id),
                                'xivo[user,edit,{}]'.format(user_id),
                                'xivo[queuemember,update]'],
                     'agentbus': ['agent.add.{}'.format(agent_id)]}

    sysconfd.assert_request('/exec_request_handlers',
                            method='POST',
                            body_entries=agent_handler)


@mocks.sysconfd()
def test_create_user_with_agent(sysconfd):
    response = confd.users.post(**AGENT_USER)
    response.assert_created('users')
    assert_that(response.item, has_entries(AGENT_USER))
    user_id = response.item['id']

    assert_sysconfd_add_user_with_agent(sysconfd, user_id)


def test_create_user_generates_appropriate_caller_id():
    expected_caller_id = '"Jôhn"'
    response = confd.users.post(firstname='Jôhn')
    assert_that(response.item, has_entry('caller_id', expected_caller_id))

    expected_caller_id = '"Jôhn Doe"'
    response = confd.users.post(firstname='Jôhn', lastname='Doe')
    assert_that(response.item['caller_id'], equal_to(expected_caller_id))


def test_create_user_fix_cti_profile():
    response = confd.users.post(firstname='CTI', lastname='User', username='ucti', password='pwdcti')
    with db.queries() as queries:
        user = queries.get_user_by_id(response.item['id'])
        assert_that(user.cti_profile_id, not_none())
        assert_that(user.cti_profile_id, greater_than(0))
        assert_that(user.enableclient, equal_to(1))


@fixtures.user(firstname="Léeroy",
               lastname="Jénkins",
               email="leeroy@jenkins.com",
               outgoing_caller_id='"Mystery Man" <5551234567>',
               username="leeroyjenkins",
               music_on_hold="leeroy_music_on_hold",
               mobile_phone_number="5552423232",
               userfield="leeroy jenkins userfield",
               description="Léeroy Jénkin's bio",
               preprocess_subroutine="leeroy_preprocess")
def test_update_user_with_all_parameters(user):
    user_url = confd.users(user['id'])

    response = user_url.put(**FULL_USER)
    response.assert_updated()

    response = user_url.get()
    assert_that(response.item, has_entries(FULL_USER))


@fixtures.user(firstname="Léeroy",
               lastname="Jénkins")
def test_bus_reload_event_when_update_user(user):
    BusClient.listen_delayed_events('config.ipbx.reload')
    expected_reload_commands = ['dialplan reload',
                                'module reload chan_sccp.so',
                                'module reload app_queue.so',
                                'sip reload']

    user_url = confd.users(user['id'])
    user_url.put(firstname="Mystery", lastname="Man")

    reload_events = BusClient.drain_events()

    assert_that(reload_events, has_length(1))
    assert_that(reload_events[0]['data']['commands'], equal_to(expected_reload_commands))


@mocks.sysconfd()
@fixtures.user(firstname="Jack",
               lastname="Bauer")
def test_update_user_add_agent_info(sysconfd, user):
    user_url = confd.users(user['id'])
    sysconfd.clear_requests()
    response = user_url.put(agent_number='2001')
    response.assert_updated()

    response = user_url.get()
    assert_that(response.item, has_entry('agent_number', '2001'))
    assert_sysconfd_update_user_with_agent(sysconfd, user['id'])


@fixtures.user(firstname="Jack",
               lastname="Bauer")
def test_update_user_add_agent_info_and_group(user):
    user_url = confd.users(user['id'])

    response = user_url.put(agent_number='2002', agent_group_id=2)
    response.assert_updated()

    response = user_url.get()
    assert_that(response.item, has_entries(agent_number='2002', agent_group_id=2))


@fixtures.user(firstname="Saul",
               lastname="Berenson",
               agent_number="2003",
               agent_group_id=1)
def test_update_user_change_agent_group(user):
    user_url = confd.users(user['id'])

    response = user_url.put(agent_number="2003", agent_group_id=2)
    response.assert_updated()

    response = user_url.get()
    assert_that(response.item, has_entries(agent_number='2003', agent_group_id=2))


@fixtures.user(firstname="Agent",
               lastname="Secret",
               agent_number="007",
               agent_group_id=1)
def test_update_user_change_agent_group_only(user):
    user_url = confd.users(user['id'])

    response = user_url.put(agent_group_id=2)
    response.assert_updated()

    response = user_url.get()
    assert_that(response.item, has_entries(agent_group_id=2))


@fixtures.user(firstname="Carrie",
               lastname="Mathison",
               agent_number="2004")
def test_update_user_remove_agent_info(user):
    user_url = confd.users(user['id'])

    response = user_url.put(agent_number=None)
    response.assert_updated()

    response = user_url.get()
    assert_that(response.item, has_entry('agent_number', None))


@fixtures.user()
def test_update_user_with_all_parameters_null(user):
    response = confd.users(user['id']).put(**NULL_USER)
    response.assert_updated()

    response = confd.users(user['id']).get()
    assert_that(response.item, has_entries(**NULL_USER))


@fixtures.user(firstname='Not', lastname='Anagent')
def test_get_user_without_agent(user):
    result = confd.users(user['id']).get()

    assert_that(result.item, has_entries(firstname='Not',
                                         lastname='Anagent',
                                         agent_number=None))


@fixtures.user(firstname='John', lastname='Agent')
@fixtures.agent(number='8008')
def test_get_user_with_its_agentnumber(user, agent):
    with db.queries() as queries:
        queries.associate_user_agent(user_id=user['id'], agent_id=agent['id'])

    result = confd.users(user['id']).get()

    assert_that(result.item, has_entries(firstname='John',
                                         lastname='Agent',
                                         agent_number=agent['number']))


@fixtures.user()
def test_that_users_can_be_edited_by_uuid(user):
    response = confd.users(user['uuid']).put({'firstname': 'Fôo',
                                              'lastname': 'Bâr'})
    response.assert_updated()

    response = confd.users(user['uuid']).get()
    assert_that(response.item, has_entries(firstname='Fôo', lastname='Bâr'))


@fixtures.user()
@fixtures.label()
@fixtures.label()
def test_edit_user_label_association(user, label1, label2):
    with db.queries() as queries:
        queries.associate_user_label(user_id=user['id'], label_id=label1['id'])

    response = confd.users(user['uuid']).put({'labels': [label2['id']]})
    response.assert_updated()

    response = confd.users(user['uuid']).get()
    assert_that(response.item, has_entries(labels=[label2['id']]))


@fixtures.user()
def test_delete(user):
    response = confd.users(user['id']).delete()
    response.assert_deleted()


@fixtures.user()
@fixtures.label()
def test_delete_with_label(user, label):
    with db.queries() as queries:
        queries.associate_user_label(user_id=user['id'], label_id=label['id'])

    response = confd.users(user['id']).delete()
    response.assert_deleted()


@fixtures.user()
def test_bus_reload_event_when_delete_user(user):
    BusClient.listen_delayed_events('config.ipbx.reload')
    expected_reload_commands = ['dialplan reload',
                                'module reload chan_sccp.so',
                                'module reload app_queue.so',
                                'sip reload']

    confd.users(user['id']).delete()

    reload_events = BusClient.drain_events()

    assert_that(reload_events, has_length(1))
    assert_that(reload_events[0]['data']['commands'], equal_to(expected_reload_commands))


@fixtures.user()
def test_that_users_can_be_deleted_by_uuid(user):
    response = confd.users(user['uuid']).delete()
    response.assert_deleted()

    response = confd.users(user['uuid']).get()
    response.assert_status(404)


@fixtures.user(email='common@email.com')
def test_post_email_already_exists_then_error_raised(user):
    response = confd.users.post(firstname='bob', email='common@email.com')
    response.assert_match(400, e.resource_exists('User'))


@fixtures.user(email='common@email.com')
@fixtures.user(email='other@email.com')
def test_put_email_already_exists_then_error_raised(_, user):
    response = confd.users(user['uuid']).put(email='common@email.com')
    response.assert_match(400, e.resource_exists('User'))
