'''Client to interact with AirNow Air Quality API'''
from json.decoder import JSONDecodeError
from typing import Optional, cast

from aiohttp import ClientSession, ClientTimeout

from .errors import AirNowError, EmptyResponseError, InvalidJsonError, \
    InvalidKeyError
from .forecast import Forecast
from .observation import Observations

API_BASE_URL: str = 'https://www.airnowapi.org'
API_DEFAULT_TIMEOUT: int = 10


class WebServiceAPI:
    '''Client to interact with AirNow API'''
    def __init__(
        self, api_key: str, *, session: Optional[ClientSession] = None
    ) -> None:
        '''Initialize with Client Session and API Key'''
        self._api_key: Optional[str] = api_key
        self._session: Optional[ClientSession] = session

        self.forecast = Forecast(self._get)
        self.observations = Observations(self._get)

    async def _get(
        self, endpoint: str, *, base_url: str = API_BASE_URL, **kwargs
    ) -> list:
        '''Run a Request against the API'''
        kwargs.setdefault('params', {})
        kwargs['params']['API_KEY'] = self._api_key
        kwargs['params']['format'] = 'application/json'

        use_running_session = self._session and not self._session.closed

        session: ClientSession
        if use_running_session:
            session = cast(ClientSession, self._session)
        else:
            session = ClientSession(
                timeout=ClientTimeout(total=API_DEFAULT_TIMEOUT)
            )

        try:
            async with session.get(
                f'{base_url}/{endpoint}', **kwargs
            ) as resp:
                data = await resp.json(content_type=None)

        except JSONDecodeError:
            # The response can't be parsed as JSON, so we'll use its body text
            # in an error:
            response_text = await resp.text()
            raise InvalidJsonError(response_text)

        finally:
            if not use_running_session:
                await session.close()

        if isinstance(data, dict) and 'WebServiceError' in data:
            # Process an Error Message from the API server
            wsErr = data['WebServiceError']
            if isinstance(wsErr, list) and len(wsErr) > 0:
                if 'Message' in wsErr[0]:
                    if wsErr[0]['Message'] == 'Invalid API key':
                        raise InvalidKeyError(wsErr[0]['Message'])
                    elif wsErr[0]['Message'] == 'Request not authenticated':
                        raise InvalidKeyError(wsErr[0]['Message'])
                    else:
                        raise AirNowError(wsErr[0]['Message'])
                else:
                    raise AirNowError(str(wsErr[0]))
            else:
                raise AirNowError(str(wsErr))

        elif not isinstance(data, list):
            # We should get a list of Observation or Forecast objects
            raise InvalidJsonError(
                'Unexpected response type: %s' % (type(data))
            )

        elif len(data) == 0:
            # No objects were returned
            raise EmptyResponseError('No data was returned')

        # Return list of objects for further processing
        return data
