"""
senzib.integrations.currencyapi.client

Клиент для сервиса https://currencyapi.com/ (v3).

❗ Библиотека:
- НЕ хранит API-ключ сама, ты передаёшь его в конструктор.
- НЕ пишет в файлы, НЕ зависит от Flask/БД.
- Только делает HTTP-запросы и возвращает JSON как dict.
"""

from __future__ import annotations

from typing import Any, Dict, List, Optional

import logging
import requests

logger = logging.getLogger(__name__)

JsonDict = Dict[str, Any]


class CurrencyAPIError(Exception):
    """Ошибка при обращении к CurrencyAPI."""


class CurrencyAPIClient:
    """
    Высокоуровневый клиент для CurrencyAPI v3.

    Пример:

        client = CurrencyAPIClient(api_key="xxx")
        data = client.get_currencies()
        usd = data["USD"]
    """

    def __init__(
        self,
        api_key: str,
        base_url: str = "https://api.currencyapi.com/v3",
        *,
        request_timeout: int = 30,
    ) -> None:
        self.api_key = api_key
        self.base_url = base_url.rstrip("/")
        self.request_timeout = request_timeout

    def _build_url(self, path: str) -> str:
        return f"{self.base_url}/{path.lstrip('/')}"

    def _get(
        self,
        path: str,
        *,
        params: Optional[Dict[str, Any]] = None,
    ) -> JsonDict:
        """
        Внутренний GET-запрос к CurrencyAPI с установкой заголовка apikey.
        """
        url = self._build_url(path)
        headers = {"apikey": self.api_key}

        logger.debug("CurrencyAPI GET %s params=%s", url, params)
        try:
            resp = requests.get(
                url,
                headers=headers,
                params=params,
                timeout=self.request_timeout,
            )
        except requests.RequestException as exc:
            raise CurrencyAPIError(
                "Network error while calling CurrencyAPI: %s" % exc
            ) from exc

        if resp.status_code != 200:
            # currencyapi обычно отдаёт JSON с полем "error"
            body = resp.text
            raise CurrencyAPIError(
                "CurrencyAPI error HTTP %s: %s" % (resp.status_code, body)
            )

        try:
            return resp.json() or {}
        except ValueError as exc:
            raise CurrencyAPIError(
                "Failed to parse JSON from CurrencyAPI: %s" % exc
            ) from exc

    # ------------------------------------------------------------------ #
    # ПУБЛИЧНЫЕ МЕТОДЫ
    # ------------------------------------------------------------------ #

    def get_currencies(
        self,
        codes: Optional[List[str]] = None,
    ) -> JsonDict:
        """
        Получение списка валют (метаданные).

        Документация CurrencyAPI: /v3/currencies

        :param codes:
            Список кодов валют, которые нужно получить (["USD", "EUR"]).
            Если None — вернёт все доступные валюты (по ограничению тарифа).

        :return:
            dict, как возвращает CurrencyAPI, обычно:
                {
                    "data": {
                        "USD": {...},
                        "EUR": {...},
                        ...
                    }
                }
        """
        params: Dict[str, Any] = {}
        if codes:
            # currencyapi ожидает "currencies=USD,EUR"
            params["currencies"] = ",".join(codes)

        return self._get("currencies", params=params)

    def get_latest(
        self,
        *,
        base_currency: str = "USD",
        currencies: Optional[List[str]] = None,
        date: Optional[str] = None,
    ) -> JsonDict:
        """
        Получение актуальных (или исторических) курсов.

        Документация CurrencyAPI: /v3/latest

        :param base_currency:
            Базовая валюта (по умолчанию "USD").
        :param currencies:
            Список валют, для которых нужны курсы.
            Если None — зависит от твоего тарифа.
        :param date:
            Опциональная дата в формате "YYYY-MM-DD" для исторического курса.
            Если не указано — берётся текущий курс.
        :return:
            dict в формате CurrencyAPI:
                {
                    "meta": {...},
                    "data": {
                        "EUR": {"value": 0.93, ...},
                        "UZS": {"value": 12800.0, ...},
                        ...
                    }
                }
        """
        params: Dict[str, Any] = {
            "base_currency": base_currency,
        }
        if currencies:
            params["currencies"] = ",".join(currencies)
        if date:
            params["date"] = date

        return self._get("latest", params=params)
