"""Automower library using aiohttp."""
import logging
import time
from urllib.parse import quote_plus, urlencode

import aiohttp

_LOGGER = logging.getLogger(__name__)


AUTH_API_URL = "https://api.authentication.husqvarnagroup.dev/v1/oauth2/token"
TOKEN_URL = "https://api.authentication.husqvarnagroup.dev/v1/token"
AUTH_HEADERS = {
    "Content-Type": "application/x-www-form-urlencoded",
    "Accept": "application/json",
}

MOWER_API_BASE_URL = "https://api.amc.husqvarna.dev/v1/mowers/"

timeout = aiohttp.ClientTimeout(total=10)


class TokenError(Exception):
    """Raised when Husqvarna Authentication API request ended in error."""

    def __init__(self, status: str):
        """Initialize."""
        super().__init__(status)
        self.status = status


class GetAccessToken:
    """Class to get an acces token from the Authentication API."""

    def __init__(self, api_key, username, password):
        """Initialize the Auth-API and store the auth so we can make requests."""
        self.username = username
        self.password = password
        self.api_key = api_key
        self.auth_data = urlencode(
            {
                "client_id": self.api_key,
                "grant_type": "password",
                "username": self.username,
                "password": self.password,
            },
            quote_via=quote_plus,
        )

    async def async_get_access_token(self):
        """Return the token."""
        async with aiohttp.ClientSession(headers=AUTH_HEADERS) as session:
            async with session.post(AUTH_API_URL, data=self.auth_data) as resp:
                _LOGGER.debug("Resp.status get access token: %i", resp.status)
                if resp.status == 200:
                    result = await resp.json(encoding="UTF-8")
                    result["expires_at"] = result["expires_in"] + time.time()
                if resp.status >= 400:
                    raise TokenError(
                        f"The token is invalid, respone from Husqvarna Automower API: {resp.status}"
                    )
        result["status"] = resp.status
        return result


class RefreshAccessToken:
    """Class to renew the Access Token."""

    def __init__(self, api_key, refresh_token):
        """Initialize the Auth-API and store the auth so we can make requests."""
        self.api_key = api_key
        self.refresh_token = refresh_token
        self.auth_data = urlencode(
            {
                "client_id": self.api_key,
                "grant_type": "refresh_token",
                "refresh_token": self.refresh_token,
            },
            quote_via=quote_plus,
        )

    async def async_refresh_access_token(self):
        """Return the refresh token."""
        async with aiohttp.ClientSession(headers=AUTH_HEADERS) as session:
            async with session.post(AUTH_API_URL, data=self.auth_data) as resp:
                _LOGGER.debug("Resp.status refresh token: %i", resp.status)
                if resp.status == 200:
                    result = await resp.json(encoding="UTF-8")
                    result["expires_at"] = result["expires_in"] + time.time()
                if resp.status >= 400:
                    raise TokenError(
                        f"The token is invalid, respone from Husqvarna Automower API: {resp.status}"
                    )
        result["status"] = resp.status
        return result


class ValidateAccessToken:
    """Class to validate the Access Token."""

    def __init__(self, api_key, access_token, provider):
        """Initialize the Auth-API and store the auth so we can make requests."""
        self.api_key = api_key
        self.access_token = access_token
        self.provider = provider
        self.token_url = f"{TOKEN_URL}/{self.access_token}"
        self.token_headers = {
            "Authorization-Provider": "{0}".format(self.provider),
            "Accept": "application/json",
            "X-Api-Key": "{0}".format(self.api_key),
        }

    async def async_validate_access_token(self):
        """Returns information about the current token."""
        async with aiohttp.ClientSession(headers=self.token_headers) as session:
            async with session.get(self.token_url) as resp:
                _LOGGER.debug("Resp.status validate token: %i", resp.status)
                if resp.status == 200:
                    result = await resp.json(encoding="UTF-8")
                if resp.status >= 400:
                    raise TokenError(
                        f"The token is invalid, respone from Husqvarna Automower API: {resp.status}"
                    )
        result["status"] = resp.status
        return result


class GetMowerData:
    """Class to communicate with the Automower Connect API."""

    def __init__(self, api_key, access_token, provider, token_type):
        """Initialize the Communication API to get data."""
        self.api_key = api_key
        self.access_token = access_token
        self.provider = provider
        self.token_type = token_type
        self.mower_headers = {
            "Authorization": "{0} {1}".format(self.token_type, self.access_token),
            "Authorization-Provider": "{0}".format(self.provider),
            "Content-Type": "application/vnd.api+json",
            "X-Api-Key": "{0}".format(self.api_key),
        }

    async def async_mower_state(self):
        """Return the mowers data as a list of mowers."""
        async with aiohttp.ClientSession(
            headers=self.mower_headers, timeout=timeout
        ) as session:
            async with session.get(MOWER_API_BASE_URL) as resp:
                _LOGGER.debug("Resp.status mower data: %i", resp.status)
                if resp.status == 200:
                    result = await resp.json(encoding="UTF-8")
                if resp.status >= 400:
                    resp.raise_for_status()
        result["status"] = resp.status
        return result


class Return:
    """Class to send commands to the Automower Connect API."""

    def __init__(self, api_key, access_token, provider, token_type, mower_id, payload):
        """Initialize the API and store the auth so we can send commands."""
        self.api_key = api_key
        self.access_token = access_token
        self.provider = provider
        self.token_type = token_type
        self.mower_id = mower_id
        self.mower_headers = {
            "Authorization": "{0} {1}".format(self.token_type, self.access_token),
            "Authorization-Provider": "{0}".format(self.provider),
            "Content-Type": "application/vnd.api+json",
            "accept": "*/*",
            "X-Api-Key": "{0}".format(self.api_key),
        }
        self.mower_action_url = f"{MOWER_API_BASE_URL}{self.mower_id}/actions"
        self.payload = payload

    async def async_mower_command(self):
        """Send a payload to the mower to execute a command."""
        async with aiohttp.ClientSession(headers=self.mower_headers) as session:
            async with session.post(self.mower_action_url, data=self.payload) as resp:
                result = await session.close()
        _LOGGER.debug("Sent payload: %s", self.payload)
        _LOGGER.debug("Resp status mower command: %s", resp.status)
        return resp.status


class DeleteAccessToken:
    """Class to invalidate an access token."""

    def __init__(self, api_key, provider, access_token):
        """Initialize the Auth-API and store the auth so we can make requests."""
        self.api_key = api_key
        self.provider = provider
        self.delete_headers = {
            "Authorization-Provider": "{0}".format(self.provider),
            "X-Api-Key": "{0}".format(self.api_key),
            "Accept": "application/json",
        }
        self.access_token = access_token
        self.delete_url = f"{TOKEN_URL}/{self.access_token}"

    async def async_delete_access_token(self):
        """Delete the token."""
        async with aiohttp.ClientSession(headers=self.delete_headers) as session:
            async with session.delete(self.delete_url) as resp:
                _LOGGER.debug("Resp.status delete token: %i", resp.status)
                if resp.status == 204:
                    result = await resp.json(encoding="UTF-8")
                if resp.status >= 400:
                    resp.raise_for_status()
        return result
