import os

from flask import Flask, render_template, request, flash, redirect, session, g
from flask_debugtoolbar import DebugToolbarExtension
from sqlalchemy.exc import IntegrityError

from forms import UserAddForm, LoginForm, MessageForm, EditUserForm
from models import db, User, Message, Likes
from user_functions import is_followed_by, is_following, signup, authenticate
from secret_key import key

CURR_USER_KEY = "curr_user"

app = Flask(__name__)

app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get('DATABASE_URL', 'postgres:///warbler')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_ECHO'] = False
app.config['DEBUG_TB_INTERCEPT_REDIRECTS'] = False
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', key)
toolbar = DebugToolbarExtension(app)

db.app = app
db.init_app(app)

# Deployed at: https://dylan-twitter-clone.herokuapp.com/


##############################################################################
# User signup/login/logout


@app.before_request
def add_user_to_g():
    """If we're logged in, add curr user to Flask global."""

    if CURR_USER_KEY in session:
        g.user = User.query.get(session[CURR_USER_KEY])

    else:
        g.user = None


def do_login(user):
    """Log in user."""

    session[CURR_USER_KEY] = user.id


def do_logout():
    """Log out the user."""

    if CURR_USER_KEY in session:
        del session[CURR_USER_KEY]


def update_user_data(form, user):
    user.location = form.location.data if form.location.data else user.location
    user.bio = form.bio.data if form.bio.data else user.bio
    user.header_image_url = form.header_image_url.data if form.header_image_url.data else user.header_image_url
    user.username = form.username.data if form.username.data else user.username
    user.email = form.email.data if form.email.data else user.email
    user.image_url = form.image_url.data if form.image_url.data else user.image_url


def pre_populate_user_edit_form_fields(form, user):
    form.username.data = user.username
    form.email.data = user.email
    form.image_url.data = user.image_url
    form.location.data = user.location if user.location else None
    form.bio.data = user.bio if user.bio else None


@app.route('/signup', methods=["GET", "POST"])
def signup_():
    """ Handle user signup """

    form = UserAddForm()

    if form.validate_on_submit():
        try:
            user = signup(
                username=form.username.data,
                password=form.password.data,
                email=form.email.data,
                image_url=form.image_url.data or User.image_url.default.arg,
            )

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

        except IntegrityError:
            flash("Username already taken", 'danger')
            return render_template('users/signup.html', form=form)

        do_login(user)

        return redirect("/")

    else:
        return render_template('users/signup.html', form=form)


@app.route('/login', methods=["GET", "POST"])
def login():
    """ Handle the user login """

    form = LoginForm()

    if form.validate_on_submit():
        user = authenticate(form.username.data, form.password.data)

        if user:
            do_login(user)
            flash(f"Hello, {user.username}!", "success")
            return redirect("/")

        flash("Invalid credentials.", 'danger')

    return render_template('users/login.html', form=form)


@app.route('/logout')
def logout():
    """ Handle log out of the user """

    user = User.query.get(session[CURR_USER_KEY])
    do_logout()
    flash(f"See You Next Time, {user.username}!", "success")
    return redirect('/')


##############################################################################
# General user routes:

@app.route('/users')
def list_users():
    """Lists all of the users """

    search = request.args.get('q')

    if not search:
        users = User.query.all()
    else:
        users = User.query.filter(User.username.like(f"%{search}%")).all()

    return render_template('users/index.html', users=users, is_following=is_following)


@app.route('/users/<int:user_id>')
def users_show(user_id):
    """ Display user's profile """

    user = User.query.get_or_404(user_id)

    messages = (Message
                .query
                .filter(Message.user_id == user_id)
                .order_by(Message.timestamp.desc())
                .limit(100)
                .all())
    return render_template('users/show.html', user=user, messages=messages, is_following=is_following)


@app.route('/users/<int:user_id>/following')
def show_following(user_id):
    """ Shows a list of people that this user is following """

    if not g.user:
        flash("Access unauthorized.", "danger")
        return redirect("/")

    user = User.query.get_or_404(user_id)
    return render_template('users/following.html', user=user, is_following=is_following)


@app.route('/users/<int:user_id>/followers')
def users_followers(user_id):
    """ Shows a list of followers for this user """

    if not g.user:
        flash("Access unauthorized.", "danger")
        return redirect("/")

    user = User.query.get_or_404(user_id)
    return render_template('users/followers.html', user=user, is_following=is_following)


@app.route('/users/follow/<int:follow_id>', methods=['POST'])
def add_follow(follow_id):
    """ Add a follower for this user """

    if not g.user:
        flash("Access unauthorized.", "danger")
        return redirect("/")

    followed_user = User.query.get_or_404(follow_id)
    g.user.following.append(followed_user)
    db.session.commit()

    return redirect(f"/users/{g.user.id}/following")


@app.route('/users/stop-following/<int:follow_id>', methods=['POST'])
def stop_following(follow_id):
    """ Have this user unfollow user with follow_id """

    if not g.user:
        flash("Access unauthorized.", "danger")
        return redirect("/")

    followed_user = User.query.get(follow_id)
    g.user.following.remove(followed_user)
    db.session.commit()

    return redirect(f"/users/{g.user.id}/following")


@app.route('/users/profile', methods=["GET", "POST"])
def profile():
    """ Updates the profile for this user """
    
    form = EditUserForm()
    user = User.query.get_or_404(session[CURR_USER_KEY])
    
    if form.validate_on_submit():
        update_user_data(form, user) 
        
        if authenticate(user.username, form.password.data):        
            db.session.add(user)
            db.session.commit()
            flash('Your account had been successfully updated.', 'success')
            return redirect(f"/users/{user.id}")
        else:
            flash('Incorrect password.', 'danger')
            return redirect('/')
    

    pre_populate_user_edit_form_fields(form, user)

    return render_template('users/edit.html', form=form, user=user)
    

@app.route('/users/delete', methods=["POST"])
def delete_user():
    """ Deletes this user """

    if not g.user:
        flash("Access unauthorized.", "danger")
        return redirect("/")

    do_logout()

    db.session.delete(g.user)
    db.session.commit()

    return redirect("/signup")


##############################################################################
# Messages routes:

@app.route('/messages/new', methods=["GET", "POST"])
def messages_add():
    """ Add a message """

    if not g.user:
        flash("Access unauthorized.", "danger")
        return redirect("/")

    form = MessageForm()

    if form.validate_on_submit():
        msg = Message(text=form.text.data)
        if len(form.text.data) > 140:
            flash("Your warble cannot exceed 140 characters", 'danger')
            return render_template('messages/new.html', form=form)

        g.user.messages.append(msg)
        db.session.commit()

        return redirect(f"/users/{g.user.id}")

    return render_template('messages/new.html', form=form)


@app.route('/messages/<int:message_id>', methods=["GET"])
def messages_show(message_id):
    """ Shows a message """

    msg = Message.query.get_or_404(message_id)
    return render_template('messages/show.html', message=msg, is_following=is_following)


@app.route('/messages/<int:message_id>/delete', methods=['GET', 'POST'])
def messages_destroy(message_id):                                       
    """ Delete a message """
    message = Message.query.get_or_404(message_id)

    if g.user:
        if message.user_id == session[CURR_USER_KEY]:
            msg = Message.query.get(message_id)
            db.session.delete(msg)
            db.session.commit()
            return redirect(f"/users/{g.user.id}")
    
    flash("Access unauthorized.", "danger")
    return redirect("/")

##############################################################################
# Homepage and error pages


@app.route('/')
def homepage():
    """ Show homepage. Anonamous users wont show any messages """

    if g.user:            
        messages = (Message
            .query
            .filter(Message.user_id.in_([user.id for user in g.user.following] + [g.user.id]))
            .order_by(Message.timestamp.desc())
            .limit(100)
            .all())

        return render_template('home.html', messages=messages)
    
    return render_template('home-anon.html')


@app.route('/users/add_like/<int:message_id>', methods=['GET', 'POST'])
def add_user_like(message_id):
    if not g.user:
        flash("Access unauthorized.", "danger")
    else:
        message = Message.query.get_or_404(message_id)
        user = User.query.get(session[CURR_USER_KEY])
        likes = user.likes

        if message in likes:
            Likes.query.filter_by(user_id=user.id, message_id=message_id).delete()
        else:
            new_like = Likes(user_id=user.id, message_id=message_id)
            db.session.add(new_like)
    
        db.session.commit()
    return redirect('/')


@app.route('/users/remove_like/<int:message_id>', methods=['POST'])
def remove_like_from_like_page(message_id):
    user = User.query.get(session[CURR_USER_KEY])
    Likes.query.filter_by(user_id=user.id, message_id=message_id).delete()    
    db.session.commit()
    
    return redirect(f"/users/{user.id}/likes")


@app.route('/users/<user_id>/likes')
def show_user_likes(user_id):
    user = User.query.get_or_404(user_id)
    likes = user.likes
    
    return render_template('/likes/show_all_user_likes.html', likes=likes)


@app.after_request
def add_header(req):
    """Add non-caching headers on every request."""

    req.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
    req.headers["Pragma"] = "no-cache"
    req.headers["Expires"] = "0"
    req.headers['Cache-Control'] = 'public, max-age=0'
    
    return req
