"""Firebase Authentication flow for Maeris MCP."""

import secrets
import time
import webbrowser
from urllib.parse import urlencode

import httpx
import structlog

logger = structlog.get_logger(__name__)


class OAuthFlow:
    """Handle Firebase authentication by delegating callback to the maeris-app server."""

    def __init__(self, app_url: str) -> None:
        """Initialize OAuth flow.

        Args:
            app_url: Base URL of Maeris App frontend (e.g., https://light-dev.up.railway.app in dev)
        """
        self.app_url = app_url.rstrip("/")

    def authenticate(self, timeout: int = 300) -> dict[str, str]:
        """Start Firebase authentication flow.

        Opens the browser to the maeris-app login page. The app handles the
        Firebase redirect at /auth/callback and stores the token by state key.
        This method polls /auth/poll?state=<state> until the token is ready.

        Args:
            timeout: Max seconds to wait for the user to complete auth (default: 300)

        Returns:
            Dict with access_token and token_type

        Raises:
            TimeoutError: If no token received within timeout
            ValueError: If state mismatch (CSRF protection)
            RuntimeError: If authentication fails
        """
        state = secrets.token_urlsafe(32)
        redirect_uri = f"{self.app_url}/auth/callback"

        auth_params = {
            "client_id": "maeris-mcp",
            "redirect_uri": redirect_uri,
            "state": state,
            "response_type": "token",
        }
        auth_url = f"{self.app_url}/auth/login?{urlencode(auth_params)}"

        logger.debug("opening_browser", url=auth_url)
        print(f"\nOpening browser for authentication...")
        webbrowser.open(auth_url)

        token, received_state, error = self._poll_for_token(state, timeout)

        if error:
            raise RuntimeError(f"Authentication failed: {error}")

        if state != received_state:
            raise ValueError("State mismatch - possible CSRF attack")

        logger.debug("authentication_successful")
        return {
            "access_token": token,
            "token_type": "Bearer",
        }

    def _poll_for_token(self, state: str, timeout: int) -> tuple[str, str, str | None]:
        """Poll the maeris-app /auth/poll endpoint until a token or error is available.

        The maeris-app is expected to expose:
            GET /auth/poll?state=<state>

        Response shape on success:
            { "token": "<firebase_id_token>", "state": "<state>" }

        Response shape on error:
            { "error": "<message>" }

        Response when not yet ready (still waiting for the user):
            404 or { "pending": true }

        Args:
            state: The CSRF state token generated for this auth attempt
            timeout: Max seconds to wait

        Returns:
            Tuple of (token, state, error)

        Raises:
            TimeoutError: If no callback received within timeout
        """
        poll_url = f"{self.app_url}/api/auth/poll"
        print("Waiting for you to sign in...")

        start = time.time()
        with httpx.Client(timeout=5.0) as client:
            while time.time() - start < timeout:
                try:
                    resp = client.get(poll_url, params={"state": state})

                    if resp.status_code == 200:
                        data = resp.json()

                        if "error" in data:
                            return "", "", data["error"]

                        if "token" in data:
                            return data["token"], data.get("state", state), None

                    # 404 or pending means the user hasn't finished yet — keep polling
                except httpx.RequestError:
                    # App server not reachable yet; keep trying
                    pass

                time.sleep(0.5)

        raise TimeoutError(f"Authentication timeout after {timeout} seconds")


class AuthenticationError(Exception):
    """Raised when authentication fails."""

    pass
