import asyncio

import aiohttp
from sqlalchemy import Column, Float, String

from seabird.plugin import Plugin, CommandMixin

from .db import Base, DatabasePlugin
from .utils import fetch_location, LocationException, Location


FORECAST_URL = "https://api.forecast.io/forecast/{}/{:.4f},{:.4f}"


class WeatherLocation(Base):
    __tablename__ = 'weather_locations'

    nick = Column(String, primary_key=True)
    address = Column(String)
    lat = Column(Float)
    lon = Column(Float)


class WeatherPlugin(Plugin, CommandMixin):
    def __init__(self, bot):
        super().__init__(bot)

        self.key = bot.config['FORECAST_KEY']

        self.db = self.bot.load_plugin(DatabasePlugin)

    async def fetch_location(self, msg):
        search_loc = msg.trailing.strip()
        loc = None
        if not search_loc:
            with self.db.session() as session:
                db_loc = session.query(WeatherLocation).filter(WeatherLocation.nick == msg.identity.name).one_or_none()

                if not db_loc:
                    self.bot.mention_reply(msg, 'No stored location found.')
                    return

                loc = Location(db_loc.address, db_loc.lat, db_loc.lon)

        if loc is None:
            try:
                loc = await fetch_location(search_loc)
            except LocationException as e:
                self.bot.mention_reply(msg, e)
                return None

        # Update the stored location for the given nick
        with self.db.session() as session:
            weather_loc, _ = session.get_or_create(WeatherLocation,
                                                   nick=msg.identity.name)
            weather_loc.address = loc.address
            weather_loc.lat = loc.lat
            weather_loc.lon = loc.lon
            session.add(weather_loc)
            session.flush()

        return loc

    def cmd_forecast(self, msg):
        loop = asyncio.get_event_loop()
        loop.create_task(self.forecast_callback(msg))

    async def forecast_callback(self, msg):
        loc = await fetch_location(msg)

    def cmd_weather(self, msg):
        loop = asyncio.get_event_loop()
        loop.create_task(self.weather_callback(msg))

    async def weather_callback(self, msg):
        loc = await self.fetch_location(msg)
        if loc is None:
            return

        async with aiohttp.get(FORECAST_URL.format(self.key, loc.lat, loc.lon)) as resp:
            if resp.status != 200:
                self.bot.mention_reply(msg, 'Could not get weather data.')
                return

            data = await resp.json()

            today = data['daily']['data'][0]
            currently = data['currently']

            self.bot.mention_reply(
                msg,
                "{}. Currently {:.1f}. High {:.2f}, Low {:.2f}, "
                "Humidity {:.0f}. {}.".format(
                    loc.address,
                    currently['temperature'],
                    today['temperatureMax'],
                    today['temperatureMin'],
                    currently['humidity']*100,
                    currently['summary'],
                ),
            )

@@ -7,16 +7,42 @@
  asyncio
+%0Afrom datetime import date
 %0A%0Aimport
@@ -1067,36 +1067,32 @@
     
-self.bot.mention_reply(msg, 
+raise LocationException(
 'No 
@@ -1119,35 +1119,8 @@
 d.')
-%0A                    return
 %0A%0A  
@@ -1217,29 +1217,8 @@
 ne:%0A
-            try:%0A    
     
@@ -1267,126 +1267,8 @@
 loc)
-%0A            except LocationException as e:%0A                self.bot.mention_reply(msg, e)%0A                return None
 %0A%0A  
@@ -1879,32 +1879,49 @@
 ack(self, msg):%0A
+        try:%0A    
         loc = aw
@@ -1916,32 +1916,37 @@
     loc = await 
+self.
 fetch_location(m
@@ -1948,16 +1948,1013 @@
 ion(msg)
+%0A        except LocationException as e:%0A            self.bot.mention_reply(msg, e)%0A            return%0A%0A        async with aiohttp.get(FORECAST_URL.format(self.key, loc.lat, loc.lon)) as resp:%0A            if resp.status != 200:%0A                self.bot.mention_reply(msg, 'Could not get weather data.')%0A                return%0A%0A            data = await resp.json()%0A%0A            self.bot.mention_reply(msg, '3 day forecast for %7B%7D.'.format(loc.address))%0A            for day in data%5B'daily'%5D%5B'data'%5D%5B:3%5D:%0A                weekday = date.fromtimestamp(day%5B'time'%5D).strftime('%25A')%0A                print(day)%0A%0A                self.bot.mention_reply(%0A                    msg,%0A                    %22%7B%7D: High %7B:.2f%7D, Low %7B:.2f%7D, Humidity %7B:.0f%7D. %7B%7D%22.format(%0A                        weekday,%0A                        day%5B'temperatureMax'%5D,%0A                        day%5B'temperatureMin'%5D,%0A                        day%5B'humidity'%5D*100,%0A                        day%5B'summary'%5D,%0A                    ),%0A                )
 %0A%0A    de
@@ -3108,32 +3108,49 @@
 ack(self, msg):%0A
+        try:%0A    
         loc = aw
@@ -3182,39 +3182,97 @@
 sg)%0A        
-if loc is None:
+except LocationException as e:%0A            self.bot.mention_reply(msg, e)
 %0A           

Update weather and forecast commands