from __future__ import annotations

import asyncio
import json
import socket

import pytest

pytest.importorskip("asyncua")

from cf_opcua_server import DEFAULT_NODE_MAP, read_nodes_once, read_nodes_once_json_sync, read_nodes_once_sync, run_server


def _reserve_port() -> int:
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
        sock.bind(("127.0.0.1", 0))
        return int(sock.getsockname()[1])


def test_read_apis_work_against_virtual_server() -> None:
    async def _scenario() -> None:
        port = _reserve_port()
        endpoint = f"opc.tcp://127.0.0.1:{port}/VirtualPhServer"
        ready_event = asyncio.Event()
        stop_event = asyncio.Event()
        server_task = asyncio.create_task(
            run_server(
                loop_interval=0.05,
                endpoint=endpoint,
                bind_endpoint=endpoint,
                stop_event=stop_event,
                ready_event=ready_event,
            )
        )

        try:
            await asyncio.wait_for(ready_event.wait(), timeout=10)
            await asyncio.sleep(0.15)

            payload = await read_nodes_once(endpoint, DEFAULT_NODE_MAP, timeout=2.0)
            assert set(payload) == set(DEFAULT_NODE_MAP)
            assert isinstance(payload["pH"], float)
            assert isinstance(payload["temperature"], float)
            assert isinstance(payload["timestamp"], str)
            assert payload["timestamp"]

            sync_payload = await asyncio.to_thread(
                read_nodes_once_sync,
                endpoint,
                {"pH": DEFAULT_NODE_MAP["pH"]},
                timeout=2.0,
            )
            assert isinstance(sync_payload["pH"], float)

            compact_payload = await asyncio.to_thread(
                read_nodes_once_json_sync,
                endpoint,
                {"pH": DEFAULT_NODE_MAP["pH"]},
                timeout=2.0,
            )
            decoded = json.loads(compact_payload)
            assert isinstance(decoded["pH"], float)
        finally:
            stop_event.set()
            await asyncio.wait_for(server_task, timeout=10)

    asyncio.run(_scenario())
