from flask_sqlalchemy import SQLAlchemy
import bcrypt
import datetime
import hashlib
import os

db = SQLAlchemy()

class User(db.Model):
    __tablename__ = 'user'
    id = db.Column(db.Integer, primary_key=True)

    # User information
    email = db.Column(db.String, nullable=False, unique=True)
    password_digest = db.Column(db.String, nullable=False)

    # Session information
    session_token = db.Column(db.String, nullable=False, unique=True)
    session_expiration = db.Column(db.DateTime, nullable=False)
    update_token = db.Column(db.String, nullable=False, unique=True)

    # post and comments
    posts = db.relationship('Post', cascade='delete')
    comments = db.relationship('Comment', cascade='delete')

    def __init__(self, **kwargs):
        self.email = kwargs.get('email')
        self.password_digest = bcrypt.hashpw(str(kwargs.get('password')).encode('utf8'),
                                            bcrypt.gensalt(rounds=13))
        self.renew_session()

    # Used to randomly generate session/update tokens
    def _urlsafe_base_64(self):
        return hashlib.sha1(os.urandom(64)).hexdigest()

    # Generates new tokens, and resets expiration time
    def renew_session(self):
        self.session_token = self._urlsafe_base_64()
        self.session_expiration = datetime.datetime.now() + \
                                datetime.timedelta(days=1)
        self.update_token = self._urlsafe_base_64()

    def verify_password(self, password):
        return bcrypt.checkpw(str(password).encode('utf8'),
                              self.password_digest)

    # Checks if session token is valid and hasn't expired
    def verify_session_token(self, session_token):
        return session_token == self.session_token and \
            datetime.datetime.now() < self.session_expiration

    def verify_update_token(self, update_token):
        return update_token == self.update_token

    def serialize(self):
        return{
            'id': self.id,
            'email': self.email
        }

class Friendship(db.Model):
    __tablename__ = 'friendship'
    user_1_id = db.Column(db.Integer, db.ForeignKey('user.id'), primary_key=True, nullable=False)
    user_2_id = db.Column(db.Integer, db.ForeignKey('user.id'), primary_key=True, nullable=False)
    db.PrimaryKeyConstraint('user_1_id', 'user_2_id', name='unique_friendship') # ensure no duplicates
    db.CheckConstraint('user_1_id < user_2_id', name = 'checkoneway') # user1 < user2
    # db.UniqueConstraint('user_1_id', 'user_2_id', name = 'unique_friendship')
    status = db.Column(db.Integer, nullable=False) # 1 for pending, 2 for accepted, 3 for declined
    action_user = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) # who performs recent activity

class Post(db.Model):
    __tablename__ = 'post'
    id = db.Column(db.Integer, primary_key=True)
    text = db.Column(db.String, nullable=False)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    comments = db.relationship('Comment', cascade='delete')

    def __init__(self, **kwargs):
        self.text = kwargs.get('text', '')
        self.user_id = kwargs.get('user_id')

    def serialize(self):
        return{
            'id': self.id,
            'text': self.text,
            'comments': [comment.serialize() for comment in self.comments]
        }

class Comment(db.Model):
    __tablename__ = 'comment'
    id = db.Column(db.Integer, primary_key=True)
    text = db.Column(db.String, nullable=False)
    liked = db.Column(db.Integer, nullable=False)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    post_id = db.Column(db.Integer, db.ForeignKey('post.id'), nullable=False)

    def __init__(self, **kwargs):
        self.text = kwargs.get('text', '')
        self.liked = 0
        self.user_id = kwargs.get('user_id')
        self.post_id = kwargs.get('post_id')
        
    def serialize(self):
        return{
            'id': self.id,
            'text': self.text,
            'liked': self.liked,
            'post_id': self.post_id
        }


