from flask import Flask, request, render_template, session, redirect, url_for, flash
from TwitterAPI import TwitterAPI
from api_keys import *
import database_connector
import hashlib
import secrets
import datetime

"""
test var, het het nogmaal gesproken op False
"""
debug = True


"""
database objecten, om minder te typen
"""
conn = database_connector.conn
cur = database_connector.cur


"""
stel flask in
"""
app = Flask(__name__)

"""
als debug op true is een vaste secret key gebruiken, dit is zodat we niet elke keer in hoefen te loggen na een
code wijziging. Maar in productie elke start een nieuwe token genereren
"""
if debug == True:
    app.secret_key = 'jdndhsyh83r90hfeqwf'  # dit is om live te testen
else:
    app.secret_key = secrets.token_hex(32)

"""
deze functie checkt de username en password die worden doorgegeven.
Als beide juist zijn geeft de functie True terug, anders False met een flash
"""
def func_check_login(uname, pword):
    hashedpass = hashlib.sha256(pword.encode('UTF-8') + 'saltandpepper'.encode('UTF-8')).hexdigest()
    cur.execute("SELECT * FROM moderators WHERE naam = (%s);", [uname])
    sqlreturn = cur.fetchone()
    if sqlreturn == None:
        flash('Gebruiker niet gevonden')
        return False
    elif hashedpass != sqlreturn[2]:
        flash('Uw wachtwoord is fout')
        return False
    elif sqlreturn[3] != True:
        flash('Dit account mag niet meer inloggen')
        return False
    else:
        return True

"""
Deze functie weergeeft alle moderator info in een geneste tuple.
Accepteert maximale aantal rijen en pagina in int
"""
def func_listmoderators(limit, page):
    cur.execute('SELECT * FROM moderators LIMIT (%s) OFFSET (%s)', (limit, page))
    allmoderators = cur.fetchall()
    cur.execute('SELECT COUNT(*) FROM moderators')
    countmoderators = cur.fetchone()
    return (allmoderators, countmoderators)

"""
Deze functie voegt een moderator toe, checkt eerst of wachtwoord aan eisen voldoet en of beide wachtwoorden hetzelfde
zijn. Daarna de gebruikersnaam checken of die niet bestaat, anders geeft de functie False terug met een flash.
Als de gebruikersnaam niet bestaat hasht de functie de wachtwoord en voegt het toe in de database met als return True.
"""
def func_addmoderator(uname, pword, pword2):
    username = uname.lower()
    password = pword
    password2 = pword2

    if len(password) < 4:
        flash('Wachtwoord lengte is te weinig, gebruik minimaal 4 tekens!')
        return False
    elif password != password2:
        flash('Wachtwoorden zijn niet hetzelfde!')
        return False

    if len(username) > 64 or len(username) < 3:
        flash('Gebruikersnaam lengte is ongeldig, gebruik minimaal 3 en maximaal 64 tekens!')
        return False
    elif ' ' in username:
        flash('Gebruikersnaam bevat spatie, dat mag niet')
        return False

    cur.execute("SELECT naam FROM moderators WHERE naam = (%s);", (username,))
    sqlreturn = cur.fetchone()

    if sqlreturn == None:
        hashedpass = hashlib.sha256(password.encode('UTF-8') + 'saltandpepper'.encode('UTF-8')).hexdigest()
        cur.execute("INSERT INTO moderators(naam, hash, maylogin) VALUES (%s, %s, 'true')", (username, hashedpass))
        conn.commit()
        flash('Account voor ' + username + ' aangemaakt!')
        return True
    else:
        flash('Probleem met de database, bestaat de gebruiker al?')
        return False

"""
Deze functie probeert een gegeven moderator te verwijderen. Als de moderator berichten heeft gemodereert kan de account
niet verwijdert worden. Dit komt door database restricties. Daarvoor zet de functie in de regel maylogin op false.
"""
def func_removemoderator(moderatorid):
    try:
        cur.execute('DELETE FROM moderators WHERE moderatorid = (%s)', [moderatorid])
        conn.commit()
        flash('Moderator account verwijdert')
        return True
    except:
        conn.rollback()
        try:
            cur.execute('UPDATE moderators SET maylogin = false WHERE moderatorid = (%s)', [moderatorid])
            conn.commit()
            flash('Account kan niet verwijdert worden, account nu mag niet meer inloggen')
            return True
        except:
            conn.rollback()
            flash('Probleem opgetreden bij het aanpassen van moderator account')
            return False

"""
deze functie haalt net zoals bij moderators alle berichten op gesorteert op de laatste ontvangen. Overigens worden
de kolomen ook met andere tabellen gejoined voor mooiere waardes. 
Deze functie returnt een tuple met op 0 geneste dicts met berichten en op 1 de totaal aantal berichten in het systeem.
"""
def func_listberichten(limit, page):
    cur.execute('SELECT berichten.berichtid, berichten.bericht, berichten.datumontvangen, berichten.naamposter, '
                'berichten.datumgekeurd, locaties.naam, moderators.naam, statuses.status, berichten.twitterid '
                'FROM berichten '
                'LEFT OUTER JOIN statuses ON berichten.statusid = statuses.statusid '
                'LEFT OUTER JOIN locaties ON berichten.locatieid = locaties.locatieid '
                'LEFT OUTER JOIN moderators ON berichten.moderatorid = moderators.moderatorid '
                'ORDER BY datumontvangen DESC LIMIT (%s) OFFSET (%s)', (limit, page))
    allberichten = cur.fetchall()
    cur.execute('SELECT COUNT(*) FROM berichten')
    countberichten = cur.fetchone()
    return (allberichten, countberichten)

"""
Deze functie haalt netzoals listberichten zelfda waarden op. maar dan met 1 bericht, en dus accepteert berichtid
"""
def func_querybericht(berichtid):
    cur.execute('SELECT berichten.berichtid, berichten.bericht, berichten.datumontvangen, berichten.naamposter, '
                'berichten.datumgekeurd, locaties.naam, moderators.naam, statuses.status, berichten.twitterid '
                'FROM berichten '
                'LEFT OUTER JOIN statuses ON berichten.statusid = statuses.statusid '
                'LEFT OUTER JOIN locaties ON berichten.locatieid = locaties.locatieid '
                'LEFT OUTER JOIN moderators ON berichten.moderatorid = moderators.moderatorid '
                'WHERE berichtid = (%s)', [berichtid])
    return cur.fetchone()

"""
Dit is de belangerijkste functie. Hier worden berichten opgehaalt om te modereren

1. Haal 1 oudste bericht uit de database met een paar joins uit andere tabellen
2. variabele bericht None is, flash en return False
3. Accepteer moderatornaam, en haal de moderatorid uit de database
4. Update de bericht met de juiste status code en moderatorid

deze functie returnt de bericht als tuple
"""
def func_autoqueuebericht(moderatornaam):
    cur.execute('SELECT berichten.berichtid, berichten.bericht, berichten.datumontvangen, berichten.naamposter, '
                'locaties.naam, statuses.status '
                'FROM berichten '
                'LEFT OUTER JOIN statuses ON berichten.statusid = statuses.statusid '
                'LEFT OUTER JOIN locaties ON berichten.locatieid = locaties.locatieid '
                'LEFT OUTER JOIN moderators ON berichten.moderatorid = moderators.moderatorid '
                'WHERE berichten.statusid = 1 ORDER BY datumontvangen LIMIT 1')
    bericht = cur.fetchone()

    if bericht == None:
        flash('Er zijn geen berichten meer om te keuren, probeer het later opnieuw')
        return False

    cur.execute('SELECT moderatorid FROM moderators WHERE naam = (%s)', [moderatornaam])
    moderatorid = cur.fetchone()[0]

    cur.execute('UPDATE berichten SET statusid = 2, moderatorid = (%s) WHERE berichtid = (%s)', (moderatorid, bericht[0]))
    conn.commit()

    return bericht

"""
ontvangt bericht id en zet moderatorid op null en statusid op 1 
"""
def func_unlockbericht(berichtid):
    cur.execute("UPDATE berichten SET statusid = 1, moderatorid = NULL WHERE berichtid = (%s)", [berichtid])
    conn.commit()
    return True

"""
Ontvangt berichtid, genereert YY-MM-DD HH:MM:SS in utc en zet dat in de berichtid, wijzigt ook statuscode naar accepted
"""
def func_messageaccept(berichtid):
    date = datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")
    QUERY = "UPDATE berichten SET statusid = 4, datumgekeurd = (%s) WHERE berichtid = (%s)"
    DATA = (date, berichtid)
    cur.execute(QUERY, DATA)
    conn.commit()
    return True

"""
Ontvangt berichtid, genereert YY-MM-DD HH:MM:SS in utc en zet dat in de berichtid,wijzigt ook statuscode naar rejected
"""
def func_messagereject(berichtid):
    date = datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")
    QUERY = "UPDATE berichten SET statusid = 3, datumgekeurd = (%s) WHERE berichtid = (%s)"
    DATA = (date, berichtid)
    cur.execute(QUERY, DATA)
    conn.commit()
    return True

"""
ontvangt berichtid en publiceert het naar twitter blah blah verder noteren later
"""
def func_publish_twitter(berichtid):
    return True






"""
en nu naar flask, dit is gewoon de root page
"""
@app.route('/')
def root():
    return render_template('root.html')

"""
de login pagina, accepteert GET en POST, als het GET is met een sessie cookie, invalideert het eerst de sessie cookie
en returnt daarna de login page. als het een POST is, roept het gelijk func_check_login aan met username en wachtwoord
als het successfol is zet het een sessie cookie in. anders krijgt de gebruikers een flash
"""
@app.route('/login', methods=['POST', 'GET'])
def login():
    if request.method == 'POST':
        if func_check_login(request.form['uname'], request.form['pword']):
            session['username'] = request.form['uname']
            return redirect(url_for('home'))
        else:
            return render_template('login.html')
    else:
        if 'username' in session:
            session.pop('username', None)
            flash('You logged out!')
        return render_template('login.html')

@app.route('/home')
def home():
    if 'username' in session:
        return render_template('home.html', username=session['username'])
    else:
        flash('You are not logged in!')
        return redirect(url_for('login'))

@app.route('/moderators')
def moderators():
    if 'username' in session:
        if request.args.get('maxitems'):
            maxitems = int(request.args.get('maxitems'))
        else:
            maxitems = 10
        if request.args.get('pagenum'):
            pagenum = int(request.args.get('pagenum')) - 1
        else:
            pagenum = 0

        page = maxitems * pagenum

        listreturn = func_listmoderators(maxitems, page)
        moderators = listreturn[0]
        moderatorcount = listreturn[1][0]

        return render_template('moderators.html', moderators=moderators, moderatorcount=moderatorcount)
    else:
        flash('You are not logged in!')
        return redirect(url_for('login'))

@app.route('/moderators/addmoderator', methods=['POST'])
def addmoderator():
    if 'username' in session:
        if func_addmoderator(request.form['uname'], request.form['pword'], request.form['pword2']):
            return redirect(url_for('moderators'))
        else:
            return redirect(url_for('moderators'))
    else:
        flash('You are not logged in!')
        return redirect(url_for('login'))

@app.route('/moderators/removemoderator', methods=['POST'])
def listmoderators():
    if 'username' in session:
        if func_removemoderator(request.form['delete']):
            return redirect(url_for('moderators'))
        else:
            return redirect(url_for('moderators'))
    else:
        flash('You are not logged in!')
        return redirect(url_for('login'))

@app.route('/berichten')
def berichten():
    if 'username' in session:
        if request.args.get('maxitems'):
            maxitems = int(request.args.get('maxitems'))
        else:
            maxitems = 10
        if request.args.get('pagenum'):
            pagenum = int(request.args.get('pagenum')) - 1
        else:
            pagenum = 0

        page = maxitems * pagenum

        listreturn = func_listberichten(maxitems, page)
        berichten = listreturn[0]
        berichtcount = listreturn[1][0]

        return render_template('berichten.html', berichten=berichten, berichtcount=berichtcount)
    else:
        flash('You are not logged in!')
        return redirect(url_for('login'))

@app.route('/bericht/<berichtid>')
def bericht(berichtid):
    if 'username' in session:
        bericht = func_querybericht(berichtid)
        return render_template('bericht.html', bericht=bericht)
    else:
        flash('You are not logged in!')
    return redirect(url_for('login'))

@app.route('/autoqueue', methods=['GET', 'POST'])
def autoqueue():
    if 'username' in session:
        if request.method == 'POST':
            if func_unlockbericht(request.form['stop']):
                return redirect(url_for('home'))
            else:
                flash('probleem met het unlocken van het bericht')
                return redirect(url_for('home'))
        else:
            bericht = func_autoqueuebericht(session['username'])

            if bericht == False:
                return redirect(url_for('home'))

            return render_template('autoqueue.html', bericht=bericht)
    else:
        flash('You are not logged in!')
    return redirect(url_for('login'))

@app.route('/autoqueue/accept', methods=['POST'])
def autoqueue_accept():
    if 'username' in session:
        if func_messageaccept(request.form['good']):
            return redirect(url_for('autoqueue'))
        else:
            return redirect(url_for('autoqueue'))
    else:
        flash('You are not logged in!')
    return redirect(url_for('login'))

@app.route('/autoqueue/reject', methods=['POST'])
def autoqueue_reject():
    if 'username' in session:
        if func_messagereject(request.form['bad']):
            return redirect(url_for('autoqueue'))
        else:
            return redirect(url_for('autoqueue'))
    else:
        flash('You are not logged in!')
    return redirect(url_for('login'))

if __name__ == "__main__":
    app.run('0.0.0.0', debug=debug)