import asyncio
import threading
from contextlib import contextmanager

from aiohttp import web


@contextmanager
def healthcheck(grpc_server_to_follow, port):
    stop = threading.Event()
    server = Server(grpc_server_to_follow, port)
    t = threading.Thread(target=run_sync, args=(stop, server))
    t.start()
    try:
        yield
    finally:
        stop.set()
        t.join()


def run_sync(stop, server):
    asyncio.run(run_async(stop, server))


async def run_async(stop: threading.Event, server):
    runner = create_app(server)
    await runner.setup()
    site = web.TCPSite(runner, '::', server.port)
    await site.start()
    while not stop.is_set():
        await asyncio.sleep(0.1)
    await site.stop()
    await runner.shutdown()


def create_app(server):
    app = web.Application()
    app.add_routes([web.get('/health', server.healthcheck_handler)])
    return web.AppRunner(app)


class Server:
    def __init__(self, grpc_server_to_follow, port):
        self.grpc_server_to_follow = grpc_server_to_follow
        self.port = port

    async def healthcheck_handler(self, request: web.Request):
        state = self.grpc_server_to_follow._state
        active_requests = state.active_rpc_count
        if active_requests > 0.8 * state.thread_pool._max_workers:
            return web.Response(status=429,
                                text=f'too many concurrent requests: {active_requests}')
        return web.Response(text='OK')
