from typing import ClassVar, Iterable, Optional

from ..core.bases import BaseService
from ..core.validators import numerical
from .exchange_rates_conversion_response import ExchangeRatesConversionResponse
from .historical_exchange_rates_response import HistoricalExchangeRatesResponse
from .live_exchange_rates_response import LiveExchangeRatesResponse


class ExchangeRates(BaseService):
    """AbstractAPI exchange rates service.

    Used to looking up the latest exchange rates for 80+ currencies, getting
    historical exchange rates, and converting an arbitrary amount from one
    currency to another.

    Attributes:
        _subdomain: Exchange rates service subdomain.
    """
    _subdomain = "exchange-rates"
    _service_name_env_var: ClassVar[str] = "EXCHANGE_RATES"

    @staticmethod
    def _target_as_param(
        target: Optional[Iterable[str]] = None
    ) -> Optional[str]:
        """Builds 'target' URL query parameter.

        Builds a string that contains selected target currencies to be used
        as a URL query parameter.

        Args:
            target: Selected target currencies.

        Returns:
            Comma-separated string with all selected target currencies.
        """
        if target is None:
            return None

        return ",".join(target)

    def live(
        self,
        base: str,
        target: Optional[Iterable[str]] = None
    ) -> LiveExchangeRatesResponse:
        """Finds exchange rates from base currency to target currency/ies.

        Args:
            base: Base currency used to get the latest exchange rate(s) for.
                Uses the ISO 4217 currency standard (e.g., USD for United
                States Dollars).
            target: The target currency or currencies to get the exchange rate
                of versus the base currency. Like the base parameters, any
                currency passed here follows the ISO 4217 standard.

        Returns:
            VATValidationResponse representing API call response.
        """
        return self._service_request(
            _response_class=LiveExchangeRatesResponse,
            _action="live",
            base=base,
            target=self._target_as_param(target)
        )

    def convert(
        self,
        base: str,
        target: str,
        date: Optional[str] = None,
        base_amount: Optional[float] = None
    ) -> ExchangeRatesConversionResponse:
        """Converts amount of money from base currency to target currency/ies.

        Args:
            base: Base currency used to get the latest exchange rate(s) for.
                Uses the ISO 4217 currency standard (e.g., USD for United
                States Dollars).
            target: The target currency to convert the base_amount to.
                Like the base parameters, any currency passed here follows the
                ISO 4217 standard. Note that unlike the other endpoints,
                convert only accepts one target currency at a time.
            date: The historical date you'd like to get rates from, in the
                format of YYYY-MM-DD. If you leave this blank, it will use the
                latest available rate.
            base_amount: The amount of the base currency you would like to
                convert to the target currency.

        Returns:
            ExchangeRatesConversionResponse representing API call response.
        """
        if base_amount is not None:
            numerical.greater_or_equal("base_amount", base_amount, 0)

        return self._service_request(
            _response_class=ExchangeRatesConversionResponse,
            _response_class_kwargs={
                "date_included_in_request": date is not None
            },
            _action="convert",
            base=base,
            target=target,
            date=date,
            base_amount=base_amount
        )

    def historical(
        self,
        base: str,
        date: str,
        target: Optional[Iterable[str]] = None
    ) -> HistoricalExchangeRatesResponse:
        """Finds historical exchange rates from base to target currencies.

        Args:
            base: Base currency used to get the latest exchange rate(s) for.
                Uses the ISO 4217 currency standard (e.g., USD for United
                States Dollars).
            date: The historical date you’d like to get rates from, in the
                format of YYYY-MM-DD.
            target: The target currency or currencies to get the exchange rate
                of versus the base currency. Like the base parameters, any
                currency passed here follows the ISO 4217 standard.

        Returns:
            HistoricalExchangeRatesResponse representing API call response.
        """
        return self._service_request(
            _response_class=HistoricalExchangeRatesResponse,
            _action="historical",
            base=base,
            target=self._target_as_param(target),
            date=date
        )
