import asyncio
import logging

import uvicorn
from fastapi import FastAPI, Query
from fastapi.responses import HTMLResponse

from harbor.auth.errors import OAuthCallbackError
from harbor.auth.constants import CALLBACK_PORT

_FONT_LINK = '<link href="https://fonts.googleapis.com/css2?family=Google+Sans:wght@400;500&display=swap" rel="stylesheet">'
_BODY_STYLE = (
    "font-family:'Google Sans',sans-serif;text-align:center;"
    "display:flex;flex-direction:column;align-items:center;justify-content:center;"
    "min-height:100vh;margin:0;font-size:1.5rem"
)

SUCCESS_HTML = f"""<!DOCTYPE html>
<html><head>{_FONT_LINK}</head>
<body style="{_BODY_STYLE}">
<h1>Authenticated</h1>
<p>You can close this tab and return to the terminal.</p>
</body></html>"""

ERROR_HTML = f"""<!DOCTYPE html>
<html><head>{_FONT_LINK}</head>
<body style="{_BODY_STYLE}">
<h1>Authentication Failed</h1>
<p>{{error}}</p>
</body></html>"""


async def wait_for_callback(timeout: float = 120.0) -> str:
    """Start an ephemeral async server and wait for the OAuth callback.

    Returns the authorization code from the callback URL.
    """
    result: dict[str, str | None] = {"code": None, "error": None}
    event = asyncio.Event()

    app = FastAPI()

    @app.get("/auth/callback")
    async def auth_callback(
        code: str | None = Query(default=None),
        error: str | None = Query(default=None),
    ) -> HTMLResponse:
        if error:
            result["error"] = error
            event.set()
            return HTMLResponse(content=ERROR_HTML.format(error=error), status_code=400)
        if code:
            result["code"] = code
            event.set()
            return HTMLResponse(content=SUCCESS_HTML, status_code=200)

        result["error"] = "No authorization code received"
        event.set()
        return HTMLResponse(
            content=ERROR_HTML.format(error="No authorization code received"),
            status_code=400,
        )

    config = uvicorn.Config(
        app,
        host="127.0.0.1",
        port=CALLBACK_PORT,
        log_level="error",
    )
    server = uvicorn.Server(config)

    serve_task = asyncio.create_task(server.serve())
    try:
        await asyncio.wait_for(event.wait(), timeout=timeout)
    except TimeoutError:
        raise OAuthCallbackError(
            "OAuth callback timed out. Please try `harbor auth login` again."
        )
    finally:
        server.should_exit = True
        await serve_task

    logging.debug("OAuth callback received")

    if result["error"]:
        raise OAuthCallbackError(f"OAuth callback error: {result['error']}")

    if result["code"] is None:
        raise OAuthCallbackError("No authorization code received from OAuth callback")

    return result["code"]
