import copy, grpc, logging, pytest
from google.protobuf.json_format import MessageToDict
#from common.database.api.context.Constants import DEFAULT_CONTEXT_ID, DEFAULT_TOPOLOGY_ID
#from common.tests.Assertions import validate_empty, validate_service, validate_service_id, \
#    validate_service_list_is_empty, validate_service_list_is_not_empty
from service.Config import GRPC_SERVICE_PORT, GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD
from service.client.ServiceClient import ServiceClient
from service.proto.context_pb2 import Service
from service.service.ServiceService import ServiceService

port = 10000 + GRPC_SERVICE_PORT # avoid privileged ports

LOGGER = logging.getLogger(__name__)
LOGGER.setLevel(logging.DEBUG)

## use "copy.deepcopy" to prevent propagating forced changes during tests
#CONTEXT_ID = {'contextUuid': {'uuid': DEFAULT_CONTEXT_ID}}
#TOPOLOGY_ID = {'contextId': copy.deepcopy(CONTEXT_ID), 'topoId': {'uuid': DEFAULT_TOPOLOGY_ID}}
#SERVICE_ID = {'contextId': copy.deepcopy(CONTEXT_ID), 'cs_id': {'uuid': 'DEV1'}}
#SERVICE = {
#    'cs_id': copy.deepcopy(SERVICE_ID),
#    'serviceType': ServiceType.L3NM,
#    'serviceConfig': {'serviceConfig': '<config/>'},
#    'serviceState': {'serviceState': ServiceStateEnum.PLANNED},
#    'constraint': [
#        {'constraint_type': 'latency_ms', 'constraint_value': '100'},
#        {'constraint_type': 'hops', 'constraint_value': '5'},
#    ],
#    'endpointList' : [
#        {'topoId': copy.deepcopy(TOPOLOGY_ID), 'dev_id': {'device_id': {'uuid': 'DEV1'}}, 'port_id': {'uuid' : 'EP5'}},
#        {'topoId': copy.deepcopy(TOPOLOGY_ID), 'dev_id': {'device_id': {'uuid': 'DEV2'}}, 'port_id': {'uuid' : 'EP5'}},
#        {'topoId': copy.deepcopy(TOPOLOGY_ID), 'dev_id': {'device_id': {'uuid': 'DEV3'}}, 'port_id': {'uuid' : 'EP5'}},
#    ]
#}

@pytest.fixture(scope='session')
def service_service():
    _service = ServiceService(port=port, max_workers=GRPC_MAX_WORKERS, grace_period=GRPC_GRACE_PERIOD)
    _service.start()
    yield _service
    _service.stop()

@pytest.fixture(scope='session')
def service_client(service_service):
    _client = ServiceClient(address='127.0.0.1', port=port)
    yield _client
    _client.close()

def test_dummy(service_client : ServiceClient):
    reply = service_client.CreateService(Service())

#def test_get_services_empty(service_client : ServiceClient):
#    # should work
#    validate_service_list_is_empty(MessageToDict(
#        service_client.GetServiceList(Empty()),
#        including_default_value_fields=True, preserving_proto_field_name=True,
#        use_integers_for_enums=False))

#def test_create_service_wrong_service_attributes(service_client : ServiceClient):
#    # should fail with wrong service context
#    with pytest.raises(grpc._channel._InactiveRpcError) as e:
#        copy_service = copy.deepcopy(SERVICE)
#        copy_service['cs_id']['contextId']['contextUuid']['uuid'] = ''
#        service_client.CreateService(Service(**copy_service))
#    assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
#    msg = 'service.cs_id.contextId.contextUuid.uuid() is out of range: '\
#          'allow_empty(False) min_length(None) max_length(None) allowed_lengths(None).'
#    assert e.value.details() == msg
#
#    # should fail with service context does not exist
#    with pytest.raises(grpc._channel._InactiveRpcError) as e:
#        copy_service = copy.deepcopy(SERVICE)
#        copy_service['cs_id']['contextId']['contextUuid']['uuid'] = 'wrong-context'
#        service_client.CreateService(Service(**copy_service))
#    assert e.value.code() == grpc.StatusCode.NOT_FOUND
#    msg = 'Context(wrong-context) does not exist in the database.'
#    assert e.value.details() == msg
#
#    # should fail with wrong service id
#    with pytest.raises(grpc._channel._InactiveRpcError) as e:
#        copy_service = copy.deepcopy(SERVICE)
#        copy_service['cs_id']['cs_id']['uuid'] = ''
#        service_client.CreateService(Service(**copy_service))
#    assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
#    msg = 'service.cs_id.cs_id.uuid() is out of range: '\
#          'allow_empty(False) min_length(None) max_length(None) allowed_lengths(None).'
#    assert e.value.details() == msg
#
#    # should fail with wrong service type
#    with pytest.raises(grpc._channel._InactiveRpcError) as e:
#        copy_service = copy.deepcopy(SERVICE)
#        copy_service['serviceType'] = ServiceType.UNKNOWN
#        service_client.CreateService(Service(**copy_service))
#    assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
#    msg = 'Method(CreateService) does not accept ServiceType(UNKNOWN). '\
#          'Permitted values for Method(CreateService) are '\
#          'ServiceType([\'L2NM\', \'L3NM\', \'TAPI_CONNECTIVITY_SERVICE\']).'
#    assert e.value.details() == msg
#
#    # should fail with wrong service state
#    with pytest.raises(grpc._channel._InactiveRpcError) as e:
#        copy_service = copy.deepcopy(SERVICE)
#        copy_service['serviceState']['serviceState'] = ServiceStateEnum.PENDING_REMOVAL
#        service_client.CreateService(Service(**copy_service))
#    assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
#    msg = 'Method(CreateService) does not accept ServiceState(PENDING_REMOVAL). '\
#          'Permitted values for Method(CreateService) are '\
#          'ServiceState([\'PLANNED\']).'
#    assert e.value.details() == msg
#
#def test_create_service_wrong_constraint(service_client : ServiceClient):
#    # should fail with wrong constraint type
#    with pytest.raises(grpc._channel._InactiveRpcError) as e:
#        copy_service = copy.deepcopy(SERVICE)
#        copy_service['constraint'][0]['constraint_type'] = ''
#        service_client.CreateService(Service(**copy_service))
#    assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
#    msg = 'constraint[#0].constraint_type() is out of range: '\
#          'allow_empty(False) min_length(None) max_length(None) allowed_lengths(None).'
#    assert e.value.details() == msg
#
#    # should fail with wrong constraint value
#    with pytest.raises(grpc._channel._InactiveRpcError) as e:
#        copy_service = copy.deepcopy(SERVICE)
#        copy_service['constraint'][0]['constraint_value'] = ''
#        service_client.CreateService(Service(**copy_service))
#    assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
#    msg = 'constraint[#0].constraint_value() is out of range: '\
#          'allow_empty(False) min_length(None) max_length(None) allowed_lengths(None).'
#    assert e.value.details() == msg
#
#    # should fail with dupplicated constraint type
#    with pytest.raises(grpc._channel._InactiveRpcError) as e:
#        copy_service = copy.deepcopy(SERVICE)
#        copy_service['constraint'][1] = copy_service['constraint'][0]
#        service_client.CreateService(Service(**copy_service))
#    assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
#    msg = 'Duplicated ConstraintType(latency_ms) in Constraint(#1) of Context(admin)/Service(DEV1).'
#    assert e.value.details() == msg
#
#def test_create_service_wrong_endpoint(service_client : ServiceClient, database : Database):
#    # should fail with wrong endpoint context
#    with pytest.raises(grpc._channel._InactiveRpcError) as e:
#        copy_service = copy.deepcopy(SERVICE)
#        copy_service['endpointList'][0]['topoId']['contextId']['contextUuid']['uuid'] = 'wrong-context'
#        print(copy_service)
#        service_client.CreateService(Service(**copy_service))
#    assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
#    msg = 'Context(wrong-context) in Endpoint(#0) of '\
#          'Context(admin)/Service(DEV1) mismatches acceptable Contexts({\'admin\'}). '\
#          'Optionally, leave field empty to use predefined Context(admin).'
#    assert e.value.details() == msg
#
#    # should fail with wrong endpoint topology
#    with pytest.raises(grpc._channel._InactiveRpcError) as e:
#        copy_service = copy.deepcopy(SERVICE)
#        copy_service['endpointList'][0]['topoId']['topoId']['uuid'] = 'wrong-topo'
#        service_client.CreateService(Service(**copy_service))
#    assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
#    msg = 'Context(admin)/Topology(wrong-topo) in Endpoint(#0) of '\
#          'Context(admin)/Service(DEV1) mismatches acceptable Topologies({\'admin\'}). '\
#          'Optionally, leave field empty to use predefined Topology(admin).'
#    assert e.value.details() == msg
#
#    # should fail with endpoint device is empty
#    with pytest.raises(grpc._channel._InactiveRpcError) as e:
#        copy_service = copy.deepcopy(SERVICE)
#        copy_service['endpointList'][0]['dev_id']['device_id']['uuid'] = ''
#        service_client.CreateService(Service(**copy_service))
#    assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
#    msg = 'endpoint_id[#0].dev_id.device_id.uuid() is out of range: '\
#          'allow_empty(False) min_length(None) max_length(None) allowed_lengths(None).'
#    assert e.value.details() == msg
#
#    # should fail with endpoint device not found
#    with pytest.raises(grpc._channel._InactiveRpcError) as e:
#        copy_service = copy.deepcopy(SERVICE)
#        copy_service['endpointList'][0]['dev_id']['device_id']['uuid'] = 'wrong-device'
#        service_client.CreateService(Service(**copy_service))
#    assert e.value.code() == grpc.StatusCode.NOT_FOUND
#    msg = 'Context(admin)/Topology(admin)/Device(wrong-device) in Endpoint(#0) of '\
#          'Context(admin)/Service(DEV1) does not exist in the database.'
#    assert e.value.details() == msg
#
#    # should fail with endpoint device duplicated
#    with pytest.raises(grpc._channel._InactiveRpcError) as e:
#        copy_service = copy.deepcopy(SERVICE)
#        copy_service['endpointList'][1] = copy_service['endpointList'][0]
#        service_client.CreateService(Service(**copy_service))
#    assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
#    msg = 'Duplicated Context(admin)/Topology(admin)/Device(DEV1) in Endpoint(#1) of '\
#          'Context(admin)/Service(DEV1).'
#    assert e.value.details() == msg
#
#    # should fail with endpoint port is empty
#    with pytest.raises(grpc._channel._InactiveRpcError) as e:
#        copy_service = copy.deepcopy(SERVICE)
#        copy_service['endpointList'][0]['port_id']['uuid'] = ''
#        service_client.CreateService(Service(**copy_service))
#    assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
#    msg = 'endpoint_id[#0].port_id.uuid() is out of range: '\
#          'allow_empty(False) min_length(None) max_length(None) allowed_lengths(None).'
#    assert e.value.details() == msg
#
#    # should fail with endpoint port not found
#    with pytest.raises(grpc._channel._InactiveRpcError) as e:
#        copy_service = copy.deepcopy(SERVICE)
#        copy_service['endpointList'][0]['port_id']['uuid'] = 'wrong-port'
#        service_client.CreateService(Service(**copy_service))
#    assert e.value.code() == grpc.StatusCode.NOT_FOUND
#    msg = 'Context(admin)/Topology(admin)/Device(DEV1)/Port(wrong-port) in Endpoint(#0) of '\
#          'Context(admin)/Service(DEV1) does not exist in the database.'
#    assert e.value.details() == msg
#
#def test_get_service_does_not_exist(service_client : ServiceClient):
#    # should fail with service context does not exist
#    with pytest.raises(grpc._channel._InactiveRpcError) as e:
#        copy_service_id = copy.deepcopy(SERVICE_ID)
#        copy_service_id['contextId']['contextUuid']['uuid'] = 'wrong-context'
#        service_client.GetServiceById(ServiceId(**copy_service_id))
#    assert e.value.code() == grpc.StatusCode.NOT_FOUND
#    msg = 'Context(wrong-context) does not exist in the database.'
#    assert e.value.details() == msg
#
#    # should fail with service does not exist
#    with pytest.raises(grpc._channel._InactiveRpcError) as e:
#        service_client.GetServiceById(ServiceId(**SERVICE_ID))
#    assert e.value.code() == grpc.StatusCode.NOT_FOUND
#    msg = 'Context(admin)/Service(DEV1) does not exist in the database.'
#    assert e.value.details() == msg
#
#def test_update_service_does_not_exist(service_client : ServiceClient):
#    # should fail with service does not exist
#    with pytest.raises(grpc._channel._InactiveRpcError) as e:
#        service_client.UpdateService(Service(**SERVICE))
#    assert e.value.code() == grpc.StatusCode.NOT_FOUND
#    msg = 'Context(admin)/Service(DEV1) does not exist in the database.'
#    assert e.value.details() == msg
#
#def test_create_service(service_client : ServiceClient):
#    # should work
#    validate_service_id(MessageToDict(
#        service_client.CreateService(Service(**SERVICE)),
#        including_default_value_fields=True, preserving_proto_field_name=True,
#        use_integers_for_enums=False))
#
#def test_create_service_already_exists(service_client : ServiceClient):
#    # should fail with service already exists
#    with pytest.raises(grpc._channel._InactiveRpcError) as e:
#        service_client.CreateService(Service(**SERVICE))
#    assert e.value.code() == grpc.StatusCode.ALREADY_EXISTS
#    msg = 'Context(admin)/Service(DEV1) already exists in the database.'
#    assert e.value.details() == msg
#
#def test_get_service(service_client : ServiceClient):
#    # should work
#    validate_service(MessageToDict(
#        service_client.GetServiceById(ServiceId(**SERVICE_ID)),
#        including_default_value_fields=True, preserving_proto_field_name=True,
#        use_integers_for_enums=False))
#
#def test_update_service(service_client : ServiceClient):
#    # should work
#    copy_service = copy.deepcopy(SERVICE)
#    copy_service['serviceConfig']['serviceConfig'] = '<newconfig/>'
#    copy_service['serviceState']['serviceState'] = ServiceStateEnum.ACTIVE
#    copy_service['constraint'] = [
#        {'constraint_type': 'latency_ms', 'constraint_value': '200'},
#        {'constraint_type': 'bandwidth_gbps', 'constraint_value': '100'},
#    ]
#    copy_service['endpointList'] = [
#        {
#            'topoId': {'contextId': {'contextUuid': {'uuid': 'admin'}}, 'topoId': {'uuid': 'admin'}},
#            'dev_id': {'device_id': {'uuid': 'DEV1'}},
#            'port_id': {'uuid' : 'EP5'}
#        },
#        {
#            'topoId': {'contextId': {'contextUuid': {'uuid': 'admin'}}, 'topoId': {'uuid': 'admin'}},
#            'dev_id': {'device_id': {'uuid': 'DEV2'}},
#            'port_id': {'uuid' : 'EP6'}
#        },
#    ]
#    validate_service_id(MessageToDict(
#        service_client.UpdateService(Service(**copy_service)),
#        including_default_value_fields=True, preserving_proto_field_name=True,
#        use_integers_for_enums=False))
#
#def test_delete_service_wrong_service_id(service_client : ServiceClient):
#    # should fail with service context is empty
#    with pytest.raises(grpc._channel._InactiveRpcError) as e:
#        copy_service_id = copy.deepcopy(SERVICE_ID)
#        copy_service_id['contextId']['contextUuid']['uuid'] = ''
#        service_client.DeleteService(ServiceId(**copy_service_id))
#    assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
#    msg = 'service_id.contextId.contextUuid.uuid() is out of range: '\
#          'allow_empty(False) min_length(None) max_length(None) allowed_lengths(None).'
#    assert e.value.details() == msg
#
#    # should fail with service context does not exist
#    with pytest.raises(grpc._channel._InactiveRpcError) as e:
#        copy_service_id = copy.deepcopy(SERVICE_ID)
#        copy_service_id['contextId']['contextUuid']['uuid'] = 'wrong-context'
#        service_client.DeleteService(ServiceId(**copy_service_id))
#    assert e.value.code() == grpc.StatusCode.NOT_FOUND
#    msg = 'Context(wrong-context) does not exist in the database.'
#    assert e.value.details() == msg
#
#    # should fail with service id is empty
#    with pytest.raises(grpc._channel._InactiveRpcError) as e:
#        copy_service_id = copy.deepcopy(SERVICE_ID)
#        copy_service_id['cs_id']['uuid'] = ''
#        service_client.DeleteService(ServiceId(**copy_service_id))
#    assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
#    msg = 'service_id.cs_id.uuid() is out of range: '\
#          'allow_empty(False) min_length(None) max_length(None) allowed_lengths(None).'
#    assert e.value.details() == msg
#
#    # should fail with service id is empty
#    with pytest.raises(grpc._channel._InactiveRpcError) as e:
#        copy_service_id = copy.deepcopy(SERVICE_ID)
#        copy_service_id['cs_id']['uuid'] = 'wrong-service'
#        service_client.DeleteService(ServiceId(**copy_service_id))
#    assert e.value.code() == grpc.StatusCode.NOT_FOUND
#    msg = 'Context(admin)/Service(wrong-service) does not exist in the database.'
#    assert e.value.details() == msg
#
#def test_delete_service(service_client : ServiceClient):
#    # should work
#    validate_empty(MessageToDict(
#        service_client.DeleteService(ServiceId(**SERVICE_ID)),
#        including_default_value_fields=True, preserving_proto_field_name=True,
#        use_integers_for_enums=False))
#
#def test_get_services_empty_2(service_client : ServiceClient):
#    # should work
#    validate_service_list_is_empty(MessageToDict(
#        service_client.GetServiceList(Empty()),
#        including_default_value_fields=True, preserving_proto_field_name=True,
#        use_integers_for_enums=False))
#
#def test_create_service_empty_endpoints(service_client : ServiceClient):
#    # should work
#    copy_service = copy.deepcopy(SERVICE)
#    copy_service['endpointList'][0]['topoId']['contextId']['contextUuid']['uuid'] = ''
#    copy_service['endpointList'][0]['topoId']['topoId']['uuid'] = ''
#    validate_service_id(MessageToDict(
#        service_client.CreateService(Service(**copy_service)),
#        including_default_value_fields=True, preserving_proto_field_name=True,
#        use_integers_for_enums=False))
#
#def test_get_services_full(service_client : ServiceClient):
#    # should work
#    validate_service_list_is_not_empty(MessageToDict(
#        service_client.GetServiceList(Empty()),
#        including_default_value_fields=True, preserving_proto_field_name=True,
#        use_integers_for_enums=False))
#