"""Fragment client for fetching HTML fragments from coop apps.

Lightweight httpx-based client (no Quart dependency) for Art-DAG to consume
coop app fragments like nav-tree, auth-menu, and cart-mini.
"""

from __future__ import annotations

import asyncio
import logging
import os
from typing import Sequence

import httpx

log = logging.getLogger(__name__)

FRAGMENT_HEADER = "X-Fragment-Request"

_client: httpx.AsyncClient | None = None
_DEFAULT_TIMEOUT = 2.0


def _get_client() -> httpx.AsyncClient:
    global _client
    if _client is None or _client.is_closed:
        _client = httpx.AsyncClient(
            timeout=httpx.Timeout(_DEFAULT_TIMEOUT),
            follow_redirects=False,
        )
    return _client


def _internal_url(app_name: str) -> str:
    """Resolve internal base URL for a coop app.

    Looks up ``INTERNAL_URL_{APP}`` first, falls back to ``http://{app}:8000``.
    """
    env_key = f"INTERNAL_URL_{app_name.upper()}"
    return os.getenv(env_key, f"http://{app_name}:8000").rstrip("/")


async def fetch_fragment(
    app_name: str,
    fragment_type: str,
    *,
    params: dict | None = None,
    timeout: float = _DEFAULT_TIMEOUT,
    required: bool = False,
) -> str:
    """Fetch an HTML fragment from a coop app.

    Returns empty string on failure by default (required=False).
    """
    base = _internal_url(app_name)
    url = f"{base}/internal/fragments/{fragment_type}"
    try:
        resp = await _get_client().get(
            url,
            params=params,
            headers={FRAGMENT_HEADER: "1"},
            timeout=timeout,
        )
        if resp.status_code == 200:
            return resp.text
        msg = f"Fragment {app_name}/{fragment_type} returned {resp.status_code}"
        log.warning(msg)
        if required:
            raise RuntimeError(msg)
        return ""
    except RuntimeError:
        raise
    except Exception as exc:
        msg = f"Fragment {app_name}/{fragment_type} failed: {exc}"
        log.warning(msg)
        if required:
            raise RuntimeError(msg) from exc
        return ""


async def fetch_fragments(
    requests: Sequence[tuple[str, str, dict | None]],
    *,
    timeout: float = _DEFAULT_TIMEOUT,
    required: bool = False,
) -> list[str]:
    """Fetch multiple fragments concurrently."""
    return list(await asyncio.gather(*(
        fetch_fragment(app, ftype, params=params, timeout=timeout, required=required)
        for app, ftype, params in requests
    )))
