"""Pytest configuration and fixtures for OAGW E2E tests."""
import asyncio
import os
import threading

import httpx
import pytest

from .mock_upstream import MockUpstreamServer


# ---------------------------------------------------------------------------
# Environment-driven fixtures
# ---------------------------------------------------------------------------

@pytest.fixture
def oagw_base_url():
    """OAGW service base URL."""
    return os.getenv("E2E_OAGW_BASE_URL", "http://localhost:8086")


@pytest.fixture
def mock_upstream_url():
    """Mock upstream base URL (must be reachable by the OAGW service)."""
    return os.getenv("E2E_MOCK_UPSTREAM_URL", "http://127.0.0.1:19876")


@pytest.fixture
def oagw_headers():
    """Standard headers for OAGW requests (auth only — tenant comes from the token)."""
    token = os.getenv("E2E_AUTH_TOKEN", "e2e-token-tenant-a")
    return {
        "Authorization": f"Bearer {token}",
    }


# ---------------------------------------------------------------------------
# Hierarchy tenant headers (for multi-tenant budget allocation tests)
# ---------------------------------------------------------------------------

@pytest.fixture
def hierarchy_root_headers():
    """Headers for hierarchy-root tenant (00000000-...001)."""
    return {"Authorization": "Bearer e2e-token-hierarchy-root"}


@pytest.fixture
def hierarchy_l1a_headers():
    """Headers for hierarchy-l1a tenant (00000000-...002), child of root."""
    return {"Authorization": "Bearer e2e-token-hierarchy-l1a"}


@pytest.fixture
def hierarchy_l1b_headers():
    """Headers for hierarchy-l1b tenant (00000000-...005), child of root."""
    return {"Authorization": "Bearer e2e-token-hierarchy-l1b"}


# ---------------------------------------------------------------------------
# Session-scoped mock upstream server
# ---------------------------------------------------------------------------

@pytest.fixture(scope="session")
def mock_upstream():
    """Start the mock upstream server for the entire test session."""
    url = os.getenv("E2E_MOCK_UPSTREAM_URL", "http://127.0.0.1:19876")

    # If a custom URL is set, assume the mock is managed externally.
    if os.getenv("E2E_MOCK_UPSTREAM_EXTERNAL"):
        yield
        return

    # Parse port from URL.
    port = int(url.rsplit(":", 1)[-1].split("/")[0])
    server = MockUpstreamServer(host="127.0.0.1", port=port)

    # Run the mock server in a background thread with its own event loop
    # so it can actually serve requests while tests run.
    loop = asyncio.new_event_loop()
    loop.run_until_complete(server.start())

    thread = threading.Thread(target=loop.run_forever, daemon=True)
    thread.start()

    yield server

    async def _shutdown() -> None:
        if server._server:
            server._server.close()
        current = asyncio.current_task()
        pending = [
            t for t in asyncio.all_tasks()
            if t is not current and not t.done()
        ]
        for task in pending:
            task.cancel()
        if pending:
            await asyncio.wait(pending, timeout=2)

    fut = asyncio.run_coroutine_threadsafe(_shutdown(), loop)
    try:
        fut.result(timeout=5)
    except (TimeoutError, Exception):
        pass  # Best-effort; the daemon thread will die with the process.
    loop.call_soon_threadsafe(loop.stop)
    thread.join(timeout=5)


# ---------------------------------------------------------------------------
# Session-scoped OAGW reachability check
# ---------------------------------------------------------------------------

@pytest.fixture(scope="session", autouse=True)
def _check_oagw_reachable():
    """Skip all OAGW tests if the service is not reachable."""
    url = os.getenv("E2E_OAGW_BASE_URL", "http://localhost:8086")
    try:
        resp = httpx.get(f"{url}/oagw/v1/upstreams", timeout=5.0)
        # Any response (even 401/403) means the service is up.
    except httpx.ConnectError:
        pytest.skip(f"OAGW service not running at {url}", allow_module_level=True)
    except Exception:
        # Timeout or other transient error — still try to run tests.
        pass
