"""
End2End test of Host and EdgeNode.
Tested with all common infrastructure: real MQTT Broker, real FSHistorical,
rest InMemory implementations
"""
import asyncio
import os

import pytest
from testcontainers.core.container import DockerContainer
from testcontainers.core.wait_strategies import LogMessageWaitStrategy

from aiospb.bootstrap import new_edge_of_node, new_host
from aiospb.infra.paho import MQTTConfig
from aiospb.messages import Event, Message
from aiospb.nodes.values import MetricInfo
from aiospb.shared import (
    DataType,
    DeviceKey,
    HostOptions,
    NodeOptions,
    PropertySet,
    PropertyValue,
)
from tests.fakes import EasyMetricsNet

CONFIG_DIR = os.path.join(os.path.dirname(__file__), "test_data", "mosquitto")


@pytest.fixture
def mqtt_config(mosquitto: DockerContainer):
    return MQTTConfig(
        mosquitto.get_container_host_ip(),
        int(mosquitto.get_exposed_port(1883)),
        "test_user",
        "password",
    )


@pytest.fixture(scope="module")
def mosquitto():
    # Initialize the Mosquitto container using the latest official image
    mosquitto = DockerContainer("eclipse-mosquitto:latest")

    mosquitto.with_volume_mapping(
        host=CONFIG_DIR, container="/mosquitto/config", mode="ro"  # Read-only mount
    )
    mosquitto.with_exposed_ports(1883)

    mosquitto.start()
    mosquitto.waiting_for(LogMessageWaitStrategy("mosquitto version .* running"))

    yield mosquitto

    mosquitto.stop()


@pytest.mark.usefixtures("mosquitto")
async def test_host_and_edge_node(tmp_path, mqtt_config):
    metrics_net = EasyMetricsNet(
        {
            DeviceKey("group/node"): [
                MetricInfo(
                    "Room/Temperature",
                    DataType.Int16,
                    PropertySet({"unit": PropertyValue("dgradC", DataType.String)}),
                    alias=1,
                )
            ]
        }
    )
    node = new_edge_of_node(
        "group/node",
        mqtt_config,
        NodeOptions(str(tmp_path), scan_rate=200),
        metrics_net,
    )
    await node.establish_session()

    queue = asyncio.Queue()

    async def observe(msg: Message):
        await queue.put(msg)

    host = new_host("hostname", mqtt_config, HostOptions())
    host.add_observer("cus", observe)
    await host.establish_session()
    messages = []

    msg = await asyncio.wait_for(queue.get(), timeout=5.0)
    assert msg.event == Event.NODE_IS_BORN
    messages.append(msg)

    await host.terminate_session()

    await metrics_net.write(DeviceKey("group/node"), 100, alias=1)

    await asyncio.sleep(0.4)

    await host.establish_session()

    msg = await asyncio.wait_for(queue.get(), timeout=5.0)
    assert msg.event == Event.NODE_IS_BORN
    messages.append(msg)

    await metrics_net.write(DeviceKey("group/node"), 50, alias=1)

    msg = await asyncio.wait_for(queue.get(), timeout=5.0)
    assert msg.event == Event.NODE_DATA_HAS_CHANGED
    messages.append(msg)
    assert len(msg.metrics) == 2
    assert msg.metrics[0].is_historical

    await host.terminate_session()
    await node.terminate_session()
