import time
from threading import Thread

import cmsgbus
import msgbus
from assertpy import assert_that

_SERVER_IP = "127.0.0.1"
_SENDER_PORT = 14583
_RECEIVER_PORT = 14582
_DEST_NAME = "large-data service"
_MAX_ELAPSED_SECONDS = 0.2
_PAYLOAD_SIZE = 5 * 1024 * 1024


def test_large_message_from_python_is_received_on_msgbus():
    sender = msgbus.MBContext(
        my_service=msgbus.MBService(
            address=_SERVER_IP,
            port=_SENDER_PORT,
        ),
        name="sender",
    )
    receiver = cmsgbus.MBContext(
        my_service=cmsgbus.MBService(
            address=_SERVER_IP,
            port=_RECEIVER_PORT,
        ),
        name="receiver",
    )

    _send_and_receive(
        sender=sender,
        destination=msgbus.MBService(
            address=_SERVER_IP,
            port=_RECEIVER_PORT,
            name=_DEST_NAME,
        ),
        receiver=receiver,
    )

    sender.MB_Finalize()
    receiver.MB_Finalize()


def test_large_message_from_msgbus_is_received_on_python():
    sender = cmsgbus.MBContext(
        my_service=cmsgbus.MBService(
            address=_SERVER_IP,
            port=_SENDER_PORT,
        ),
        name="sender",
    )
    receiver = msgbus.MBContext(
        my_service=msgbus.MBService(
            address=_SERVER_IP,
            port=_RECEIVER_PORT,
        ),
        name="receiver",
    )

    _send_and_receive(
        sender=sender,
        destination=cmsgbus.MBService(
            address=_SERVER_IP,
            port=_RECEIVER_PORT,
            pservice=_DEST_NAME,
        ),
        receiver=receiver,
    )

    sender.MB_Finalize()
    receiver.MB_Finalize()


def test_large_message_from_python_is_received_on_python():
    sender = msgbus.MBContext(
        my_service=msgbus.MBService(
            address=_SERVER_IP,
            port=_SENDER_PORT,
        ),
        name="sender",
    )
    receiver = msgbus.MBContext(
        my_service=msgbus.MBService(
            address=_SERVER_IP,
            port=_RECEIVER_PORT,
        ),
        name="receiver",
    )

    _send_and_receive(
        sender=sender,
        destination=msgbus.MBService(
            address=_SERVER_IP,
            port=_RECEIVER_PORT,
            name=_DEST_NAME,
        ),
        receiver=receiver,
    )

    sender.MB_Finalize()
    receiver.MB_Finalize()


def _send_and_receive(sender, destination, receiver):
    sent_data = b"x" * _PAYLOAD_SIZE
    reply_data = b"ok"

    def receive_thread():
        received = receiver.MB_GetMessage()
        assert_that(received.data).is_equal_to(sent_data)
        received.token = msgbus.TK_SYS_ACK
        receiver.MB_ReplyMessage(
            message=received,
            format=msgbus.FM_STRING,
            data=reply_data,
        )

    t = Thread(target=receive_thread)
    t.start()

    start = time.monotonic()
    reply = sender.MB_SendMessage(
        destination=destination,
        token=msgbus.TK_CMP_GETSERVICES,
        format=msgbus.FM_STRING,
        data=sent_data,
        timeout=int(_MAX_ELAPSED_SECONDS * 1_000_000_000),
    )
    elapsed = time.monotonic() - start

    t.join()

    assert_that(reply.token).is_equal_to(msgbus.TK_SYS_ACK)
    assert_that(reply.data).is_equal_to(reply_data)
    assert_that(elapsed).is_less_than(_MAX_ELAPSED_SECONDS)
