"""LibriVox fetcher — public-domain audiobook recordings.

JSON API: https://librivox.org/api/feed/audiobooks (keyless). LibriVox is
public domain by charter; the reader/author is credited in notes but
attribution is not required. Section-level (per-track) file resolution is a
future refinement; this resolves the audiobook's zip as the media target.
"""

from __future__ import annotations

import re

from tools.ingest.base import Candidate
from tools.licensing import librivox_license

BASE = "https://librivox.org/api/feed/audiobooks"


def _slug(text: str) -> str:
    s = re.sub(r"[^a-z0-9]+", "-", (text or "").lower()).strip("-")
    return s or "untitled"


def _ext_from_url(url: str, default: str) -> str:
    tail = url.rsplit("/", 1)[-1]
    return tail.rsplit(".", 1)[-1].lower() if "." in tail else default


class LibriVoxFetcher:
    archive = "librivox"

    def __init__(self, client):
        self.client = client

    def _candidate(self, book) -> Candidate:
        title = book.get("title", "")
        authors = book.get("authors") or []
        credit = ", ".join(
            f"{a.get('first_name', '')} {a.get('last_name', '')}".strip()
            for a in authors
        ).strip(", ")
        media_url = book.get("url_zip_file", "")
        description = (book.get("description", "") or "").strip()
        if credit:
            description = (description + f" (author: {credit})").strip()
        lic, attr = librivox_license()
        return Candidate(
            source_archive=self.archive,
            source_url=book.get("url_librivox", ""),
            media_url=media_url,
            title=title,
            license=lic,
            attribution=attr,
            suggested_id=f"librivox-{_slug(title)}",
            media_ext=_ext_from_url(media_url, "zip"),
            description=description,
        )

    def search(self, query: str, *, limit: int) -> list[Candidate]:
        url = f"{BASE}/title/^{query}?format=json&limit={limit}"
        data = self.client.get_json(url)
        books = (data.get("books") or [])[:limit]
        return [self._candidate(b) for b in books]

    def resolve(self, identifier: str) -> Candidate:
        url = f"{BASE}/id/{identifier}?format=json"
        data = self.client.get_json(url)
        books = data.get("books") or []
        if not books:
            raise ValueError(f"librivox: no audiobook for {identifier!r}")
        return self._candidate(books[0])
