"""
Integration tests: async execution (HTTP), mTLS, and async + mTLS.

Author: Vasiliy Zdanovskiy
email: vasilyvz@gmail.com

Run from project root with server(s) already running, or use --start-server.
Certificates: mtls_certificates/ (server: test-server.crt/key, client: test-client.crt/key, ca: ca.crt).
"""

from __future__ import annotations

import asyncio
import subprocess
import sys
from pathlib import Path

import pytest

# Add project root
PROJECT_ROOT = Path(__file__).resolve().parents[1]
if str(PROJECT_ROOT) not in sys.path:
    sys.path.insert(0, str(PROJECT_ROOT))

from mcp_proxy_adapter.client.jsonrpc_client import JsonRpcClient  # noqa: E402


# Paths relative to project root
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"
HTTP_BASIC_CONFIG = (
    PROJECT_ROOT
    / "mcp_proxy_adapter"
    / "examples"
    / "full_application"
    / "configs"
    / "http_basic.json"
)
MTLS_CONFIG = (
    PROJECT_ROOT
    / "mcp_proxy_adapter"
    / "examples"
    / "full_application"
    / "configs"
    / "mtls_no_roles_correct.json"
)


def _start_server(
    config_path: Path, port: int, timeout: float = 15.0
) -> subprocess.Popen | None:
    """Start full_application server; return process or None on failure."""
    cmd = [
        sys.executable,
        "-m",
        "mcp_proxy_adapter.examples.full_application.main",
        "--config",
        str(config_path),
        "--port",
        str(port),
    ]
    proc = subprocess.Popen(
        cmd,
        cwd=str(PROJECT_ROOT),
        stdout=subprocess.DEVNULL,
        stderr=subprocess.PIPE,
    )
    return proc


async def _wait_port(host: str, port: int, timeout: float) -> bool:
    """Wait until host:port is accepting connections."""
    import socket

    start = asyncio.get_event_loop().time()
    while asyncio.get_event_loop().time() - start < timeout:
        try:
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.settimeout(1.0)
            sock.connect((host, port))
            sock.close()
            return True
        except OSError:
            await asyncio.sleep(0.3)
    return False


# --- 1. Async (HTTP) ---


@pytest.mark.asyncio
async def test_async_http_execute_async_and_ws_result() -> None:
    """Submit echo via execute_async over HTTP; receive result via WebSocket."""
    client = JsonRpcClient(protocol="http", host="127.0.0.1", port=8080)
    received: list[dict] = []

    def on_result(event: dict) -> None:
        received.append(event)

    try:
        result = await client.execute_async(
            "echo",
            {"message": "async-http"},
            on_result=on_result,
            timeout=15.0,
        )
        assert result.get("accepted") is True
        assert result.get("deliver_id")
        # Wait for WS push
        await asyncio.sleep(2.0)
        assert len(received) >= 1
        ev = received[0]
        assert ev.get("event") == "job_completed"
        assert ev.get("result", {}).get("data", {}).get("message") == "async-http"
    finally:
        await client.close()


# --- 2. mTLS ---


@pytest.mark.asyncio
async def test_mtls_health_and_echo() -> None:
    """mTLS: health and echo via client with client certs."""
    if (
        not MTLS_CLIENT_CERT.exists()
        or not MTLS_CLIENT_KEY.exists()
        or not MTLS_CA.exists()
    ):
        pytest.skip("mTLS certificates not found (mtls_certificates/)")
    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),
        check_hostname=False,
        token_header="X-API-Key",
        token="admin-secret-key-mtls",
    )
    try:
        health = await client.health()
        assert health.get("status") == "ok"
        result = await client.execute_command("echo", {"message": "mtls-check"})
        assert result.get("success") is True
        assert result.get("data", {}).get("message") == "mtls-check"
    finally:
        await client.close()


# --- 3. Async + mTLS ---


@pytest.mark.asyncio
async def test_async_mtls_execute_async_and_ws_result() -> None:
    """Submit echo via execute_async over mTLS; receive result via WebSocket (wss)."""
    if (
        not MTLS_CLIENT_CERT.exists()
        or not MTLS_CLIENT_KEY.exists()
        or not MTLS_CA.exists()
    ):
        pytest.skip("mTLS certificates not found (mtls_certificates/)")
    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),
        check_hostname=False,
        token_header="X-API-Key",
        token="admin-secret-key-mtls",
    )
    received: list[dict] = []

    def on_result(event: dict) -> None:
        received.append(event)

    try:
        result = await client.execute_async(
            "echo",
            {"message": "async-mtls"},
            on_result=on_result,
            timeout=15.0,
        )
        assert result.get("accepted") is True
        assert result.get("deliver_id")
        await asyncio.sleep(2.0)
        assert len(received) >= 1
        ev = received[0]
        assert ev.get("event") == "job_completed"
        assert ev.get("result", {}).get("data", {}).get("message") == "async-mtls"
    finally:
        await client.close()
