"""Quick-start helpers that remove the boiler-plate required in the
examples/sample_agent.py script.

The three public async helpers cover the parts that tend to repeat in every
prototype:

    1. ``login_interactive`` – obtain a user JWT (cached) **and** a ready
       ``BarndoorSDK`` instance in one go.
    2. ``ensure_server_connected`` – wrapper around
       :pymeth:`barndoor.sdk.client.BarndoorSDK.ensure_server_connected` with a
       sensible progress / timeout default.
    3. ``get_mcp_adapter`` – construct a ``crewai_tools.MCPServerAdapter`` (or
       any other framework-agnostic adapter) that already carries the correct
       proxy URL, streaming transport and *provider* access token in the
       ``Authorization`` header.

The helpers are intentionally dependency-free – they import optional packages
only when necessary.  They can therefore be used both inside the Barndoor SDK
repo and by downstream projects that vendor-copy the file.
"""

from __future__ import annotations

# Standard library
import asyncio
import logging
from uuid import uuid4

# Import required auth helpers explicitly so type checkers can resolve them
from barndoor.sdk.auth import (
    build_authorization_url,
    exchange_code_for_token_backend,
    get_pending_oauth_state,
    start_local_callback_server,
)

# Automatically load .env if not done yet
from barndoor.sdk.config import get_static_config, load_dotenv_for_sdk

from .auth_store import (
    load_user_token,
    save_user_token,
)

# Load default .env file exactly once – safe no-op if already loaded by caller
load_dotenv_for_sdk()

# Internal imports -----------------------------------------------------------
from .client import BarndoorSDK
from .logging import get_logger

logger = get_logger("quickstart")


# ---------------------------------------------------------------------------
# Public helpers
# ---------------------------------------------------------------------------


a_sync = asyncio.run  # tiny alias for examples


async def login_interactive(
    *,
    auth_issuer: str | None = None,
    client_id: str | None = None,
    client_secret: str | None = None,
    audience: str | None = None,
    base_url: str | None = None,
    port: int = 52765,
) -> BarndoorSDK:
    """Return an initialized BarndoorSDK after ensuring valid user JWT."""
    from .auth_store import is_token_active_with_refresh

    logger.info("Starting interactive login flow")

    cfg = get_static_config()

    auth_issuer = auth_issuer or cfg.auth_issuer
    client_id = client_id or cfg.client_id
    client_secret = client_secret or cfg.client_secret
    audience = audience or cfg.api_audience  # Use config value

    # Track if user explicitly provided base_url
    user_provided_base_url = base_url is not None

    # 1. try cached token with refresh first ----------------------------------
    token_data = None
    # Use a placeholder URL for token validation check (will be resolved later)
    check_url = base_url or cfg.base_url
    if check_url and await is_token_active_with_refresh(check_url):
        logger.info("Using cached/refreshed valid token")
        token_data = load_user_token()
    else:
        # Only require client credentials if we need to perform OAuth
        if not client_id or not client_secret:
            raise RuntimeError(
                "AGENT_CLIENT_ID / AGENT_CLIENT_SECRET not set – "
                "create a .env file or export in the shell"
            )

        logger.info("No valid cached token, starting OAuth flow")
        # 2. if none – run interactive PKCE flow --------------------------
        redirect_uri, waiter = start_local_callback_server(port=port)
        auth_url = build_authorization_url(
            domain="",  # Not used when issuer is provided
            client_id=client_id,
            redirect_uri=redirect_uri,
            audience=audience,
            issuer=auth_issuer,
        )
        import webbrowser

        webbrowser.open(auth_url)
        logging.getLogger(__name__).info("Please complete login in your browser…")
        code, returned_state = await waiter
        # Validate OAuth state (if available) to mitigate CSRF
        expected_state = get_pending_oauth_state()
        if expected_state is not None and returned_state != expected_state:
            raise RuntimeError("OAuth state mismatch; possible CSRF attempt")
        token_data = exchange_code_for_token_backend(
            domain="",  # Not used when issuer is provided
            client_id=client_id,
            client_secret=client_secret,
            code=code,
            redirect_uri=redirect_uri,
            issuer=auth_issuer,
        )
        save_user_token(token_data)

    # Extract access token for SDK
    access_token = token_data if isinstance(token_data, str) else token_data["access_token"]

    # 3. build dynamic configuration - extracts org from JWT and resolves URL
    from barndoor.sdk.config import get_dynamic_config

    cfg_dyn = get_dynamic_config(access_token)

    # Use user-provided base_url if given, otherwise use the resolved URL from JWT
    final_base_url = base_url if user_provided_base_url else cfg_dyn.base_url

    # 4. create SDK
    sdk = BarndoorSDK(final_base_url, barndoor_token=access_token, validate_token_on_init=False)
    logger.info("Login completed successfully")
    return sdk


async def ensure_server_connected(
    sdk: BarndoorSDK,
    server_identifier: str,
    *,
    timeout: int = 90,
) -> None:
    """Guarantee that *server_identifier* (slug or provider) is connected.

    If the server is already connected the coroutine is a no-op, otherwise it
    launches the browser OAuth flow and waits (up to *timeout* seconds) until
    the connection is live.
    """
    logger.info(f"Ensuring {server_identifier} server is connected")

    # Try to get server by slug first (more efficient)
    server = None
    try:
        server = await sdk.get_server_by_slug(server_identifier)
    except Exception:
        # If slug lookup fails, fall back to searching the list
        # This handles cases where server_identifier might be a provider name
        servers = await sdk.list_servers()
        server = next(
            (
                s
                for s in servers
                if s.slug == server_identifier
                or (getattr(s, "provider", None) or "").lower() == server_identifier.lower()
            ),
            None,
        )

    if not server:
        logger.error(f"Server '{server_identifier}' not found")
        raise ValueError(f"Server '{server_identifier}' not found")

    if server.connection_status == "connected":
        logger.info(f"Server {server_identifier} already connected")
        return

    logger.info(f"Connecting to {server_identifier}...")
    await sdk.ensure_server_connected(server_identifier, poll_seconds=timeout)


async def make_mcp_connection_params(
    sdk: BarndoorSDK,
    server_slug: str,
    *,
    transport: str = "streamable-http",
):
    """Return ``(params_dict, public_url)`` where *params_dict* has the keys

    ``url``, ``headers`` and (optionally) ``transport`` so that it can be fed
    directly to whatever framework you're using (CrewAI, LangChain, custom).

    The helper hides the rules:
      • If BARNDOOR_ENV is "prod" → build public MCP URL
        otherwise ("dev" or "local") → route through the local proxy.
      • Inject JWT + session-id headers
    """
    # 1. Find the server by slug using the direct endpoint
    # This is more efficient than searching through paginated list results,
    # especially important in performance tests where servers may not be on the first page
    try:
        server = await sdk.get_server_by_slug(server_slug)
    except Exception as e:
        # Re-raise with a clearer error message
        from .exceptions import HTTPError

        if isinstance(e, HTTPError) and e.status_code == 404:
            raise ValueError(f"Server '{server_slug}' not found for current user") from e
        raise

    # Always prefer registry-provided proxy_url; fall back to detailed proxy_url
    url = getattr(server, "proxy_url", None)
    if not url:
        # Fallback: try getting by ID if proxy_url wasn't in the response
        details = await sdk.get_server(server.id)
        url = getattr(details, "proxy_url", None)

    if not url:
        raise RuntimeError(
            "Registry did not provide a proxy_url for this server. Ensure backend is updated."
        )

    params = {
        "url": url,
        "transport": transport,
        "headers": {
            "Accept": "application/json, text/event-stream",
            "Authorization": f"Bearer {sdk.token}",
            "x-barndoor-session-id": str(uuid4()),
        },
    }

    return params, url
