import json

import rethinkdb as rdb
import bcrypt
from flask import session, flash

from flask.ext.login import login_user, logout_user

from rethinkORM import RethinkModel

from util import gravatar_url
from services import db, cache


def to_json(res, first=False):
    """
    Jsonify query result.
    """
    res = to_dict(res, first)
    return json.dumps(res, separators=(',',':'))


def to_dict(cur, first=False):
    if not cur:
        return None
    ret = []
    for row in cur:
        ret.append(row)
    if ret and first:
        return ret[0]
    else:
        return ret


def cache_it(fn):
    def wrap(*args, **kw):
        key = "%s:%s" % (args[0].table, args[1])
        data = cache.get(key)
        # Assume strings are JSON encoded
        try:
            data = json.loads(data)
        except TypeError:
            pass
        except ValueError:
            pass

        if data is not None:
            return data
        else:
            data = fn(*args)
            print data
            ret = data
            if data is None:
                data = ''
            if not isinstance(data, basestring):
                try:
                    data = json.dumps(data, separators=(',', ':'))
                except TypeError:
                    pass
            cache.set(key, data)
            return ret
    return wrap


class BaseModel(RethinkModel):

    _conn = db

    def __init__(self, **kwargs):
        if not kwargs.get('conn'):
            kwargs['conn'] = db
        super(BaseModel, self).__init__(**kwargs)

    @classmethod
    def create(cls, **kwargs):
        return super(BaseModel, cls).create(**kwargs)

    @cache_it
    def get_by_id(self, id):
        return rdb.table(self.table).get(id).run(self._conn)

    def get_all(self, arg, index):
        return rdb.table(self.table).get_all(arg, index=index).run(self._conn)

    #@cache_it
    def get_one(self, arg, index):
        return rdb.table(self.table).get_all(arg, index=index).limit(1).run(self._conn)


class Site(BaseModel):
    table = 'sites'

    def get_by_name(self, name):
        return to_dict(self.get_one(name, 'name'), True)


class CurrentUser():
    id = None

    def __init__(self, id):
        self.id = id
        if id:
            user = User()
            session['user'] = user.get_by_id(id)
        #import sys
        #sys.exit()
        #session['user'] = to_dict(user.get_one(id, 'id'), True)

    def get_id(self):
        return self.id

    def is_active(self):
        return True if self.id else False

    def is_anonymous(self):
        return False if self.id else True

    def is_authenticated(self):
        return True if self.id else False

    @staticmethod
    def get(key):
        try:
            return session['user'][key]
        except KeyError:
            return None


class User(BaseModel):
    table = 'users'

    def get_by_email(self, email):
        return to_dict(self.get_one(email, 'email'), True)

    def get_by_username(self, username):
        return to_dict(self.get_one(username, 'username'), True)

    def login(self, login, password):
        pass

    @classmethod
    def get(cls, id):
        print id
        return cls(id=id)

    @classmethod
    def auth(cls, username, password):
        u = User()
        data = u.get_by_email(username)
        if not data:
            return False

        if bcrypt.checkpw(password, data['password']):
            cls.login(data['id'])
            return True
        else:
            return False

    @classmethod
    def register(cls, username, email, password):
        user = User()
        email = email.lower()
        if user.get_by_email(email):
            flash('Email is already taken')
            return False
        if user.get_by_username(username):
            flash('Username is already taken')
            return False

        # Create user and login
        u = User.create(email=email,
                        username=username,
                        password=bcrypt.hashpw(password, bcrypt.gensalt(10)),
                        avatar=gravatar_url(email))

        User.login(u.id)

    @classmethod
    def login(cls, id, data=None):
        login_user(CurrentUser(id), True)

    @classmethod
    def logout(cls):
        logout_user()
        session.pop('user', None)