"""Testy Orchestrator dla resources — wymagają działającego serwera MCP."""

import asyncio
from unittest.mock import AsyncMock, MagicMock

import pytest

from ..connection_pool import ConnectionPool
from ..executor import Executor
from ..orchestrator import Orchestrator
from ..registry import Registry


def make_gateway_mock():
    gateway = MagicMock()
    gateway.notify_tool_list_changed = AsyncMock()
    gateway.notify_resource_list_changed = AsyncMock()
    gateway.notify_resource_updated = AsyncMock()
    return gateway


@pytest.mark.anyio
async def test_add_backend_registers_resources(mcp_server_url):
    registry = Registry()
    pool = ConnectionPool()
    executor = Executor(pool)
    orchestrator = Orchestrator(registry, pool, executor)

    await orchestrator.add_backend("test", mcp_server_url)

    resources = registry.list_resources()
    uris = [r.public_uri for r in resources]
    assert "test://greeting" in uris
    assert "test://config.json" in uris
    await pool.close_all()


@pytest.mark.anyio
async def test_add_backend_notifies_resource_list_changed(mcp_server_url):
    registry = Registry()
    pool = ConnectionPool()
    executor = Executor(pool)
    gateway = make_gateway_mock()
    orchestrator = Orchestrator(registry, pool, executor, gateway=gateway)

    await orchestrator.add_backend("test", mcp_server_url)

    gateway.notify_resource_list_changed.assert_called()
    await pool.close_all()


@pytest.mark.anyio
async def test_handle_disconnect_removes_resources(mcp_server_url):
    registry = Registry()
    pool = ConnectionPool()
    executor = Executor(pool)
    gateway = make_gateway_mock()
    orchestrator = Orchestrator(registry, pool, executor, gateway=gateway)

    await orchestrator.add_backend("test", mcp_server_url)
    assert len(registry.list_resources()) > 0

    await orchestrator._handle_disconnect("test")

    assert registry.list_resources() == []

    task = orchestrator._reconnect_tasks.get("test")
    if task:
        task.cancel()
    await pool.close_all()


@pytest.mark.anyio
async def test_remove_backend_clears_resources(mcp_server_url):
    registry = Registry()
    pool = ConnectionPool()
    executor = Executor(pool)
    orchestrator = Orchestrator(registry, pool, executor)

    await orchestrator.add_backend("test", mcp_server_url)
    await orchestrator.remove_backend("test")

    assert registry.list_resources() == []
    await pool.close_all()


@pytest.mark.anyio
async def test_resource_updated_forwards_to_gateway(mcp_server_url):
    """Notyfikacja resources/updated z backendu zostaje przekazana do gateway."""
    registry = Registry()
    pool = ConnectionPool()
    executor = Executor(pool)
    gateway = make_gateway_mock()
    orchestrator = Orchestrator(registry, pool, executor, gateway=gateway)

    await orchestrator.add_backend("test", mcp_server_url)

    # symuluj notyfikację z poola
    orchestrator._schedule_resource_updated("test", "test://greeting")
    # poczekaj aż task się wykona
    for _ in range(20):
        await asyncio.sleep(0.05)
        if gateway.notify_resource_updated.called:
            break

    gateway.notify_resource_updated.assert_called_with("test://greeting")
    await pool.close_all()


@pytest.mark.anyio
async def test_refresh_backend_resources_only(mcp_server_url):
    """refresh_backend_resources odświeża tylko resources, nie tools."""
    registry = Registry()
    pool = ConnectionPool()
    executor = Executor(pool)
    gateway = make_gateway_mock()
    orchestrator = Orchestrator(registry, pool, executor, gateway=gateway)

    await orchestrator.add_backend("test", mcp_server_url)
    tools_before = {t.public_name for t in registry.list_tools()}

    gateway.notify_resource_list_changed.reset_mock()
    await orchestrator.refresh_backend_resources("test")

    tools_after = {t.public_name for t in registry.list_tools()}
    assert tools_before == tools_after
    assert len(registry.list_resources()) > 0
    gateway.notify_resource_list_changed.assert_called()
    await pool.close_all()
