from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey

from sqlalchemy.ext.declarative import declared_attr
from sqlalchemy.orm import relationship
from radar.auth.passwords import check_password_hash, generate_password_hash
from radar.database import db
from radar.models import OrganisationUser, CohortUser
from radar.models.common import ModifiedDateMixin, CreatedDateMixin


class UserCreatedUserMixin(object):
    @declared_attr
    def created_user_id(cls):
        # Nullable as it is a self-reference
        return Column(Integer, ForeignKey('users.id'), nullable=True)

    @declared_attr
    def created_user(cls):
        return relationship('User', primaryjoin="User.id == %s.created_user_id" % cls.__name__, remote_side='User.id', post_update=True)


class UserModifiedUserMixin(object):
    @declared_attr
    def modified_user_id(cls):
        # Nullable as it is a self-reference
        return Column(Integer, ForeignKey('users.id'), nullable=True)

    @declared_attr
    def modified_user(cls):
        return relationship('User', primaryjoin="User.id == %s.modified_user_id" % cls.__name__, remote_side='User.id', post_update=True)


class User(db.Model, UserCreatedUserMixin, UserModifiedUserMixin, CreatedDateMixin, ModifiedDateMixin):
    __tablename__ = 'users'

    id = Column(Integer, primary_key=True)
    username = Column(String, nullable=False, unique=True)
    password_hash = Column(String, nullable=False)
    email = Column(String, nullable=False)
    first_name = Column(String)
    last_name = Column(String)
    is_admin = Column(Boolean, default=False, nullable=False)
    is_bot = Column(Boolean, default=False, nullable=False)
    is_enabled = Column(Boolean, default=True, nullable=False)

    reset_password_token = Column(String)
    reset_password_date = Column(DateTime)

    force_password_change = Column(Boolean, default=False, nullable=False)

    organisation_users = relationship('OrganisationUser', back_populates='user', foreign_keys=[OrganisationUser.user_id])
    cohort_users = relationship('CohortUser', back_populates='user', foreign_keys=[CohortUser.user_id])

    def __init__(self, *args, **kwargs):
        super(User, self).__init__(*args, **kwargs)
        self._password = None

    @property
    def organisations(self):
        return [x.organisation for x in self.organisation_users]

    @property
    def cohorts(self):
        return [x.cohort for x in self.cohort_users]

    @property
    def password(self):
        return getattr(self, '_password', None)

    @password.setter
    def password(self, value):
        self._password = value
        self.password_hash = generate_password_hash(value)
        self.reset_password_token = None
        self.force_password_change = False

    def set_initial_password(self, value):
        self._password = value
        self.force_password_change = True

    def check_password(self, password):
        return check_password_hash(self.password_hash, password)

    @classmethod
    def is_authenticated(cls):
        return True


class AnonymousUser(object):
    @classmethod
    def is_authenticated(cls):
        return False

@@ -125,16 +125,66 @@
 ed_attr%0A
+from sqlalchemy.ext.hybrid import hybrid_property%0A
 from sql
@@ -1402,16 +1402,17 @@
 ue)%0A    
+_
 username
@@ -1413,32 +1413,44 @@
 ername = Column(
+'username', 
 String, nullable
@@ -1525,16 +1525,17 @@
 se)%0A    
+_
 email = 
@@ -1533,32 +1533,41 @@
 _email = Column(
+'email', 
 String, nullable
@@ -2336,16 +2336,466 @@
 = None%0A%0A
+    @hybrid_property%0A    def username(self):%0A        return self._username%0A%0A    @username.setter%0A    def username(self, username):%0A        if username is not None:%0A            username = username.lower()%0A%0A        self._username = username%0A%0A    @hybrid_property%0A    def email(self):%0A        return self._email%0A%0A    @email.setter%0A    def email(self, email):%0A        if email is not None:%0A            email = email.lower()%0A%0A        self._email = email%0A%0A
     @pro

Ensure username and email are lowercase