import threading
from datetime import datetime
from sqlalchemy import DateTime, cast, func
from validate_email import validate_email
from flask_mail import Message
from flask import current_app, render_template, url_for
from app import db, mail
from app.models import User
from .lock import Lock
from .reservation import ReservationController
from .record import RecordController
from ..exceptions import ActiveReservationExistsError, \
    ActiveRecordExistsError, NoActiveRecordError, ReservationExpiredError, \
    NoFreeWorkplaceError, UserRegistrationInvalidDataError


class UserController():

    @staticmethod
    def check_user_data(name, email, telegram_id=None, password=None, password2=None):
        errors = []

        if name is None or len(name) < 3:
            errors.append('name')
        if not validate_email(email_address=email, \
                check_regex=True, \
                check_mx=False, \
                use_blacklist=True, \
                debug=False) or \
                User.query.filter(User.email == email).count():
            errors.append('email')
        if telegram_id is not None and User.query.filter(User.telegram_id == telegram_id).count() > 0:
            errors.append('telegram_id')
        if telegram_id is None and (password is None or len(password) == 0):
            errors.append('password')
        if password != password2:
            errors.append('password2')

        if len(errors) > 0:
            raise UserRegistrationInvalidDataError(errors)


    @classmethod
    def create(cls, name, email, telegram_id=None, telegram_chat_id=None, password=None):
        cls.check_user_data( \
            name=name, \
            email=email, \
            telegram_id=telegram_id, \
            password=password, \
            password2=password)

        user = User()
        user.name = name
        user.email = email
        user.telegram_id = telegram_id
        user.telegram_chat_id = telegram_chat_id
        user.generate_new_token()

        if password is not None:
            user.set_password(password)

        db.session.add(user)
        db.session.commit()

        cls.send_confirm_email(user)


    @staticmethod
    def update(user, name=None, email=None, telegram_chat_id=None):
        if name:
            user.name = name
        if email:
            user.email = email
            user.is_confirmed = False
        if telegram_chat_id:
            user.telegram_chat_id = telegram_chat_id
        db.session.commit()


    @staticmethod
    def get(user_id):
        return User.query.get(user_id)


    @staticmethod
    def get_by_email(email):
        return User.query.filter(User.email == email).first()


    @staticmethod
    def get_by_telegram_user_id(telegram_user_id):
        return User.query.filter(User.telegram_id == telegram_user_id).first()


    @classmethod
    def verify_confirm_email(cls, email, token):
        user = cls.get_by_email(email)
        return user is not None and user.is_token_valid(token)


    @classmethod
    def confirm_email(cls, email, token):
        user = cls.get_by_email(email)

        if user is None or not user.is_token_valid(token):
            return False
        
        user.is_confirmed = True
        user.generate_new_token()
        db.session.commit()
        return True


    @classmethod
    def reset_password(cls, email, token, password):
        user = cls.get_by_email(email)

        if user is None or not user.is_token_valid(token):
            return False

        user.set_password(password)
        user.generate_new_token()
        db.session.commit()
        return True


    @staticmethod
    def reserve(user):
        if ReservationController.has_active_reservation(user):
            raise ActiveReservationExistsError
        if RecordController.has_active_record(user):
            raise ActiveRecordExistsError

        return ReservationController.create(user)


    @staticmethod
    def start_record(user):
        if RecordController.has_active_record(user):
            raise ActiveRecordExistsError
        reservation = ReservationController.get_active_reservation(user)

        if reservation is not None:
            RecordController.create_from_reservation(reservation)
        else:
            RecordController.create(user)


    @staticmethod
    def terminate_record(user):
        record = RecordController.get_active_record(user)

        if record is None:
            raise NoActiveRecordError

        RecordController.terminate(record)


    @classmethod
    def unlink_telegram_user_id(cls, telegram_user_id):
        user = cls.get_by_telegram_user_id(telegram_user_id)

        if user:
            user.telegram_id = None
            user.telegram_chat_id = None
            db.session.commit()


    @staticmethod
    def get_password_reset_link(user):
        user.generate_new_token()
        db.session.commit()
        return url_for('login.password_reset', token=user.token, email=user.email, _external=True)


    @classmethod
    def send_password_reset_email(cls, user):
        password_reset_link = cls.get_password_reset_link(user)
        msg = Message(subject='[Bastli Bouncer] Reset Password',
                  body=render_template('email/password_reset.txt', user=user, password_reset_link=password_reset_link),
                  recipients=[user.email])
        print(password_reset_link)
        mail.send(msg)


    @staticmethod
    def send_confirm_email(user):
        msg = Message(
            subject='[Bastli Bouncer] Confirm Email Address',
            body=render_template('email/confirm.txt', user=user),
            recipients=[user.email])
        print(url_for('login.confirm_email', token=user.token, email=user.email, _external=True))
        mail.send(msg)