"""
Integration test: submit queued job, receive event via /ws.

When run via pytest, the test server (full_application with http_basic.json on 8080
and mtls_no_roles_correct.json on 8443) is started automatically by the
integration_test_servers fixture (tests/conftest.py).
Or run server manually and: pytest tests/test_job_push_ws_integration.py -v -m integration
Or as script: python tests/test_job_push_ws_integration.py

Author: Vasiliy Zdanovskiy
email: vasilyvz@gmail.com
"""

import asyncio
import os
import sys
from pathlib import Path

import pytest

# Add project root
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))

from mcp_proxy_adapter.client.jsonrpc_client import JsonRpcClient  # noqa: E402
from mcp_proxy_adapter.client.jsonrpc_client.ws_job_status import (  # noqa: E402
    wait_for_job_via_websocket,
)

PROJECT_ROOT = Path(__file__).resolve().parents[1]
MTLS_CLIENT_CERT = PROJECT_ROOT / "mtls_certificates" / "client" / "test-client.crt"
MTLS_CLIENT_KEY = PROJECT_ROOT / "mtls_certificates" / "client" / "test-client.key"
MTLS_CA = PROJECT_ROOT / "mtls_certificates" / "ca" / "ca.crt"


def _server_available(host: str = "127.0.0.1", port: int = 8080) -> bool:
    """Check if server is reachable (health endpoint)."""
    try:
        import httpx

        r = httpx.get(f"http://{host}:{port}/health", timeout=2.0)
        return r.status_code == 200
    except Exception:
        return False


@pytest.fixture
def client():
    """HTTP client for local server (no token)."""
    return JsonRpcClient(
        protocol="http",
        host="127.0.0.1",
        port=8080,
    )


async def _wait_server(host: str, port: int, timeout: float = 12.0) -> bool:
    """Wait for HTTP server to become available (fixture may have just started it)."""
    deadline = asyncio.get_event_loop().time() + timeout
    while asyncio.get_event_loop().time() < deadline:
        if _server_available(host, port):
            return True
        await asyncio.sleep(0.5)
    return False


@pytest.mark.asyncio
@pytest.mark.integration
async def test_submit_job_and_receive_push_via_ws(
    integration_test_servers: None,
    client: JsonRpcClient,
) -> None:
    """Submit a queued command, get job_id, subscribe via /ws, receive job_completed."""
    if not await _wait_server("127.0.0.1", 8080):
        pytest.skip("Server not available on 127.0.0.1:8080 (need integration_test_servers)")
    # Use long_running_task (queued) with short duration so it completes quickly
    result = await client.jsonrpc_call(
        "long_running_task",
        {"seconds": 1},
    )
    data = result.get("result", result) if isinstance(result, dict) else {}
    job_id = data.get("job_id") or data.get("data", {}).get("job_id")
    assert job_id, f"Expected job_id in response: {data}"

    # Wait for completion via WebSocket (built-in /ws + job push)
    event = await asyncio.wait_for(
        wait_for_job_via_websocket(client, job_id, timeout=30.0),
        timeout=35.0,
    )
    assert event.get("event") in ("job_completed", "job_failed", "job_stopped")
    assert event.get("job_id") == job_id
    if event.get("event") == "job_completed":
        assert "result" in event or "result" in event.get("data", {})


def _mtls_server_available() -> bool:
    """Check if mTLS server is reachable on 8443."""
    try:
        import httpx
        r = httpx.get(
            "https://127.0.0.1:8443/health",
            verify=False,
            timeout=2.0,
        )
        return r.status_code == 200
    except Exception:
        return False


async def _wait_mtls_server(timeout: float = 12.0) -> bool:
    """Wait for mTLS server to become available."""
    deadline = asyncio.get_event_loop().time() + timeout
    while asyncio.get_event_loop().time() < deadline:
        if _mtls_server_available():
            return True
        await asyncio.sleep(0.5)
    return False


@pytest.mark.asyncio
@pytest.mark.integration
async def test_mtls_submit_job_and_receive_push_via_ws(
    integration_test_servers: None,
) -> None:
    """Submit queued command over mTLS, get job_id, receive job_completed via WebSocket (wss)."""
    if not MTLS_CLIENT_CERT.exists() or not MTLS_CLIENT_KEY.exists():
        pytest.skip("mTLS certificates not found (mtls_certificates/)")
    if not await _wait_mtls_server():
        pytest.skip("mTLS server not available on 127.0.0.1:8443 (need integration_test_servers)")
    client = JsonRpcClient(
        protocol="mtls",
        host="127.0.0.1",
        port=8443,
        cert=str(MTLS_CLIENT_CERT),
        key=str(MTLS_CLIENT_KEY),
        ca=str(MTLS_CA) if MTLS_CA.exists() else None,
        check_hostname=False,
        token_header="X-API-Key",
        token="admin-secret-key-mtls",
    )
    try:
        result = await client.jsonrpc_call(
            "long_running_task",
            {"seconds": 1},
        )
        data = result.get("result", result) if isinstance(result, dict) else {}
        job_id = data.get("job_id") or data.get("data", {}).get("job_id")
        assert job_id, f"Expected job_id in response: {data}"

        event = await asyncio.wait_for(
            wait_for_job_via_websocket(client, job_id, timeout=30.0),
            timeout=35.0,
        )
        assert event.get("event") in ("job_completed", "job_failed", "job_stopped")
        assert event.get("job_id") == job_id
        if event.get("event") == "job_completed":
            assert "result" in event or "result" in event.get("data", {})
    finally:
        await client.close()


if __name__ == "__main__":
    # Run as script: start server if SERVER_URL not set, then run test
    port = int(os.environ.get("PORT", "8080"))
    host = os.environ.get("HOST", "127.0.0.1")
    cl = JsonRpcClient(protocol="http", host=host, port=port)

    async def main():
        print("Submitting long_running_task(seconds=2)...")
        result = await cl.jsonrpc_call("long_running_task", {"seconds": 2})
        data = result.get("result", result) if isinstance(result, dict) else {}
        job_id = data.get("job_id") or data.get("data", {}).get("job_id")
        if not job_id:
            print("No job_id:", data)
            return
        print("job_id:", job_id)
        print("Waiting for job completion via WebSocket...")
        event = await wait_for_job_via_websocket(cl, job_id, timeout=30.0)
        print("Event:", event)

    asyncio.run(main())
