import logging, pytest
from google.protobuf.json_format import MessageToDict
from common.database.Factory import get_database, DatabaseEngineEnum
from common.database.api.Database import Database
from common.tests.Assertions import validate_device_id, validate_link_id, validate_service_id, \
    validate_service_list_is_not_empty, validate_topology_has_devices, validate_topology_has_links, \
    validate_topology_is_empty
from context.Config import GRPC_SERVICE_PORT as GRPC_CONTEXT_SERVICE_PORT, \
    GRPC_MAX_WORKERS as GRPC_CONTEXT_MAX_WORKERS, GRPC_GRACE_PERIOD as GRPC_CONTEXT_GRACE_PERIOD
from context.client.ContextClient import ContextClient
from context.proto.context_pb2 import Device, Empty, Link
from context.service.ContextService import ContextService
from device.Config import GRPC_SERVICE_PORT as GRPC_DEVICE_SERVICE_PORT, \
    GRPC_MAX_WORKERS as GRPC_DEVICE_MAX_WORKERS, GRPC_GRACE_PERIOD as GRPC_DEVICE_GRACE_PERIOD
from device.client.DeviceClient import DeviceClient
from device.service.DeviceService import DeviceService
from service.service.ServiceService import ServiceService
from tester_integration.definitions import DEVICE_DEV1, DEVICE_DEV2, DEVICE_DEV3
from tester_integration.definitions import LINK_DEV1_DEV2, LINK_DEV1_DEV3, LINK_DEV2_DEV1, LINK_DEV2_DEV3, \
    LINK_DEV3_DEV1, LINK_DEV3_DEV2
from tester_integration.definitions import SERVICE_SVC1, SERVICE_SVC2, SERVICE_SVC3
from service.Config import GRPC_SERVICE_PORT as GRPC_SERVICE_SERVICE_PORT, \
    GRPC_MAX_WORKERS as GRPC_SERVICE_MAX_WORKERS, GRPC_GRACE_PERIOD as GRPC_SERVICE_GRACE_PERIOD
from service.client.ServiceClient import ServiceClient
from service.proto.service_pb2 import Service

PORT_CONTEXT = 10000 + GRPC_CONTEXT_SERVICE_PORT # avoid privileged ports
PORT_DEVICE  = 10000 + GRPC_DEVICE_SERVICE_PORT  # avoid privileged ports
PORT_SERVICE = 10000 + GRPC_SERVICE_SERVICE_PORT # avoid privileged ports

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

@pytest.fixture(scope='session')
def redis_database():
    _database = get_database(engine=DatabaseEngineEnum.REDIS, REDIS_DATABASE_ID=0)
    return _database

@pytest.fixture(scope='session')
def context_service(redis_database : Database):
    context_database = get_database(engine=DatabaseEngineEnum.REDIS, REDIS_DATABASE_ID=0)
    _service = ContextService(
        context_database, port=PORT_CONTEXT, max_workers=GRPC_CONTEXT_MAX_WORKERS,
        grace_period=GRPC_CONTEXT_GRACE_PERIOD)
    _service.start()
    yield _service
    _service.stop()

@pytest.fixture(scope='session')
def context_client(context_service):
    _client = ContextClient(address='127.0.0.1', port=PORT_CONTEXT)
    yield _client
    _client.close()

@pytest.fixture(scope='session')
def device_service(redis_database : Database):
    device_database = get_database(engine=DatabaseEngineEnum.REDIS, REDIS_DATABASE_ID=0)
    _service = DeviceService(
        device_database, port=PORT_DEVICE, max_workers=GRPC_DEVICE_MAX_WORKERS,
        grace_period=GRPC_DEVICE_GRACE_PERIOD)
    _service.start()
    yield _service
    _service.stop()

@pytest.fixture(scope='session')
def device_client(device_service):
    _client = DeviceClient(address='127.0.0.1', port=PORT_DEVICE)
    yield _client
    _client.close()

@pytest.fixture(scope='session')
def service_service(redis_database : Database):
    service_database = get_database(engine=DatabaseEngineEnum.REDIS, REDIS_DATABASE_ID=0)
    _service = ServiceService(
        service_database, port=PORT_SERVICE, max_workers=GRPC_SERVICE_MAX_WORKERS,
        grace_period=GRPC_SERVICE_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_SERVICE)
    yield _client
    _client.close()

def test_clean_database(redis_database : Database):
    # should work
    redis_database.clear_all()

def test_get_topology_empty(context_client : ContextClient):
    # should work
    validate_topology_is_empty(MessageToDict(
        context_client.GetTopology(Empty()),
        including_default_value_fields=True, preserving_proto_field_name=True,
        use_integers_for_enums=False))

def test_add_devices(context_client : ContextClient, device_client : DeviceClient):
    # should work
    for device in [DEVICE_DEV1, DEVICE_DEV2, DEVICE_DEV3]:
        validate_device_id(MessageToDict(
            device_client.AddDevice(Device(**device)),
            including_default_value_fields=True, preserving_proto_field_name=True,
            use_integers_for_enums=False))

    # should work
    validate_topology_has_devices(MessageToDict(
        context_client.GetTopology(Empty()),
        including_default_value_fields=True, preserving_proto_field_name=True,
        use_integers_for_enums=False))

def test_add_links(context_client : ContextClient):
    # should work
    for link in [LINK_DEV1_DEV2, LINK_DEV1_DEV3, LINK_DEV2_DEV1, LINK_DEV2_DEV3, LINK_DEV3_DEV1, LINK_DEV3_DEV2]:
        validate_link_id(MessageToDict(
            context_client.AddLink(Link(**link)),
            including_default_value_fields=True, preserving_proto_field_name=True,
            use_integers_for_enums=False))

    # should work
    validate_topology_has_links(MessageToDict(
        context_client.GetTopology(Empty()),
        including_default_value_fields=True, preserving_proto_field_name=True,
        use_integers_for_enums=False))

def test_add_services(service_client : ServiceClient):
    # should work
    for service in [SERVICE_SVC1, SERVICE_SVC2, SERVICE_SVC3]:
        validate_service_id(MessageToDict(
            service_client.CreateService(Service(**service)),
            including_default_value_fields=True, preserving_proto_field_name=True,
            use_integers_for_enums=False))

    # 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))
