import json
from datetime import datetime

import requests

from exceptions import ServerCommunicationError, NoArgError, WeatherStationNotFoundError

token: str


def __init__():
    with open("openweathermap_token") as openweathermap_token:
        global token
        token = openweathermap_token.read()


class HourlyWeather:
    time: datetime
    temperature: float
    feels_like: float
    wind_speed: float
    probability_of_precipitation: float
    rain: float
    main: str
    description: str

    def __init__(self, json_data: json):
        self.time = datetime.fromtimestamp(json_data['dt'])
        self.temperature = json_data['temp']
        self.feels_like = json_data['feels_like']
        self.wind_speed = json_data['wind_speed']
        self.probability_of_precipitation = json_data['pop']
        if 'rain' in json_data:
            self.rain = json_data['rain']['1h']
        else:
            self.rain = 0
        self.main = json_data['weather'][0]['main']
        self.description = json_data['weather'][0]['description']

    def __str__(self):
        string = ""
        string += "*{time}*: {main} | {temperature:.0f}°C 🧑: {feels_like:.0f}°C" \
            .format(time=self.time.strftime("%Hh"),
                    main=self.main,
                    temperature=self.temperature,
                    feels_like=self.feels_like)
        if self.probability_of_precipitation != 0:
            string += " ☔: {pop:.0f}% {rain}mm".format(pop=100 * self.probability_of_precipitation, rain=self.rain)

        string += " 💨{wind_speed:.1f}km/h".format(wind_speed=3.6 * self.wind_speed)
        return string


class DailyWeather:
    time: datetime
    temp: dict
    feels_like: float
    wind_speed: float
    probability_of_precipitation: float
    rain: float
    main: str
    description: str

    def __init__(self, json_data: json):
        self.time = datetime.fromtimestamp(json_data['dt'])
        self.temp = {'min': json_data['temp']['min'], 'day': json_data['temp']['day'], 'max': json_data['temp']['max']}
        self.feels_like = json_data['feels_like']['day']
        self.wind_speed = json_data['wind_speed']
        self.probability_of_precipitation = json_data['pop']
        if 'rain' in json_data:
            self.rain = json_data['rain']
        else:
            self.rain = 0
        self.main = json_data['weather'][0]['main']
        self.description = json_data['weather'][0]['description']

    def __str__(self):
        string = ""
        string += "*{time}*: {main} | {min}°C - {day}°C - {max}°C 🧑: {feels_like_day:.0f}°C" \
            .format(time=self.time.strftime("%d"),
                    main=self.main,
                    min=self.temp['min'],
                    day=self.temp['day'],
                    max=self.temp['max'],
                    feels_like_day=self.feels_like)
        if self.probability_of_precipitation != 0:
            string += " ☔: {pop:.0f}% {rain}mm".format(pop=100 * self.probability_of_precipitation, rain=self.rain)
        string += " 💨: {wind_speed:.1f}km/h".format(wind_speed=3.6 * self.wind_speed)
        return string


class TotalWeather:
    hourly_weather = []
    daily_weather = []

    def __init__(self, json_data: json):
        for single_daily_weather in json_data['daily'][2:]:
            self.daily_weather.append(DailyWeather(single_daily_weather))
        for single_hourly_weather in json_data['hourly']:
            self.hourly_weather.append(HourlyWeather(single_hourly_weather))

    def __str__(self):
        string = ""
        short_hourly_weather = self.hourly_weather[:5] + self.hourly_weather[6:20:2] + self.hourly_weather[21::4]
        day_before = short_hourly_weather[0].time.strftime("%d")
        for weather in short_hourly_weather:
            if day_before != weather.time.strftime("%d"):
                string += '\n'
            string += (str(weather) + '\n')
            day_before = weather.time.strftime("%d")
        string += '\n'
        for weather in self.daily_weather:
            string += (str(weather) + '\n')
        return string

    def clear(self):
        self.hourly_weather.clear()
        self.daily_weather.clear()


class City:
    cid: int
    name: str
    lat: int
    lon: int
    weather = TotalWeather

    def __init__(self, cid: int, name: str, lat: int, lon: int):
        self.cid = cid
        self.name = name
        self.lat = lat
        self.lon = lon

    def query_weather(self):
        request = requests.get(
            "http://api.openweathermap.org/data/2.5/onecall?\
            lang=de&units=metric&exclude=minutely&lat={lat}&lon={lon}&appid={token}".format(
                lat=self.lat,
                lon=self.lon, token=token))
        if request.status_code != 200:
            raise ServerCommunicationError
        self.weather = TotalWeather(request.json())

    def print_weather(self):
        print(self.weather)


def get_city_by_name(city_name: str) -> City:
    with open("city.list.json", "r") as city_list_file:
        city_data = city_list_file.read()
    city_json = json.loads(city_data)
    city_list = list(filter(lambda x: x['name'].casefold() == city_name.casefold(), city_json))
    if len(city_list) == 0:
        city_list = list(filter(lambda x: (x['name'].casefold().find(city_name.casefold())) != -1, city_json))
    if len(city_list) == 0:
        return City(cid=-1, name="none", lat=0, lon=0)
    return City(cid=city_list[0]['id'], name=city_list[0]['name'], lat=city_list[0]['coord']['lat'],
                lon=city_list[0]['coord']['lon'])


def check_city_list():
    with open("city.list.json", "r") as city_list_file:
        city_data = city_list_file.read()
    city_json = json.loads(city_data)
    de_city_list = list()
    for city in city_json:
        # if city['country'] == "DE":
        de_city_list.append(city)
    already_mentioned_city_list = []
    for current_city in de_city_list:
        matched_city_list = list(
            filter(
                lambda x: x['name'] == current_city['name'] and current_city['coord']['lat'] - 1 > x['coord'][
                    'lat'] >
                          current_city['coord']['lat'] + 1 and current_city['coord']['lon'] - 1 > x['coord'][
                              'lon'] >
                          current_city['coord']['lon'] + 1, de_city_list))
        if len(matched_city_list) > 1 and current_city['name'] not in already_mentioned_city_list:
            print(matched_city_list)
            already_mentioned_city_list.append(current_city['name'])

    # city_list = get_city_list_by_name("Berlin")
    # print(city_list[0])
    # for city in city_list:
    #     if city['country'] == "DE":
    #         print(city['name'] + " in " + city['country'] + " is " + str(city['id']) + " at " + str(
    #             city['coord']['lat']) + "," + str(city['coord']['lon']))


def handle_weather(update, context):
    if len(context.args) == 0:
        raise NoArgError
    requested_city = get_city_by_name(' '.join(context.args))
    if requested_city.name == "none":
        raise WeatherStationNotFoundError
    requested_city.query_weather()
    update.message.reply_text(str(requested_city.weather), disable_notification=True, parse_mode="Markdown")

    requested_city.weather.clear()
