"""Audiobookshelf integration via its REST API.

Authentication uses a Bearer API token (Audiobookshelf account → API token).
All functions take an ``httpx.AsyncClient`` so they share the app-wide client.
This is the foundation for upcoming audiobook tooling (renaming, etc.); for now
it covers connection status, libraries, and aggregate stats.
"""

from __future__ import annotations

import os

import httpx

ABS_URL = os.environ.get("AUDIOBOOKSHELF_URL", "")
ABS_TOKEN = os.environ.get("AUDIOBOOKSHELF_TOKEN", "")


class AudiobookshelfError(Exception):
    def __init__(self, message: str, status: int = 502):
        self.message = message
        self.status = status
        super().__init__(message)


def is_configured() -> bool:
    return bool(ABS_URL and ABS_TOKEN)


def _require_configured() -> None:
    if not is_configured():
        raise AudiobookshelfError(
            "Audiobookshelf is not configured. Set AUDIOBOOKSHELF_URL and AUDIOBOOKSHELF_TOKEN.",
            status=503,
        )


def _base_url() -> str:
    return ABS_URL.rstrip("/")


def _headers() -> dict:
    return {"Authorization": f"Bearer {ABS_TOKEN}", "Accept": "application/json"}


async def _call(client: httpx.AsyncClient, path: str, params: dict | None = None) -> dict:
    _require_configured()
    url = f"{_base_url()}{path}"
    try:
        response = await client.get(url, params=params, headers=_headers())
    except httpx.RequestError as exc:
        raise AudiobookshelfError(f"Could not reach Audiobookshelf at {ABS_URL}: {exc}") from exc

    if response.status_code in (401, 403):
        raise AudiobookshelfError("Audiobookshelf rejected the API token.", status=401)
    try:
        response.raise_for_status()
    except httpx.HTTPStatusError as exc:
        raise AudiobookshelfError(
            f"Audiobookshelf returned HTTP {response.status_code} for {path}.", status=502
        ) from exc

    try:
        return response.json()
    except ValueError as exc:
        raise AudiobookshelfError("Audiobookshelf returned an invalid response.", status=502) from exc


async def ping(client: httpx.AsyncClient) -> dict:
    if not is_configured():
        return {"connected": False, "configured": False, "url": ABS_URL}
    try:
        me = await _call(client, "/api/me")
        return {
            "connected": True,
            "configured": True,
            "url": ABS_URL,
            "username": me.get("username"),
        }
    except AudiobookshelfError as exc:
        return {"connected": False, "configured": True, "url": ABS_URL, "error": exc.message}


async def get_libraries(client: httpx.AsyncClient) -> list[dict]:
    payload = await _call(client, "/api/libraries")
    raw = payload.get("libraries") or []
    return [
        {
            "id": lib.get("id"),
            "name": lib.get("name") or "",
            "media_type": lib.get("mediaType") or "book",
            "provider": lib.get("provider"),
        }
        for lib in raw
    ]


async def _library_item_total(client: httpx.AsyncClient, library_id: str) -> int:
    try:
        payload = await _call(client, f"/api/libraries/{library_id}/items", {"limit": 0})
        return int(payload.get("total") or 0)
    except AudiobookshelfError:
        return 0


async def get_stats(client: httpx.AsyncClient) -> dict:
    """Aggregate stats across all libraries for the overview page."""
    libraries = await get_libraries(client)
    book_items = podcast_items = author_count = 0
    total_duration = total_size = num_tracks = 0
    library_views: list[dict] = []

    for lib in libraries:
        stats: dict = {}
        try:
            stats = await _call(client, f"/api/libraries/{lib['id']}/stats")
        except AudiobookshelfError:
            stats = {}

        items = int(stats.get("totalItems") or 0)
        if not items:
            items = await _library_item_total(client, lib["id"])

        duration = int(stats.get("totalDuration") or 0)
        size = int(stats.get("totalSize") or 0)
        authors = int(stats.get("totalAuthors") or 0)
        tracks = int(stats.get("numAudioTracks") or 0)

        if lib["media_type"] == "podcast":
            podcast_items += items
        else:
            book_items += items
        author_count += authors
        total_duration += duration
        total_size += size
        num_tracks += tracks

        library_views.append(
            {
                "id": lib["id"],
                "name": lib["name"],
                "media_type": lib["media_type"],
                "items": items,
                "authors": authors,
                "duration": duration,
                "size": size,
            }
        )

    return {
        "library_count": len(libraries),
        "book_count": book_items,
        "podcast_count": podcast_items,
        "author_count": author_count,
        "total_duration": total_duration,
        "total_size": total_size,
        "num_audio_tracks": num_tracks,
        "libraries": library_views,
    }
