import logging
import os
from Scraper import Scraper
from google.appengine.ext.webapp import template
from google.appengine.ext import webapp
from google.appengine.api import users
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.ext import db

class AchievementSource(db.Model):
    name = db.StringProperty()
    url = db.LinkProperty()
    created_by = db.SelfReferenceProperty(default=None)

class UserAccount(db.Model):
    user = db.UserProperty()
    source = db.ReferenceProperty(reference_class=AchievementSource)
    credentials = db.StringProperty()
    added = db.DateTimeProperty(auto_now_add=True)
    updated = db.DateTimeProperty()

class Achievement(db.Model):
    name = db.StringProperty()
    image = db.LinkProperty()
    description = db.StringProperty()
    source = db.ReferenceProperty(reference_class=AchievementSource)

class AwardedAchievement(db.Model):
    achievement = db.ReferenceProperty(reference_class=Achievement)
    user = db.UserProperty()
    awarded = db.DateTimeProperty()
    discovered = db.DateTimeProperty(auto_now_add=True)

class MainPage(webapp.RequestHandler):
    def get(self):
        user = users.get_current_user()

        if not user:
            self.redirect(users.create_login_url(self.request.uri))
            return

        template_values = {
            'is_admin': users.is_current_user_admin(),
            'sources': AchievementSource.all().filter('created_by = ', None),
            'accounts': UserAccount.gql("WHERE user = :user", user=user),
            'achievements': AwardedAchievement.all().filter('user = ', user)
                                              .order('-awarded')
        }

        path = os.path.join(os.path.dirname(__file__), 'index.html')
        self.response.out.write(template.render(path, template_values))

class AddSourcePage(webapp.RequestHandler):
    def post(self):

        if not users.is_current_user_admin():
            self.error(403)
            return

        source = AchievementSource(name=self.request.get('name'),
                                   url=self.request.get('url'))
        source.put()

        self.redirect('/')

class AddAccountPage(webapp.RequestHandler):
    def post(self):

        if not users.get_current_user():
            self.error(403)
            return

        source = db.get(db.Key(self.request.get('type')))

        account = UserAccount(user=users.get_current_user(),
                              source=source,
                              credentials=self.request.get('credentials'))
        account.put()

        self.redirect('/')

class DeleteAccountPage(webapp.RequestHandler):
    def post(self):

        if not users.get_current_user():
            self.error(403)
            return

        account = db.get(db.Key(self.request.get('key')))

        if account.user != users.get_current_user() and not users.is_current_user_admin():
            logging.warning("Account deletion attempted by :u1 for account owned by :u2",
                            u1 = users.get_current_user(), u2 = account.user)
            self.error(403)
            return

        account.delete()

        self.redirect('/')


class UpdatePage(webapp.RequestHandler):

    def post(self):
        account = db.get(db.Key(self.request.get('key')))

        if account.source.name == 'Spore':
            UpdatePage.merge_achievements(account, Scraper.scrape_spore(account.credentials))
        elif account.source.name == 'Steam':
            UpdatePage.merge_sources(account, Scraper.scrape_steam(account.credentials))
        elif account.source.created_by != None and account.source.created_by.name == 'Steam':
            UpdatePage.merge_achievements(account, Scraper.scrape_steam_game(account.credentials, account.source.url))

        self.redirect('/')

    @staticmethod
    def merge_sources(account, sources):
        for user_source in sources:
            UpdatePage.get_or_create_source(user_source, account)

    @staticmethod
    def merge_achievements(account, achievements):
        for awarded in achievements:
            achievement = UpdatePage.get_achievement(account.source, awarded)

            res = AwardedAchievement.gql("WHERE achievement = :ac AND user = :user",
                                         ac=achievement,
                                         user=account.user)

            if res.count(1) == 0:
                AwardedAchievement(achievement=achievement,
                                   user=account.user,
                                   awarded=awarded['date']).put()

    @staticmethod
    def get_achievement(source, achievement):
        res = Achievement.gql("WHERE name = :name AND source = :source",
                              name=achievement['title'],
                              source=source)

        if res.count(1) == 0:
            res = Achievement(name=achievement['title'],
                              image=achievement['img'],
                              description=achievement['desc'],
                              source=source)
            res.put()
        else:
            res = res.get()

        return res

    @staticmethod
    def get_or_create_source(source_info, account):
        source = AchievementSource.gql("WHERE name = :name", name=source_info['name'])

        if source.count(1) == 0:
            source = AchievementSource(name = source_info['name'],
                                       url = source_info['url'],
                                       created_by = account.source)
            source.put()
        else:
            source = source.get()

        res = UserAccount.gql("WHERE source = :source AND user = :user AND "
                            + "credentials = :creds", source = source,
                                                      user = account.user,
                                                      creds = account.credentials)

        if res.count(1) == 0:
            res = UserAccount(user = account.user,
                              source = source,
                              credentials = account.credentials)
            res.put()
        else:
            res = res.get()

        return res

application = webapp.WSGIApplication([('/', MainPage),
                                      ('/admin/source/add', AddSourcePage),
                                      ('/worker/update', UpdatePage),
                                      ('/account/add', AddAccountPage),
                                      ('/account/delete', DeleteAccountPage)],
                                     debug=True)

def main():
    run_wsgi_app(application)

if __name__ == "__main__":
    main()