"""US-010 integration tests: typed meshnet SDK over local gateway/tracker/nodes."""

import json
import urllib.request

from meshnet import Client
from meshnet_contracts import LocalSolanaContracts
from meshnet_gateway.server import GatewayServer, _payment_address_for_api_key
from meshnet_node.server import StubNodeServer
from meshnet_tracker.server import TrackerServer, _NodeEntry, _coverage_percentage


def test_sdk_support_helpers_are_deterministic_without_network():
    "Non-socket coverage for SDK-facing gateway/tracker helper behavior.\n\nTags: gateway, http, sdk"

    address = _payment_address_for_api_key("sdk-key")
    # Registration always resolves a precision, so a registry node carries one;
    # coverage counts only nodes that can actually serve the layers they claim.
    nodes = [
        _NodeEntry("a", "http://node-a", 0, 15, "stub-model", None, {}, None, 1.0,
                   quantization="bfloat16"),
        _NodeEntry("b", "http://node-b", 16, 31, "stub-model", None, {}, None, 1.0,
                   quantization="bfloat16"),
    ]

    assert 32 <= len(address) <= 44
    assert _coverage_percentage(nodes, 0, 31) == 100.0


def _post_json(url: str, payload: dict) -> dict:
    data = json.dumps(payload).encode()
    req = urllib.request.Request(
        url,
        data=data,
        headers={"Content-Type": "application/json"},
        method="POST",
    )
    with urllib.request.urlopen(req) as r:
        return json.loads(r.read())


def _register_node(
    tracker_url: str,
    endpoint: str,
    shard_start: int,
    shard_end: int,
    *,
    wallet_address: str,
) -> None:
    _post_json(
        f"{tracker_url}/v1/nodes/register",
        {
            "endpoint": endpoint,
            "shard_start": shard_start,
            "shard_end": shard_end,
            "hardware_profile": {},
            "wallet_address": wallet_address,
            "score": 1.0,
        },
    )


def test_meshnet_sdk_methods_against_local_network():
    "SDK covers OpenAI chat passthrough plus meshnet wallet/model/cost/request APIs.\n\nTags: gateway, http, sdk"

    contracts = LocalSolanaContracts(starting_credit_lamports=0)
    contracts.payment.fund_api_key("sdk-key", lamports=2_000_000_000, usdc_micro=123_000_000)

    tracker = TrackerServer(contracts=contracts)
    tracker_port = tracker.start()
    tracker_url = f"http://127.0.0.1:{tracker_port}"

    nodes = [
        StubNodeServer(shard_start=0, shard_end=15, is_last_shard=False),
        StubNodeServer(shard_start=16, shard_end=31, is_last_shard=True),
        StubNodeServer(shard_start=0, shard_end=15, is_last_shard=False),
        StubNodeServer(shard_start=16, shard_end=31, is_last_shard=True),
    ]
    node_ports = [node.start() for node in nodes]

    for index, (port, shard_start, shard_end) in enumerate(
        [
            (node_ports[0], 0, 15),
            (node_ports[1], 16, 31),
            (node_ports[2], 0, 15),
            (node_ports[3], 16, 31),
        ],
        start=1,
    ):
        _register_node(
            tracker_url,
            f"http://127.0.0.1:{port}",
            shard_start,
            shard_end,
            wallet_address=f"wallet-{index}",
        )

    gateway = GatewayServer(tracker_url=tracker_url, contracts=contracts)
    gateway_port = gateway.start()
    gateway_url = f"http://127.0.0.1:{gateway_port}"

    try:
        client = Client(api_key="sdk-key", base_url=gateway_url)

        chat_response = client.chat.completions.create(
            model="stub-model",
            messages=[{"role": "user", "content": "hello sdk"}],
        )
        assert chat_response.choices[0].message.content == "stub response to: hello sdk"

        balance = client.wallet.balance()
        assert balance.sol == 2.0
        assert balance.usdc == 123.0

        top_up = client.wallet.top_up(amount_sol=1.0)
        assert top_up.amount_sol == 1.0
        assert 32 <= len(top_up.payment_address) <= 44
        assert top_up.qr.startswith("solana:")

        available_models = client.models.available()
        assert available_models == [
            {
                "id": "stub-model",
                "shard_coverage_percentage": 100.0,
            }
        ]

        estimate = client.estimate_cost(model="stub-model", tokens=1_000)
        assert estimate.model == "stub-model"
        assert estimate.tokens == 1_000
        assert estimate.sol == 0.000032

        redundant = client.request(
            model="stub-model",
            messages=[{"role": "user", "content": "redundant"}],
            redundancy=2,
        )
        assert redundant.redundancy == 2
        assert redundant.majority_response.choices[0].message.content == (
            "stub response to: redundant"
        )
        assert len(redundant.responses) == 2
        assert nodes[1].received_activations
        assert nodes[3].received_activations
    finally:
        gateway.stop()
        for node in nodes:
            node.stop()
        tracker.stop()
