"""Iotty middleware for CloudApi."""

from abc import ABC, abstractmethod
import logging
from typing import Any

from aiohttp import ClientResponse, ClientSession
from iottycloud.errors import (
    BadRequestException,
    ForbiddenException,
    NotFoundException,
    UnauthorizedException,
    CloudError,
)
from iottycloud.utils import Factory
from iottycloud.verbs import HTTP_HEADER_CLIENT_ID

_LOGGER = logging.getLogger(__name__)


class CloudApi(ABC):
    """Class to make authenticated requests to iotty CloudApi."""

    def __init__(self, websession: ClientSession, host: str, client_id: str) -> None:
        """Initialize the auth."""

        if websession is None:
            raise ValueError("websession")
        if host is None:
            raise ValueError("host")
        if client_id is None:
            raise ValueError("client_id")

        self.websession = websession
        self.host = host
        self._client_id = client_id

    @abstractmethod
    async def async_get_access_token(self) -> str:
        """Return a valid access token."""

    async def get_devices(self) -> list:
        """Return iotty devices for a specific user."""

        response = await self.__request("GET", "api/getdevices")

        await self.__handle_error(response)

        devices = await response.json()
        _LOGGER.debug("Got %d iotty Devices", len(devices))

        ret = []
        for _d in devices:
            _LOGGER.debug("Creating device from %s", _d)
            new_device = Factory.create_device(_d)
            if new_device is not None:
                ret.append(new_device)

        return ret

    async def get_status(self, device_id: str) -> Any:
        """Check device status EP."""
        response = await self.__request("GET", f"api/device/{device_id}/getstatus")

        await self.__handle_error(response)

        return await response.json()

    async def command(self, device_id: str, command: str, body: dict[str, Any] = None) -> Any:
        """Issue a command to a iotty device EP."""
        response = await self.__request(
            "POST",
            f"api/device/{device_id}/command/{command}",
            body=body,
        )

        _LOGGER.debug("Response from server: %s", response.status)

        await self.__handle_error(response)

        return await response.json()

    async def __handle_error(self, response: ClientResponse) -> None:
        """Handle possible errors from a request."""

        _LOGGER.debug("Response from server: %d", response.status)

        if response.status / 100 > 2:
            _LOGGER.warning(
                "Unable to fecth devices from server (Http status %d)", response.status)
            _LOGGER.debug(
                "Server replied: %s", await response.text())

        if response.status == 400:
            raise BadRequestException("Bad request")
        if response.status == 401:
            raise UnauthorizedException("Unauthorized")
        if response.status == 403:
            raise ForbiddenException("Forbidden")
        if response.status == 404:
            raise NotFoundException("Not found")
        if response.status / 100 == 5:
            raise CloudError("Internal server error")

    async def __request(self, method: str, url: str, body: dict[str, Any] = None, **kwargs) -> ClientResponse:
        """Make a request."""
        headers = kwargs.get("headers")

        if headers is None:
            headers = {}
        else:
            headers = dict(headers)

        access_token = await self.async_get_access_token()

        headers["Authorization"] = f"Bearer {access_token}"
        headers[HTTP_HEADER_CLIENT_ID] = self._client_id
        headers["Content-Type"] = "application/json"

        return await self.websession.request(
            method,
            f"{self.host}/{url}",
            **kwargs,
            json=body,
            headers=headers,
        )
