import asyncio

from ratelimit import limits, sleep_and_retry

import asyncio
import aiohttp
from async_timeout import timeout

from . import exceptions

# pylint: disable=pointless-string-statement
""":copyright: (c) 2020 by Robert Hillis.
Not affiliated with Goal Zero. Credit for headers belongs to Goal Zero.
:license: Apache 2.0, see LICENSE for more details."""


HEADER = {
    "Content-Type": "application/json",
    "User-Agent": "YetiApp/1340 CFNetwork/1125.2 Darwin/19.4.0",
    "Connection": "keep-alive",
    "Accept": "application/json",
    "Accept-Language": "en-us",
    "Content-Length": "19",
    "Accept-Encoding": "gzip, deflate",
    "Cache-Control": "no-cache",
}
TIMEOUT = 10


class Yeti:
    """A class for handling connections with a Goal Zero Yeti."""

    def __init__(self, host: str, session: aiohttp.ClientSession, **data) -> None:
        """Initialize the connection to Goal Zero Yeti instance."""
        self._session = session
        self.control = {}
        self.data = {}
        self.endpoint = {}
        self.host = host
        self.payload = {}
        self.mac = None
        self.sysdata = {}

    async def init_connect(self) -> None:
        """Get states from Goal Zero Yeti instance."""
        self.endpoint = "/sysinfo"
        self.control = "name"
        await Yeti.send_get(self)
        self.control = "thingName"
        self.endpoint = "/state"
        await Yeti.send_get(self)

    async def get_state(self) -> None:
        """Get states from Goal Zero Yeti instance."""
        self.control = "thingName"
        self.endpoint = "/state"
        await Yeti.send_get(self)

    async def post_state(self, payload: dict[str, int]) -> None:
        """Post payload to Goal Zero Yeti instance."""
        self.endpoint = "/state"
        self.payload = payload
        await Yeti.send_post(self)

    async def sysinfo(self) -> None:
        """Get system info from Goal Zero Yeti instance."""
        self.endpoint = "/sysinfo"
        self.control = "name"
        await Yeti.send_get(self)

    async def reset(self) -> None:
        """Reset Goal Zero Yeti instance."""
        self.endpoint = "/factory-reset"
        await Yeti.send_get(self)

    async def reboot(self) -> None:
        """Reboot Goal Zero Yeti instance."""
        self.endpoint = "/rpc/Sys.Reboot"
        await Yeti.send_get(self)

    async def get_loglevel(self) -> None:
        """Get log level Goal Zero Yeti instance."""
        self.endpoint = "/loglevel"
        self.control = "app"
        await Yeti.send_get(self)

    async def post_loglevel(self, payload: dict[str, int]) -> None:
        """Post log level Goal Zero Yeti instance."""
        self.endpoint = "/loglevel"
        self.payload = payload
        await Yeti.send_post(self)

    async def join(self, payload: dict) -> None:
        """Join Goal Zero Yeti instance to Wifi network."""
        self.endpoint = "/join"
        self.payload = payload
        await Yeti.send_post(self)

    async def wifi(self) -> None:
        """Get available Wifi networks visible to Goal Zero Yeti instance."""
        self.endpoint = "/wifi"
        await Yeti.send_get(self)

    async def password_set(self, payload: str) -> None:
        """Set password to Zero Yeti instance."""
        self.payload = {"new_password": "" + payload + ""}
        self.endpoint = "/password-set"
        await Yeti.send_post(self)

    async def join_direct(self) -> None:
        """Directly Join Goal Zero Yeti instance on its Wifi."""
        self.endpoint = "/join-direct"
        await Yeti.send_post(self)

    async def start_pair(self) -> None:
        """Pair with Goal Zero Yeti instance."""
        self.endpoint = "/start-pair"
        await Yeti.send_post(self)

    @sleep_and_retry
    @limits(calls=1, period=2)
    async def send_get(self) -> None:
        """Send get request."""
        try:
            async with timeout(TIMEOUT, loop=asyncio.get_running_loop()):
                response = await self._session.get(
                    "http://" + self.host + self.endpoint
                )
                if self.endpoint == "/sysinfo":
                    self.sysdata = await response.json()
                else:
                    self.data = await response.json()
                    if self.control not in self.data.keys():
                        raise exceptions.InvalidHost()
                    if self.data["socPercent"] > 100:
                        self.data["socPercent"] = 100
        except (
            OSError,
            TypeError,
            aiohttp.client_exceptions.ClientConnectorError,
            asyncio.exceptions.TimeoutError,
        ) as err:
            raise exceptions.ConnectError() from err
        except aiohttp.client_exceptions.ServerDisconnectedError:
            pass

    @sleep_and_retry
    @limits(calls=1, period=2)
    async def send_post(self) -> None:
        """Send post request."""
        try:
            async with timeout(TIMEOUT, loop=asyncio.get_running_loop()):
                request = await self._session.post(
                    "http://" + self.host + self.endpoint,
                    json=self.payload,
                    headers=HEADER,
                )
                self.data = await request.json()
                if self.data["socPercent"] > 100:
                    self.data["socPercent"] = 100
        except (
            OSError,
            TypeError,
            aiohttp.client_exceptions.ClientConnectorError,
            asyncio.exceptions.TimeoutError,
        ) as err:
            raise exceptions.ConnectError() from err
        except aiohttp.client_exceptions.ServerDisconnectedError:
            pass
