from models.modules.core import logger, request, InvalidUsage, make_response, app, url_for 
from models.dbmodels.user_model import *
from models.libs.jwt import *
from models.libs.mailing import *
from datetime import datetime
import os
import ast

class Auths():
    def confirms(token):
        try:
            resp = confirm_email_token(token)
            if resp == False:
                return make_response({
                    "error": "confirmation link expired or invalid"
                }, 400)
            
            userinfo = Auth.query.filter_by(email = resp).first()
            email = userinfo.email
            userinfo.isVerified = 1
            db.session.commit()
            db.session.close()

            welcome_msg = f'Welcome to our platform user {email}'
            send_email(
                "Welcome email", [email], welcome_msg
            )
            return make_response({
                "message": "validation complete",
            }, 200)
        except exc.SQLAlchemyError as e:
            raise Exception(e._message)
        except Exception as e:
            raise Exception(str(e))

    def login():
        email = request.json['email']
        password = request.json['password']
        user = Auth.query.filter_by(email = email).first()
        if not user:
            return make_response({
                "error": "user email is invaild"
            }, 404)

        if user.isVerified == 0:
            token = generate_email_token(email)
            link = url_for('verify', token=token, external=True)
            link = f"{app.config['CONFIRM_LINK']}../{link}"
            confirm_msg = f'Dear Customer,<br/>We have created a customer account for you on our website. To activate your account, please click the link below: <br /><a href="{link}">Click to confirm your email</a><br /><br /><br />Please note that the link expires in 24 hours.<br />We thank you for choosing Skittar<br /> <br /> <h2 style="font-weight:600">The Skittar Team</h2><br />'
            send_email(
                "Validate your account", [email], confirm_msg, True
            )
            return make_response({
                "error": "your account is not validated"
            }, 401)

        check = verify_password(user.password, password)
        if check == False:
            return make_response({
                "error": "password is incorrect"
            }, 401)
        del user.password
        del user.id

        token = generatejwt({
            "email": email,
            "isVerified": user.isVerified,
            "uid": user.uid
        })
        return jsonify({
            "jwt": token
        })

    def delete_account(uid):
        # email = request.json['email']
        try:
            user = Auth.query.filter_by(uid = uid).first()
            obj = User.query.filter_by(auth_id = uid).first()
            delivery = Delivery.query.filter_by(auth_id = uid).first()
            if not user or not obj:
                return make_response({
                    "error": "user account info not found"
                }, 400)
            db.session.delete(user)
            db.session.delete(delivery)
            db.session.delete(obj)
            db.session.commit()
            db.session.close()
            return make_response({
                "message": "user deleted successfully",
            }, 200)
        except exc.SQLAlchemyError as e:
            raise Exception(e._message)
        except Exception as e:
            raise Exception(str(e))

    def register():
        email = request.json['email']
        password = request.json['password']
        access = 'user' if request.json['access'] == '' else request.json['access']

        try:
            user = Auth.query.filter_by(email = email).first()
            if user:
                return make_response({
                    "error": "email in use by another account"
                }, 400)

            uid = datetime.now().strftime("%Y%m%d%H%M%S")
            new_user = Auth(uid, hash_password(password), email, False)
            db.session.add(new_user)
            db.session.flush()

            date = datetime.now()
            userinfo = User('', '', '', email, '', access, '', '', uid, '', date, "email")
            db.session.add(userinfo)
            db.session.commit()
            db.session.close()

            token = generate_email_token(email)
            link = url_for('verify', token=token, external=True)
            link = f"{app.config['CONFIRM_LINK']}../{link}"
            confirm_msg = 'Dear Customer,<br/>We have created a customer account for you on our website. To activate your account, please click the link below: <br /><a href="{}">Click to confirm your email</a><br /><br /><br />Please note that the link expires in 24 hours.<br />We thank you for choosing Skittar<br /> <br /> <h2 style="font-weight:600">The Skittar Team</h2><br />'.format(link)
            send_email(
                "Validate your account", [email], confirm_msg, True
            )
            return make_response({
                "message": "registration successful",
            }, 200)
        except exc.SQLAlchemyError as e:
            raise Exception(e._message)
        except Exception as e:
            raise Exception(str(e))

    def change_password():
        email = request.json['email']
        password = request.json['new_password']

        try:
            user = Auth.query.filter_by(email = email).first()
            if not user:
                return make_response({
                    "error": "email is invalid"
                }, 400)

            user.email = email
            user.password = hash_password(password)
            db.session.add(user)
            db.session.commit()
            db.session.close()
            return make_response({
                "message": "password changed",
            }, 200)
        except exc.SQLAlchemyError as e:
            raise Exception(e._message)
        except Exception as e:
            raise Exception(str(e))

    def confirm_password_link():
        try:
            resp = confirm_email_token(token, "email-forgot")
            if resp == False:
                return make_response({
                    "error": "confirmation link expired or invalid"
                }, 400)
            
            user = Auth.query.filter_by(email = resp).first()

            email = user.email
            password = "pwd" + str(int(datetime.now().strftime("%Y%m%d%H%M%S")))
            user.password = hash_password(password)
            db.session.add(user)
            db.session.commit()
            db.session.close()
            confirm_msg = "To access your account please use the password below<br /><b>Your new paswword is: <h3>{}</h3></b>".format(password)
            send_email(
                "New password for you account", [email], confirm_msg, True
            )
            return make_response({
                "message": "new password changed",
            }, 200)
        except exc.SQLAlchemyError as e:
            raise Exception(e._message)
        except Exception as e:
            raise Exception(str(e))

    def forgot_password():
        email = request.json['email']
        password = request.json['new_password']

        try:
            user = Auth.query.filter_by(email = email).first()
            if not user:
                return make_response({
                    "error": "email is invalid"
                }, 400)


            token = generate_email_token(email, 'email-forgot')
            link = url_for('forgot', token=token, external=True)
            return make_response({
                "message": "password reset link sent",
            }, 200)
        except exc.SQLAlchemyError as e:
            raise Exception(e._message)
        except Exception as e:
            raise Exception(str(e))

    def social_login(type = "google"):
        try:
            userData = {
                "email": request.json['email'],
                "id": request.json['uid'],
                "image": request.json['image'],
                "fullname": request.json['fullname'],
                "username": request.json['username'],
                "access": "user"
            }
            if type not in ["google", "facebook", "phone"]:
                return make_response({
                    "error": "invalid social authentication provider"
                }, 404)

            userInfo = User.query.filter(and_(
                User.auth_id.like(userData['id']), User.email.like(userData['email']))).first()
            
            if userInfo is None:
                date = datetime.now()
                authinfo = Auth(userData['id'], '', userData['email'], True)
                db.session.add(authinfo)
                db.session.commit()
                db.session.flush()

                useri = User(userData['username'], userData['fullname'], '', userData['email'], '', userData['access'], '', '', userData['id'], userData['image'], date, type)
                db.session.add(useri)
                db.session.commit()
                db.session.close()

            send_email("Welcome email", [userData['email']], f"Welcome to our platform user {userData['email']}")
            token = generatejwt({
                "email": userData['email'],
                "isVerified": True,
                "uid": int(userData['id'])
            })
            return {
                "jwt": token
            }
        except Exception as e:
            raise Exception(str(e))

class Users():
    def get_all_users():
        try:
            users = User.query.all()
            return {
                "message": users_scheme.dump(users)[::-1]
            }
        except exc.SQLAlchemyError as e:
            raise Exception(e._message)
        except Exception as e:
            raise Exception(str(e))

    def get_user_info(value):
        try:
            user = User.query.filter_by(auth_id = value).first()
            if not user:
                return make_response({
                    "error": "user account not found"
                }, 400)

            userInfo = user_scheme.dump(user)
            user_choices = ChoiceModel.query.filter(ChoiceModel.auth_id == user.auth_id).all()
            user_tokens = UserFcmModel.query.filter(and_(UserFcmModel.uid == user.auth_id)).first()

            userInfo['preferences'] = []
            for sources in user_choices:
                userChoiceObject = choice_model_scheme.dump(sources)
                userChoiceObject['info'] = ast.literal_eval(userChoiceObject['info'])
                userInfo['preferences'].append(userChoiceObject)
            
            userInfo['fcm_tokens'] = []
            if user_tokens is not None:
                tokens = user_fcm_scheme.dump(user_tokens)
                tokens['tokens'] = ast.literal_eval(tokens['tokens'])
                userInfo['fcm_tokens'] = tokens['tokens']

            return {
                "message": userInfo
            }
        except exc.SQLAlchemyError as e:
            raise Exception(e._message)
        except Exception as e:
            raise Exception(str(e))

    def get_user_by(value):
        try:
            users = User.query.filter(or_(
                User.email.like(value), User.auth_id.like(value), User.firstname.like(value),
                User.access.like(value), User.sex.like(value),User.country.like(value))).join(
                Auth, Auth.uid == User.auth_id).all()
            if not users:
                return make_response({
                    "error": "user account not found"
                }, 400)

            usersResults = []
            for user in users:
                userObject = user_scheme.dump(user)
                userObject['isverified'] = user.isVerified
                usersResults.append(userObject)
            return {
                "message": usersResults[::-1]
            }
        except exc.SQLAlchemyError as e:
            raise Exception(e._message)
        except Exception as e:
            raise Exception(str(e))

    def add_userinfo():
        email = request.json['email']
        auth_id = request.json['auth_id']
        sex = request.json['sex']
        phone = request.json['phone']
        bio = request.json['bio']
        access = request.json['access']
        country = request.json['country']
        username = request.json['username']
        fullname = request.json['fullname']
        image = request.json['image']
        try:
            auth = Auth.query.filter_by(email = email).first()
            if not auth:
                return make_response({
                    "error": "invalid email address"
                }, 400)
            
            date = datetime.now()
            userinfo = User(username, fullname, email, bio, sex, access, phone, country, auth_id, image, date, "email")
            db.session.add(userinfo)
            db.session.commit()
            db.session.close()
            return make_response({
                "message": "user info saved",
            }, 200)
        except exc.SQLAlchemyError as e:
            raise Exception(e._message)
        except Exception as e:
            raise Exception(str(e))

    def update_userinfo(uid):
        email = request.json['email']
        username = request.json['username']
        fullname = request.json['fullname']
        bio = request.json['bio']
        country = request.json['country']
        phone = request.json['phone']
        image = request.json['image']
        try:
            userInfo = User.query.filter_by(auth_id = uid).first()
            authinfo = Auth.query.filter_by(uid = uid).first()
            if not authinfo or not userInfo:
                return make_response({
                    "error": "invalid user id"
                }, 400)

            userInfo.fullname = fullname
            userInfo.username = username
            userInfo.bio = bio
            userInfo.phone = phone
            userInfo.image = image
            userInfo.country = country
            authinfo.email = userInfo.email = email
            db.session.add(userInfo)
            db.session.add(authinfo)
            db.session.commit()
            db.session.close()
            return make_response({
                "message": "user info updated",
            }, 200)
        except exc.SQLAlchemyError as e:
            raise Exception(e._message)
        except Exception as e:
            raise Exception(str(e))

class Choice(object):
    def save_user_choice():
        try:
            uid = request.json['uid']
            sources = request.json['sources']
            type = request.json['type']

            auth = Auth.query.filter_by(uid = uid).first()
            if not auth:
                return make_response({
                    "error": "invalid userid"
                }, 400)

            user_sms_sources = ChoiceModel.query.filter(and_(ChoiceModel.auth_id == uid, ChoiceModel.type == type)).first()
            if user_sms_sources is not None:
                return make_response({
                    "message": "user choice found",
                }, 400)
            sourceinfo = ChoiceModel(uid, str(sources), type)
            db.session.add(sourceinfo)
            db.session.commit()
            db.session.close()
            return make_response({
                "message": "user choice saved",
            }, 201)
        except exc.SQLAlchemyError as e:
            raise Exception(e._message)
        except Exception as e:
            raise Exception(str(e))

    def update_user_choice():
        try:
            uid = request.json['uid']
            sources = request.json['sources']
            type = request.json['type']

            auth = Auth.query.filter_by(uid = uid).first()
            if not auth:
                return make_response({
                    "error": "invalid userid"
                }, 400)

            user_sources = ChoiceModel.query.filter(and_(ChoiceModel.auth_id == uid, ChoiceModel.type == type)).first()
            if user_sources is None:
                return make_response({
                    "message": "user choice is invalid",
                }, 400)

            user_sources.info = str(sources)
            db.session.add(user_sources)
            db.session.commit()
            db.session.close()
            return make_response({
                "message": "user choice updated",
            }, 201)
        except exc.SQLAlchemyError as e:
            raise Exception(e._message)
        except Exception as e:
            raise Exception(str(e))

    def get_user_choice(uid):
        try:
            user_choice = ChoiceModel.query.filter(ChoiceModel.auth_id == uid).all()
            choicesResults = []
            for sources in user_choice:
                userChoiceObject = choice_model_scheme.dump(sources)
                userChoiceObject['info'] = ast.literal_eval(userChoiceObject['info'])
                choicesResults.append(userChoiceObject)
            return {
                "message": choicesResults[::-1]
            }
            
        except exc.SQLAlchemyError as e:
            raise Exception(e._message)
        except Exception as e:
            raise Exception(str(e))

class UserFcm(object):
    def save_user_choice():
        try:
            uid = request.json['uid']
            tokens = request.json['tokens']
            canPush = request.json['canPush']

            auth = Auth.query.filter_by(uid = uid).first()
            if not auth:
                return make_response({
                    "error": "invalid userid"
                }, 400)

            user_tokens = UserFcmModel.query.filter(and_(UserFcmModel.uid == uid)).first()
            if user_tokens is not None:
                user_tokens.tokens = str(tokens)
                user_tokens.canPush = canPush
                db.session.add(user_tokens)
            else:
                sourceinfo = UserFcmModel(uid, str(tokens), canPush)
                db.session.add(sourceinfo)
            
            db.session.commit()
            db.session.close()
            return make_response({
                "message": "user token saved",
            }, 201)
        except exc.SQLAlchemyError as e:
            raise Exception(e._message)
        except Exception as e:
            raise Exception(str(e))

    def update_user_choice():
        try:
            uid = request.json['uid']
            tokens = request.json['tokens']
            canPush = request.json['canPush']

            auth = Auth.query.filter_by(uid = uid).first()
            if not auth:
                return make_response({
                    "error": "invalid userid"
                }, 400)

            user_tokens = UserFcmModel.query.filter(and_(UserFcmModel.uid == uid)).first()
            if user_tokens is None:
                return make_response({
                    "message": "no fcm token found",
                }, 400)

            user_tokens.tokens = str(tokens)
            user_tokens.canPush = canPush
            db.session.add(user_tokens)
            db.session.commit()
            db.session.close()
            return make_response({
                "message": "user token updated",
            }, 201)
        except exc.SQLAlchemyError as e:
            raise Exception(e._message)
        except Exception as e:
            raise Exception(str(e))

    def get_user_choice(uid):
        try:
            user_tokens = UserFcmModel.query.filter(and_(UserFcmModel.uid == uid)).first()
            if user_tokens is None:
                return make_response({
                    "error": "invalid userid"
                }, 400)

            user_tokens = user_fcm_scheme.dump(user_tokens)
            user_tokens['tokens'] = ast.literal_eval(user_tokens['tokens'])
            return {
                "message": user_tokens
            }
            
        except exc.SQLAlchemyError as e:
            raise Exception(e._message)
        except Exception as e:
            raise Exception(str(e))
