import queue, time, uuid, json, logging, datetime, os
from flask import Flask, render_template, request, make_response, session, send_file, g 
from flask_socketio import SocketIO, join_room, leave_room 
from flask_session import Session
from markupsafe import escape
from .db import get_db


#flask_config
DATABASE = 'Website/mate.db'

def create_logs(app):
    now = datetime.datetime.now().strftime('%d-%m-%Y-%H-%M-%S')
    logging.basicConfig(filename=f"logs/matekasse-{now}.log",filemode='w', format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ,encoding='utf-8', level=logging.INFO)
    app.logger = logging.getLogger('db')

    app.logger.info("Website is starting")

def create_app(test_config=None):
    app = Flask(__name__)
    key = str(uuid.uuid4().hex)
    if test_config is None:
        app.config['SESSION_TYPE'] = 'filesystem'
        app.config['SECRET_KEY'] = key
        app.config['DATABASE'] = DATABASE
    else:
        app.config.from_mapping(test_config)

    try:
        os.makedirs(app.instance_path)
    except OSError:
        pass
    with app.app_context():
        create_logs(app)

    Session(app)
    socketio = SocketIO(app) 
    
    #@app.teardown_appcontext
    #def close_connection(exception): 
    #    db = getattr(g, '_database', None)
    #    if db is not None:
    #        db.close()
    #    app.logger.info("Website exited")
    
    #var
    user_queue = queue.Queue()

    #website
    @app.route('/favicon.ico')
    def favicon():
        return send_file("../static/Logo_CCC.svg.png") 

    #@app.route('/socket.io.js')
    #def socketiojs():
    #    return url_for('static', filename='socket.io.js') 

    @app.route("/")
    def index():
        return """
        <a href="/list">user and tag list</a> 
        <p>The creator of this website accepts no liability for any linguistic or technical errors!</p>
        <br style="line-height: 500%;"></br>
        <a href="/documentation">Doumentation</a><script src="/socket.io.js" integrity="sha512-q/dWJ3kcmjBLU4Qc47E4A9kTB4m3wuTY7vkFJDTZKjTs8jhyGQnaUrxa0Ytd0ssMZhbNua9hE+E7Qv1j+DyZwA==" crossorigin="anonymous">
        </script>
        <script type="text/javascript" charset="utf-8">
            window.location="/list"
        </script>
        """

    @app.route("/list")
    def list():
        db = get_db()
        c = db.cursor()
        c.execute("SELECT * FROM users")   
        users = c.fetchall()
        text = ""
        for i in users:
            text = text + f'<p><a href="list/user?id={i[0]}">{escape(i[1])}</a>: {i[2]} <form action="/change" method="get"><input name="id" type="hidden" value="{i[0]}"> Change balance: <input name="change"><input type="submit"></form></p> <br style="line-height: 50%;"></br>'
        return '''<!DOCTYPE html>
            <html lang="en">
            <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js" integrity="sha512-q/dWJ3kcmjBLU4Qc47E4A9kTB4m3wuTY7vkFJDTZKjTs8jhyGQnaUrxa0Ytd0ssMZhbNua9hE+E7Qv1j+DyZwA==" crossorigin="anonymous"></script>
            <script type="text/javascript" charset="utf-8">
                var socket = io();
                socket.on("update", function(){
                    window.location="http://matekasse.server.c3h/list"
                });
            </script>
            <p><a href="/list">user and tag list</a> | <a href="/documentation">Documentation</a></p>
            <p>1 Credit =  1,50 Euro</p>
            <form action="/list/user" method="get"> Search for User: <input name="user"><input type="submit"></form>
            <form action="/adduser" method="get"><button type="submit">Add User</button></form> 
            <br></br>
            ''' + text + '<a href="/documentation">Doumentation</a></html>'  

    @app.route("/list/user", methods=['GET'])
    def user_info():
        db = get_db()
        c = db.cursor()
        id = request.args.get("id")
        c.execute(f"SELECT * FROM users WHERE id=?", [id])
        user_list = c.fetchall()
        if user_list != []:
            user = user_list[0]
            c.execute(f"SELECT * FROM tags WHERE userid={user[0]}")
            tags = c.fetchall()
            text = ""
            for tag in tags:
                text = text + f"<p>{tag[0]}</p>"
            return f"""<!DOCTYPE html>
            <html lang="en">
                <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js" integrity="sha512-q/dWJ3kcmjBLU4Qc47E4A9kTB4m3wuTY7vkFJDTZKjTs8jhyGQnaUrxa0Ytd0ssMZhbNua9hE+E7Qv1j+DyZwA==" crossorigin="anonymous"></script>
                <script type="text/javascript" charset="utf-8">
                var socket = io();
            """ + 'socket.on("update", function(){ window.location="http://matekasse.server.c3h/list/user?id=' + id + '"});' + f"""
                </script>
                <p><a href="/list">user and tag list</a> | <a href="/documentation">Documentation</a></p>
                <p> {escape(user[1])} : {user[2]} <p>
                <form action="/addtag" method="get"><input name="id" type="hidden" value="{user[0]}"><button type="submit">Add Tag</button></form>
                <form action="/removetag" method="get"><input name="id" type="hidden" value="{user[0]}"><button type="submit">Remove Tag</button></form>
                </p><form action="/change" method="get"><input name="id" type="hidden" value="{user[0]}"> Change balance: <input name="change"><input type="submit"></form>
                </p>
                <br></br>
                <p>Tags:</p>
                {text}
                <br></br>
                <form action="/removeuser/confirmation" method="get"><input name="id" type="hidden" value="{user[0]}"><button type="submit">Remove User</button></form>
            </html>
            """
        else:
            return "Error: 043"

    @app.route("/adduser")
    def new_user():
        return render_template("adduser.html")

    @app.route("/removeuser", methods=['GET'])
    def remove_user():
        db = get_db()
        c = db.cursor()
        user_id = request.args.get("id")
        c.execute(f"SELECT * FROM users WHERE id=?", [user_id])
        users = c.fetchall()
        if users != []:
            user_name = users[0][1]
            c.execute(f"DELETE FROM tags WHERE userid=?", [user_id])  
            app.logger.info(f"Deleted all tags from user ?", [user_id])
            c.execute(f"DELETE FROM users WHERE id=?", [user_id])
            app.logger.info(f"Deleted user ?", [user_id])
            db.commit()
            socketio.emit("update", "update")
            return f'<p><p><a href="/list">user and tag list</a> | <a href="/documentation">Documentation</a></p> <p>Deleted user {escape(user_name)}</p><a href="/list">return to the tags and user list</a></p>'
        else:
            return "Error: 043"

    @app.route("/adduser/user", methods=['GET'])
    def adduser():
        db = get_db()
        c = db.cursor()
        username = request.args.get("username")
        if username == None:
            return "418"
        c.execute("SELECT * FROM users WHERE username=?", [username]) 
        if c.fetchall() == []:
            c.execute("INSERT or IGNORE INTO users (username, balance) VALUES (?, 0)", [username])
            db.commit()
            socketio.emit("update", "update")
            c.execute(f"SELECT * FROM users WHERE username=?", [username])
            user = c.fetchone()
            app.logger.info(f"Added user id: {user[0]} name: {user[2]}")
            return """<html>
                <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js" integrity="sha512-q/dWJ3kcmjBLU4Qc47E4A9kTB4m3wuTY7vkFJDTZKjTs8jhyGQnaUrxa0Ytd0ssMZhbNua9hE+E7Qv1j+DyZwA==" crossorigin="anonymous"></script>
                <script type="text/javascript" charset="utf-8">
                    window.location="/list";
                </script>
                <p>tag was sucsesfully added</p>
                </html>
                """
        else:
            return '<p><a href="/list">user and tag list</a> | <a href="/documentation">Documentation</a></p><p>Error: 757</p> '


    @app.route("/change", methods=['GET'])
    def change():
        db = get_db()
        c = db.cursor()
        try: 
            user_id = request.args.get("id")
            change = int(request.args.get("change"))
        except:
            return '<p>Error: 095</p><a href="/list">tags and user list</a>'
        c.execute(f"SELECT * FROM users WHERE id=?", [user_id])
        users = c.fetchall()
        if users != []:
            balance_old = users[0][2]
            c.execute(f"UPDATE users SET balance = balance + {change} WHERE id={user_id}")
            db.commit()
            c.execute(f"SELECT * FROM users WHERE id={user_id}")
            user = c.fetchone()
            app.logger.info(f"Changed the balance from user {user[0]} from {balance_old} to {user[2]}")
            socketio.emit("update", "update")
            return """<html>
                    <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js" integrity="sha512-q/dWJ3kcmjBLU4Qc47E4A9kTB4m3wuTY7vkFJDTZKjTs8jhyGQnaUrxa0Ytd0ssMZhbNua9hE+E7Qv1j+DyZwA==" crossorigin="anonymous"></script>
                    <script type="text/javascript" charset="utf-8">
                        window.location="/list";
                    </script>
                    </html>
                    """
        else:
            return '<p><a href="/list">user and tag list</a> | <a href="/documentation">Documentation</a></p> <p>Error: 043</p>'

    @app.route("/addtag", methods=['GET'])
    def get_addtag_request(): 
        try:
            user_id = int(request.args.get("id"))
        except:  #except im Normalen Code!
            return "Error: 095"
        session_id = uuid.uuid4()
        session[id] = session_id
        user_queue.put([user_id, "add", session_id])
        return render_template("addtag.html", user=user_id)
    
    @socketio.on('addtag')
    def request_addtag(data):
        global finished
        global message
        join_room(session[id])
        if len(user_queue.queue) > 0:
            user = user_queue.queue[len(user_queue.queue) - 1]
            if user == [data["data"], "add", session[id]]:
                socketio.emit("wait", "wait", to=session[id])
                i = 0
                while finished != [data["data"], "add", session[id]]:
                    time.sleep(1)
                    i += 1
                    if i > 20:
                        socketio.emit("error", "352", to=session[id])
                        notimportant = user_queue.get()
                        break
                else:
                    finished = None
                    socketio.emit("finished", f"{message}", to=session[id])
                    
            else:
                socketio.emit("busy", "busy", to=session[id])
        else:
            socketio.emit("error", "418", to=session[id])
        leave_room(session[id])
    
    @app.route("/removetag", methods=['GET'])
    def get_removetag_request(): 
        try:
            user_id = int(request.args.get("id"))
        except:  #except im Normalen Code!
            return "Wrong user id!"
        session_id = uuid.uuid4()
        session[id] = session_id
        user_queue.put([user_id, "remove", session_id])
        return render_template("removetag.html", user=user_id)
    
    @socketio.on('removetag')
    def request_removetag(data):
        global finished
        global message
        join_room(session[id])
        if len(user_queue.queue) > 0:
            queue_item = user_queue.queue[len(user_queue.queue) - 1]
            user = queue_item[0]
            if queue_item == [data["data"], "remove", session[id]]:
                socketio.emit("wait", "wait", to=session[id])
                i = 0
                while finished != [data["data"], "remove", session[id]]:
                    time.sleep(1)
                    i += 1
                    if i > 20:
                        socketio.emit("error", "352", to=session[id])
                        notimportant = user_queue.get()
                        break
                else:
                    finished = None
                    socketio.emit("finished", f"{message}", to=session[id])
            else:
                socketio.emit("busy", "busy", to=session[id])
        else:
            socketio.emit("error", "418", to=session[id])
        leave_room(session[id])

    #api
    @app.route("/api/change", methods=['GET']) 
    def api_change():
        db = get_db()
        c = db.cursor()
        userid = request.args.get("id")
        c.execute("SELECT * FROM users WHERE id=?", [userid])
        user_list = c.fetchall()
        if user_list != []:
            user = user_list[0]
            try:
                change = int(request.args.get("change"))
            except:
                change = -1 
            c.execute(f"UPDATE users SET balance = balance + {change} WHERE id={user[0]}")
            db.commit()
            c.execute(f"SELECT * FROM users WHERE id = {userid}")
            user_new = c.fetchone()
            app.logger.info(f"Changed the balance from user {user[0]} from {user[2]} to {user_new[2]}")
            socketio.emit("update", "update")
            return make_response(json.dumps({"mode":"balance", "username":user[1], "balance":user_new[2]}))
        else:
            return make_response(json.dumps({"mode":"error","error":"043"}))


    @app.route("/api/tag_id", methods=['GET'])
    def get_id():
        global finished
        global message
        db = get_db()
        c = db.cursor()
        tag_id = request.args.get("id")
        c.execute(f"SELECT * FROM tags WHERE tagid=?", [tag_id])

        tag_list = c.fetchall()
        if user_queue.qsize() > 0:
            queue_item = user_queue.get()
            user = queue_item[0]

            state = queue_item[1]
            c.execute(f"SELECT * FROM users WHERE id=?", [user])
            username = c.fetchone()[1]
            if state == "add":
                c.execute(f"SELECT * FROM tags WHERE tagid={tag_id}")
                if c.fetchall() != []:
                    message = "Error: 170"
                    finished = queue_item
                    return make_response(json.dumps({"mode":"error","error":"170"}))
                else:
                    c.execute(f"INSERT OR IGNORE INTO tags (tagid, userid) VALUES ({tag_id}, ?)", [user])
                    message = f"Added {tag_id} to {username}"
                    app.logger.info(message)
                    finished = queue_item
                    db.commit()
                    return make_response(json.dumps({"mode":"message","username":"%s".format(username),"message":"A tag was added"}))
            elif state == "remove":
                c.execute(f"SELECT * FROM tags WHERE (tagid = {tag_id} AND userid = ?)", [user])
                tags = c.fetchall()
                if tags != []:
                    c.execute(f"DELETE FROM tags WHERE (tagid = {tag_id} AND userid = ?)", [user])
                    message = f"Removed {tag_id} from {username}"
                    app.logger.info(message)
                    finished = queue_item
                    db.commit()
                    return make_response(json.dumps({"mode":"message","username":"%s".format(username),"message":"A tag was removed"}))
                else:
                    message = "054"
                    finished = queue_item
                    return make_response(json.dumps({"mode":"error","error":"054"}))
            finished = queue_item
            socketio.emit("update", "update")
            return make_response(json.dumps({"mode":"error","error":"418"}))

        elif tag_list != []:
            tag = tag_list[0]
            c.execute(f"SELECT * FROM users WHERE id={tag[1]}")
            user_list = c.fetchall()
            if user_list != []:
                balance_old = user_list[0][2]
                if user_queue.qsize() == 0:
                    c.execute(f"UPDATE users SET balance = balance - 1 WHERE id={tag[1]}")
                    db.commit() 
                    c.execute(f"SELECT * FROM users WHERE id={tag[1]}")
                    user = c.fetchone()
                    app.logger.info(f"Changed the balance from user {user[0]} from {balance_old} to {user[2]}")
                    socketio.emit("update", "update")
                    return make_response(json.dumps({"mode":"balance", "username":user[1], "balance":user[2]}))
            else:
                return make_response(json.dumps({"mode":"error", "error":"043"}))
        socketio.emit("update", "update")
        return make_response(json.dumps({"mode":"error","error":"054"}))

    #Documentation
    @app.route("/documentation")
    def documentation():
        return render_template("documentation.html")

    return {"app":app,"socketio":socketio}