"""Windy functions."""

from datetime import datetime, timedelta
import logging

from aiohttp.client_exceptions import ClientError
from py_typecheck import checked

from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession

from .const import (
    PURGE_DATA,
    WINDY_ENABLED,
    WINDY_INVALID_KEY,
    WINDY_LOGGER_ENABLED,
    WINDY_NOT_INSERTED,
    WINDY_STATION_ID,
    WINDY_STATION_PW,
    WINDY_SUCCESS,
    WINDY_UNEXPECTED,
    WINDY_URL,
)
from .utils import update_options

_LOGGER = logging.getLogger(__name__)


class WindyNotInserted(Exception):
    """NotInserted state."""


class WindySuccess(Exception):
    """WindySucces state."""


class WindyApiKeyError(Exception):
    """Windy API Key error."""


def timed(minutes: int):
    """Simulate timedelta.

    So we can mock td in tests.
    """
    return timedelta(minutes=minutes)


class WindyPush:
    """Push data to Windy."""

    def __init__(self, hass: HomeAssistant, config: ConfigEntry) -> None:
        """Init."""
        self.hass = hass
        self.config = config

        """ lets wait for 1 minute to get initial data from station
            and then try to push first data to Windy
        """
        self.last_update: datetime = datetime.now()
        self.next_update: datetime = datetime.now() + timed(minutes=1)

        self.log: bool = self.config.options.get(WINDY_LOGGER_ENABLED, False)
        self.invalid_response_count: int = 0

    def verify_windy_response(
        self,
        response: str,
    ):
        """Verify answer form Windy."""

        if self.log:
            _LOGGER.info("Windy response raw response: %s", response)

        if "NOTICE" in response:
            raise WindyNotInserted

        if "SUCCESS" in response:
            raise WindySuccess

        if "Invalid API key" in response:
            raise WindyApiKeyError

        if "Unauthorized" in response:
            raise WindyApiKeyError

    def _covert_wslink_to_pws(self, indata: dict[str, str]) -> dict[str, str]:
        """Convert WSLink API data to Windy API data protocol."""
        if "t1ws" in indata:
            indata["wind"] = indata.pop("t1ws")
        if "t1wgust" in indata:
            indata["gust"] = indata.pop("t1wgust")
        if "t1wdir" in indata:
            indata["winddir"] = indata.pop("t1wdir")
        if "t1hum" in indata:
            indata["humidity"] = indata.pop("t1hum")
        if "t1dew" in indata:
            indata["dewpoint"] = indata.pop("t1dew")
        if "t1tem" in indata:
            indata["temp"] = indata.pop("t1tem")
        if "rbar" in indata:
            indata["mbar"] = indata.pop("rbar")
        if "t1rainhr" in indata:
            indata["precip"] = indata.pop("t1rainhr")
        if "t1uvi" in indata:
            indata["uv"] = indata.pop("t1uvi")
        if "t1solrad" in indata:
            indata["solarradiation"] = indata.pop("t1solrad")

        return indata

    async def push_data_to_windy(
        self, data: dict[str, str], wslink: bool = False
    ) -> bool:
        """Pushes weather data do Windy stations.

        Interval is 5 minutes, otherwise Windy would not accepts data.

        we are sending almost the same data as we received
        from station. But we need to do some clean up.
        """

        if self.log:
            _LOGGER.info(
                "Windy last update = %s, next update at: %s",
                str(self.last_update),
                str(self.next_update),
            )

        if self.next_update > datetime.now():
            return False

        purged_data = data.copy()

        for purge in PURGE_DATA:
            if purge in purged_data:
                _ = purged_data.pop(purge)

        if wslink:
            # WSLink -> Windy params
            self._covert_wslink_to_pws(purged_data)

        if (
            windy_station_id := checked(self.config.options.get(WINDY_STATION_ID), str)
        ) is None:
            _LOGGER.error("Windy API key is not provided! Check your configuration.")
            return False

        if (
            windy_station_pw := checked(self.config.options.get(WINDY_STATION_PW), str)
        ) is None:
            _LOGGER.error(
                "Windy station password is missing! Check your configuration."
            )
            return False

        request_url = f"{WINDY_URL}"

        purged_data["id"] = windy_station_id

        purged_data["time"] = "now"

        headers = {"Authorization": f"Bearer {windy_station_pw}"}

        if self.log:
            _LOGGER.info("Dataset for windy: %s", purged_data)
        session = async_get_clientsession(self.hass)
        try:
            async with session.get(
                request_url, params=purged_data, headers=headers
            ) as resp:
                status = await resp.text()
                try:
                    self.verify_windy_response(status)
                except WindyNotInserted:
                    # log despite of settings
                    _LOGGER.error(WINDY_NOT_INSERTED)

                except WindyApiKeyError:
                    # log despite of settings
                    _LOGGER.critical(WINDY_INVALID_KEY)

                    if not (
                        await update_options(
                            self.hass, self.config, WINDY_ENABLED, False
                        )
                    ):
                        _LOGGER.debug("Failed to set Windy option to false.")

                except WindySuccess:
                    if self.log:
                        _LOGGER.info(WINDY_SUCCESS)
                else:
                    if self.log:
                        _LOGGER.debug(WINDY_NOT_INSERTED)

        except ClientError as ex:
            _LOGGER.critical("Invalid response from Windy: %s", str(ex))
            self.invalid_response_count += 1
            if self.invalid_response_count > 3:
                _LOGGER.critical(WINDY_UNEXPECTED)
                if not await update_options(
                    self.hass, self.config, WINDY_ENABLED, False
                ):
                    _LOGGER.debug("Failed to set Windy options to false.")

        self.last_update = datetime.now()
        self.next_update = self.last_update + timed(minutes=5)

        if self.log:
            _LOGGER.info("Next update: %s", str(self.next_update))

        return True
