from __future__ import annotations

from typing import Any, Dict, Optional

from .HTTP import HTTPClient

DEFAULT_BASE_URL = "https://tidal-api.binimum.org"


class HifiApiError(Exception):
    """Raised when the HiFi API returns an error or malformed response."""


class HifiApiClient:
    """Lightweight client for the hifi-api endpoints.

    Supported endpoints:
    - GET /search/ with exactly one of s, a, v, p
    - GET /track/ with id (and optional quality)
    - GET /info/ with id
    - GET /album/ with id
    - GET /lyrics/ with id
    """

    def __init__(self, base_url: str = DEFAULT_BASE_URL, *, timeout: float = 10.0) -> None:
        self.base_url = str(base_url or DEFAULT_BASE_URL).rstrip("/")
        self.timeout = float(timeout)

    def _get_json(self, path: str, params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
        url = f"{self.base_url}/{str(path or '').lstrip('/')}"
        with HTTPClient(timeout=self.timeout) as client:
            response = client.get(url, params=params, allow_redirects=True)
            response.raise_for_status()
        try:
            return response.json()
        except Exception as exc:  # pragma: no cover - defensive
            raise HifiApiError(f"Invalid JSON response from {url}: {exc}") from exc

    def search(self, params: Dict[str, str]) -> Dict[str, Any]:
        usable = {k: v for k, v in (params or {}).items() if v}
        search_keys = [key for key in ("s", "a", "v", "p") if usable.get(key)]
        if not search_keys:
            raise HifiApiError("One of s/a/v/p is required for /search/")
        if len(search_keys) > 1:
            first = search_keys[0]
            usable = {first: usable[first]}
        return self._get_json("search/", params=usable)

    def track(self, track_id: int, *, quality: Optional[str] = None) -> Dict[str, Any]:
        try:
            track_int = int(track_id)
        except Exception as exc:
            raise HifiApiError(f"track_id must be int-compatible: {exc}") from exc
        if track_int <= 0:
            raise HifiApiError("track_id must be positive")

        params: Dict[str, Any] = {"id": track_int}
        if quality:
            params["quality"] = str(quality)
        return self._get_json("track/", params=params)

    def info(self, track_id: int) -> Dict[str, Any]:
        try:
            track_int = int(track_id)
        except Exception as exc:
            raise HifiApiError(f"track_id must be int-compatible: {exc}") from exc
        if track_int <= 0:
            raise HifiApiError("track_id must be positive")

        return self._get_json("info/", params={"id": track_int})

    def album(self, album_id: int) -> Dict[str, Any]:
        """Fetch album details, including track list when provided by the backend."""

        try:
            album_int = int(album_id)
        except Exception as exc:
            raise HifiApiError(f"album_id must be int-compatible: {exc}") from exc
        if album_int <= 0:
            raise HifiApiError("album_id must be positive")

        return self._get_json("album/", params={"id": album_int})

    def lyrics(self, track_id: int) -> Dict[str, Any]:
        """Fetch lyrics (including subtitles/LRC) for a track."""

        try:
            track_int = int(track_id)
        except Exception as exc:
            raise HifiApiError(f"track_id must be int-compatible: {exc}") from exc
        if track_int <= 0:
            raise HifiApiError("track_id must be positive")

        return self._get_json("lyrics/", params={"id": track_int})
