import random
import string
import uuid
from hashlib import md5

from sqlalchemy.schema import ForeignKey, Table
from sqlalchemy import Column, Boolean, Unicode, Integer
from sqlalchemy.orm import synonym, relationship

from brewman.models.guidtype import GUID
from brewman.models import Base
from brewman.models import DBSession


def encrypt(val):
    return md5(val.encode('utf-8') + "Salt".encode('utf-8')).hexdigest()


class Client(Base):
    __tablename__ = 'auth_clients'

    id = Column('ClientID', GUID(), primary_key=True, default=uuid.uuid4)
    code = Column('Code', Integer, unique=True, nullable=False)
    name = Column('Name', Unicode(255), unique=True, nullable=False)
    enabled = Column('Enabled', Boolean, nullable=False)
    otp = Column('OTP', Integer)

    def __init__(self, code=None, name=None, enabled=False, otp=None):
        self.code = code
        self.name = name
        self.enabled = enabled
        self.otp = otp

    @classmethod
    def by_id(cls, id):
        if not isinstance(id, uuid.UUID):
            id = uuid.UUID(id)
        return DBSession.query(cls).filter(cls.id == id).first()

    @classmethod
    def by_code(cls, code):
        if not isinstance(code, int):
            code = int(code)
        return DBSession.query(cls).filter(cls.code == code).first()

    @classmethod
    def list(cls):
        return DBSession.query(cls).order_by(cls.name).all()

    @classmethod
    def enabled_list(cls):
        return DBSession.query(cls).filter(cls.enabled == True).order_by(cls.name).all()

    @classmethod
    def create(cls):
        client_code = random.randint(1000, 9999)
        otp = random.randint(1000, 9999)
        name = ''.join(random.choice(string.ascii_uppercase + string.digits) for x in range(6))
        client = Client(client_code, name, False, otp)
        DBSession.add(client)
        return client


user_group = Table(
    'auth_usergroups', Base.metadata,
    Column('UserGroupID', GUID(), primary_key=True, default=uuid.uuid4),
    Column('UserID', GUID(), ForeignKey('auth_users.UserID')),
    Column('GroupID', GUID(), ForeignKey('auth_groups.GroupID'))
)

role_group = Table(
    'auth_rolegroups', Base.metadata,
    Column('RoleGroupID', GUID(), primary_key=True, default=uuid.uuid4),
    Column('RoleID', GUID(), ForeignKey('auth_roles.RoleID')),
    Column('GroupID', GUID(), ForeignKey('auth_groups.GroupID'))
)


class User(Base):
    __tablename__ = 'auth_users'

    id = Column('UserID', GUID(), primary_key=True, default=uuid.uuid4)
    name = Column('Name', Unicode(255), unique=True)
    _password = Column('Password', Unicode(60))
    locked_out = Column('LockedOut', Boolean)

    groups = relationship("Group", secondary=user_group)

    def _get_password(self):
        return self._password

    def _set_password(self, password):
        self._password = encrypt(password)

    password = property(_get_password, _set_password)
    password = synonym('_password', descriptor=password)


    @property
    def __name__(self):
        return self.name

    def __init__(self, name=None, password=None, locked_out=None, id=None):
        self.name = name
        self.password = password
        self.locked_out = locked_out
        self.id = id

    @classmethod
    def by_name(cls, name):
        if not name:
            return None
        return DBSession.query(cls).filter(cls.name.ilike(name)).first()

    @classmethod
    def by_id(cls, id):
        if not isinstance(id, uuid.UUID):
            id = uuid.UUID(id)
        return DBSession.query(cls).filter(cls.id == id).one()

    @classmethod
    def auth(cls, name, password):
        if password is None:
            return False, None
        user = cls.by_name(name)
        if not user:
            return False, None
        if user.password != encrypt(password) or user.locked_out:
            return False, None
        else:
            return True, user

    @classmethod
    def list(cls):
        return DBSession.query(cls).order_by(cls.name).all()

    @classmethod
    def query(cls):
        return DBSession.query(cls)

    @classmethod
    def filtered_list(cls, name):
        query = DBSession.query(cls)
        for item in name.split():
            query = query.filter(cls.name.ilike('%' + item + '%'))
        return query.order_by(cls.name)


class Group(Base):
    __tablename__ = 'auth_groups'

    id = Column('GroupID', GUID(), primary_key=True, default=uuid.uuid4)
    name = Column('Name', Unicode(255), unique=True)


    def __init__(self, name=None, id=None):
        self.name = name
        self.id = id

    @classmethod
    def by_id(cls, id):
        return DBSession.query(cls).filter(cls.id == id).one()

    @classmethod
    def list(cls):
        return DBSession.query(cls).order_by(cls.name).all()


class Role(Base):
    __tablename__ = 'auth_roles'

    id = Column('RoleID', GUID(), primary_key=True, default=uuid.uuid4)
    name = Column('Name', Unicode(255), unique=True)

    groups = relationship("Group", secondary=role_group, backref="roles")

    def __init__(self, name=None, id=None):
        self.name = name
        self.id = id

    @classmethod
    def list(cls):
        return DBSession.query(cls).order_by(cls.name).all()

    @classmethod
    def by_id(cls, id):
        return DBSession.query(cls).filter(cls.id == id).one()

