# Copyright (c) 2019 Riverbed Technology, Inc.
#
# This software is licensed under the terms and conditions of the MIT License
# accompanying the software ("License").  This software is distributed "AS IS"
# as set forth in the License.

from collections import OrderedDict

import mock
import pytest

from sleepwalker import service
from sleepwalker.exceptions import (ResourceException,
                                    TypeException, LinkError)
from reschema import servicedef

ANY_HOSTNAME = 'http://hostname.nbttech.com'
ANY_AUTH = ('username', 'password')
ANY_PORT = 1234
NON_DEFAULT_VERIFY = False

ANY_BIND_KWARGS = {'foo': 'bar', 'x': 1}

ANY_ID = 'http://support.riverbed.com/apis/test/1.0'
ANY_NAME = 'test'
ANY_VERSION = '1.0'

ANY_SCHEMA_FILENAME = 'any_schema.yml'
ANY_TYPE_NAME = 'any_type_name'
ANY_RESOURCE_NAME = 'any_resource_name'
ANY_RESOURCE_JSONSCHEMA = OrderedDict({ANY_RESOURCE_NAME: {}})
ANY_RESOURCE_LOOKUP_FUNCTION = mock.MagicMock(
    return_value=ANY_RESOURCE_JSONSCHEMA)
# This exception needs to be specific to sleepwalker.
ANY_RESOURCE_LOOKUP_EXCEPTION = ResourceException


@pytest.fixture
def any_service():
    with mock.patch('reschema.servicedef.ServiceDef') as patched:
        mock_servicedef = patched.return_value
        mock_servicedef.id = ANY_ID
        mock_servicedef.name = ANY_NAME
        mock_servicedef.version = ANY_VERSION
    return service.Service(mock_servicedef, ANY_HOSTNAME)


def test_service_instantiation(any_service):
    assert any_service.connection is None


def test_response_no_connection(any_service):
    assert any_service.response is None


def test_response_no_response(any_service):
    any_service.connection = mock.MagicMock(return_value=None)
    any_service.connection.response = None
    assert any_service.response is None


def test_service_bind_no_schema(any_service):
    with pytest.raises(LinkError):
        any_service.bind(ANY_RESOURCE_NAME)


# XXXCJ - this now fails and the bind(ANY_RESOURCE_NAME), but I don't
# really understand what they should be doing.
#
# Failure is:
#    test_service.py:89: in test_service_bind
#    > mock_service.bind(ANY_RESOURCE_NAME)
#    ../sleepwalker/service.py:140: in bind
#    > return schema.bind(**kwargs)
#    ../sleepwalker/datarep.py:319: in bind
#    > raise LinkError("Cannot bind a schema that has no 'self' link")
#    E LinkError: Cannot bind a schema that has no 'self' link
@pytest.mark.xfail
def test_service_bind(any_service):
    with mock.patch('reschema.servicedef.ServiceDef') as patched_servicedef:
        with mock.patch('reschema.jsonschema.Schema') as patched_schema:
            empty_dict = OrderedDict()
            mock_servicedef = patched_servicedef.return_value
            mock_servicedef.find_resource = mock.Mock(return_value=empty_dict)
            mock_schema = patched_schema.return_value

            mock_service = service.Service(patched_servicedef)
            mock_service.bind(ANY_RESOURCE_NAME)
            mock_schema.bind.assert_called_with()

            mock_service.bind(ANY_RESOURCE_NAME, **ANY_BIND_KWARGS)
            mock_schema.bind.assert_called_with(**ANY_BIND_KWARGS)


def test__lookup_cant_find_name(any_service):
    with mock.patch('reschema.servicedef.ServiceDef'):
        mock_lookup_keyerror = mock.Mock(side_effect=KeyError)
        with pytest.raises(ANY_RESOURCE_LOOKUP_EXCEPTION):
            any_service._lookup(ANY_RESOURCE_NAME, mock_lookup_keyerror,
                                ANY_RESOURCE_LOOKUP_EXCEPTION)


# XXXCJ - this also fails but don't know why
#
# Failure:
#    test_service.py:124: in test__lookup_success
#    >               assert schema is mock_schema
#
#    E               assert <[AttributeError("'OrderedDict' object has no
#                       attribute 'links'") raised in repr()] SafeRepr object
#                       at 0x106a4d248> is <MagicMock name='Schema()'
#                       id='4408401808'>
@pytest.mark.xfail
def test__lookup_success(any_service):
    with mock.patch('reschema.servicedef.ServiceDef'):
        with mock.patch('reschema.jsonschema.Schema') as patched_schema:
            mock_schema = patched_schema.return_value
            schema = any_service._lookup(ANY_RESOURCE_NAME,
                                         ANY_RESOURCE_LOOKUP_FUNCTION,
                                         ANY_RESOURCE_LOOKUP_EXCEPTION)
            assert schema is mock_schema
            patched_schema.assert_called_with(any_service,
                                              ANY_RESOURCE_JSONSCHEMA)


def test_lookup_resource(any_service):
    any_service._lookup = mock.Mock()
    any_service.servicedef = mock.Mock(servicedef.ServiceDef)
    any_service.servicedef.find_resource = mock.Mock(
        return_value=OrderedDict())
    any_service.lookup_resource(ANY_RESOURCE_NAME)
    any_service._lookup.assert_called_once_with(
        ANY_RESOURCE_NAME,
        any_service.servicedef.find_resource,
        ResourceException)


def test_lookup_type(any_service):
    any_service._lookup = mock.Mock()
    any_service.servicedef = mock.Mock(servicedef.ServiceDef)
    any_service.servicedef.find_type = mock.Mock(return_value=OrderedDict())
    any_service.lookup_type(ANY_TYPE_NAME)
    any_service._lookup.assert_called_once_with(
        ANY_TYPE_NAME,
        any_service.servicedef.find_type,
        TypeException)
