import json
from datetime import datetime, timezone
from typing import Any, Dict, List
from influxdb_client_3 import Point
from juham.base import Base, MqttMsg
from juham.web import RCloud, RCloudThread


class OpenWeatherMapThread(RCloudThread):
    """Asynchronous thread for acquiring forecast from the open weathermap.org web service
    site."""

    _forecast_topic = ""
    _interval: float = 60
    _appid = "f3954a78a9eaa74096305a29054d8c88"
    _location = "Lahti,fi"
    _url = ""

    def __init__(self, client=None):
        """Construct with the given mqtt client. Acquires data from the visual
        crossing web service and publishes the forecast data to
        ```forecast_topic```.

        Args:
            client (object, optional): MQTT client. Defaults to None.
        """
        super().__init__(client)
        self.mqtt_client = client

    def init(
        self,
        forecast_topic: str,
        location: str,
        appid: str,
        url: str,
        update_interval: float,
    ):
        """Initialize thread

        Args:
            forecast_topic (str): topic to post the forecast
            location (str): geographic location
            appid (str): appid for the openweathermap service
            url (str): url to openweathermap service
            update_interval (float): update interval
        """
        self._forecast_topic = forecast_topic
        self._location = location
        self._appid = appid
        self._url = url
        self._interval = update_interval

    # @override
    def update_interval(self) -> float:
        return self._interval

    # @override
    def make_url(self) -> str:
        return f"{self._url}?q={self._location}&APPID={self._appid}"
        # r = requests.get(openWeather_url + '?q=Lahti,fi&APPID=f3954a78a9eaa74096305a29054d8c88').json()

    # @override
    def process_data(self, data: Any) -> None:
        super().process_data(data)
        r = data.json()

        forecast: List = []

        for r in r["list"]:
            dt: float = r["dt"]
            # dt_txt: str = r["dt_txt"]
            temp: float = r["main"]["temp"]
            feels: float = r["main"]["feels_like"]
            pressure: float = r["main"]["pressure"]
            humidity: float = r["main"]["humidity"]
            windspeed: float = r["wind"]["speed"]

            rec = {
                "id": "openweathermap",
                "ts": dt,
                "hour": datetime.fromtimestamp(dt, tz=timezone.utc).strftime("%H"),
                "day": datetime.fromtimestamp(dt, tz=timezone.utc).strftime("%Y%m%d%H"),
                "temp": temp - 273,  # from Kelvin to Celcius
                "feels": feels - 273,
                "pressure": pressure,
                "humidity": humidity,
                "windspeed": windspeed,
            }
            forecast.append(rec)

        msg = json.dumps(forecast)
        self.publish(self._forecast_topic, msg, qos=1, retain=False)
        self.info(
            f"OpenWeatherMap forecast for the next {len(forecast)} days published"
        )


class OpenWeatherMap(RCloud):
    """This class constructs a data acquisition object for reading weather
    forecasts from 'openweathermap.org'. It subscribes to the
    forecast topic and writes hourly data such as solar energy, temperature,
    and other attributes relevant to home automation into a time series
    database.

    Spawns an asynchronous thread to run queries at the specified
    update_interval.
    """

    workerThreadId: str = OpenWeatherMapThread.get_class_id()
    forecast_topic: str = Base.mqtt_root_topic + "/forecast"

    bucket: str = "OpenWeather"
    appid: str = "f3954a78a9eaa74096305a29054d8c88"
    location: str = "Lahti,fi"
    url: str = "https://api.openweathermap.org/data/2.5/forecast"
    update_interval: float = 12 * 3600

    def __init__(self, name="openweathermap"):
        super().__init__(name)

    def on_connect(self, client, userdata, flags, rc):
        super().on_connect(client, userdata, flags, rc)
        if rc == 0:
            self.subscribe(self.forecast_topic)

    def on_message(self, client, userdata, msg):
        if msg.topic == self.forecast_topic:
            em = json.loads(msg.payload.decode())
            self.on_forecast(em)
        else:
            super().on_message(client, userdata, msg)

    def on_forecast(self, em: dict) -> None:
        """Handle weather forecast data.

        Args:
            em (dict): forecast
        """

    def run(self):
        # create, initialize and start the asynchronous thread for acquiring forecast
        self.worker = Base.instantiate(OpenWeatherMap.workerThreadId)
        self.worker.init(
            self.forecast_topic,
            self.location,
            self.appid,
            self.url,
            self.update_interval,
        )
        super().run()

    def to_dict(self):
        data = super().to_dict()
        data["_openweathermap"] = {
            "topic": self.forecast_topic,
            "location": self.location,
            "appid": self.appid,
            "url": self.url,
            "interval": self.update_interval,
        }
        return data

    def from_dict(self, data):
        super().from_dict(data)
        if "_openweathermap" in data:
            for key, value in data["_openweathermap"].items():
                setattr(self, key, value)
