from flask import request, render_template, make_response, redirect, Flask, Response
from flask_oidc import OpenIDConnect
from pathlib import Path
from os import environ, path

from helper import *
from db import *
from base64 import b64encode
from api import services, restricted_services, dump_entry_html, entries_heads, add_entry_to_list, delete_entry


app = Flask(__name__)
app.config["DEBUG"] = False
app.config["OIDC_CLIENT_SECRETS"] = str(Path(__file__).parent.resolve()) + '/static/default.jpg'
app.secret_key = "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"
oidc = OpenIDConnect(app)


def post_req(req, params=None):
    new_params = {}
    try:
        query_parameters = req.args
        if params:
            if len(query_parameters) == 0:
                query_parameters = req.form
                for x in params:
                    new_params[x] = query_parameters.get(x)
            else:
                for x in params:
                    new_params[x] = query_parameters[x]
            if "sound" in params:
                try:
                    new_params[x] = b64encode(req.files["sound"].read())
                except:
                    print("sound not added due to error")
        return new_params
    except:
        return None


@app.route('/')
@oidc.require_login
def home():
    username, role = userrole()
    return render_template('ui.html', data=[username, role])

@app.route('/img')
def img():
    return render_template('img.html', data=path.join('static', 'jerteh.jpg'))

@app.route('/admin')
@oidc.require_login
def admin_panel():
    username, role = userrole()
    if role not in ["redactor", "editor"]:
        return cfg["guest_msg"]
    userstatuses = get_userstatuses()
    return render_template('admin.html', data=userstatuses.users, skip=userstatuses.skip)

@app.route('/entry/<id>', methods=['GET'])
@oidc.require_login
def entry_view(id):
    username, role = userrole()
    if role not in ["viewer", "editor", "redactor"]:
        return cfg["guest_msg"]
    entry = get_entry_for_id(id)
    template = render_template("entry.html", entry=entry, role=role, poses=POS, gram=gram, dom=dom, ety=ety, qual=qual, expi=expi, nots=nots)
    response = make_response(template)
    response.headers['Content-Type'] = 'text/html'
    return response

@app.route('/delete/<id>', methods=['GET'])
@oidc.require_login
def delete(id):
    if not user_can_del():
        return cfg["guest_msg"]
    try:
        delete_entry(id)
        return "success"
    except:
        return "failed :("

@app.route('/entryHtml/<id>', methods=['GET'])
@oidc.require_login
def entry_html_view(id):
    username, role = userrole()
    if role not in ["viewer", "editor", "redactor"]:
        return cfg["guest_msg"]
    html = dump_entry_html(id)
    return Response(html, mimetype="text/html",
                            headers={'Content-Disposition': 'inline;filename=rssj_dump.html'})

@app.route('/entry/<eid>', methods=['POST'])
@oidc.require_login
def save_entry_changes(eid):
    if myentry(eid):
        params = post_req(request, ["head", "pos", "homograph", "head_ijk"])
        save_entry_for_id(eid, params)
        return "Измене су сачуване"

@app.route('/sound/<eid>', methods=['POST'])
@oidc.require_login
def add_sound(eid):
    username, role = userrole()
    if myentry(eid):
        params = post_req(request, ["headword", "sound"])
        params["username"] = username
        save_sound(eid, params)
        return redirect("/entry/" + str(eid))

@app.route('/remsound/<pid>/<eid>', methods=['GET'])
@oidc.require_login
def remove_sound(pid, eid):
    if myentry(eid):
        delete_sound(pid)
        return redirect("/entry/" + str(eid))

@app.route('/reserve/<id>', methods=['GET'])
@oidc.require_login
def reserve_entry(id):
    username, role = userrole()
    if role in ["editor", "redactor"]:
        try_reserve(username, id)
    return redirect("/entry/" + str(id))

@app.route('/drop/<id>', methods=['GET'])
@oidc.require_login
def drop_entry_reservation(id):
    username = get_username()
    drop_reserve(username, id)
    return redirect("/entry/" + str(id))

@app.route('/finish/<id>', methods=['GET'])
@oidc.require_login
def finish_editing_entry(id):
    username, role = userrole()
    if role in ["editor", "redactor"]:
        finish_edit(username, id)
    return redirect("/entry/" + str(id))

@app.route('/finish2/<id>', methods=['GET'])
@oidc.require_login
def finish_entry_redaction(id):
    username, role = userrole()
    if role in ["redactor"]:
        finish_redact(username, id)
        add_entry_to_list(id)
    return redirect("/entry/" + str(id))

@app.route('/add_entry', methods=['POST'])
@oidc.require_login
def addentry():
    username, role = userrole()
    if role not in ["editor", "redactor"]:
        return cfg["guest_msg"]

    params = post_req(request, ["str"])
    new_id = add_entry(user=username, stri=params["str"])
    return redirect("/entry/" + str(new_id))

@app.route('/sense/<id>', methods=['GET'])
@oidc.require_login
def view_sense(id):
    username, role = userrole()
    if role not in ["viewer", "editor", "redactor"]:
        return cfg["guest_msg"]
    sense = get_sense_for_id(id)
    template = render_template("sense.html", zero=False, sense=sense, role=role, gram=gram, dom=dom, ety=ety, qual=qual, exi=exi,  defi=defi, conn=conni)
    response = make_response(template)
    response.headers['Content-Type'] = 'text/html'
    return response

@app.route('/sensezero/<id>', methods=['GET'])
@oidc.require_login
def view_sense0(id):
    username, role = userrole()
    if role not in ["viewer", "editor", "redactor"]:
        return cfg["guest_msg"]
    sense = get_sense_for_id(id)
    template = render_template("sense.html", zero=True, sense=sense, role=role, gram=gram, dom=dom, ety=ety, qual=qual, exi=exi,  defi=defi, conn=conni)
    response = make_response(template)
    response.headers['Content-Type'] = 'text/html'
    return response

@app.route('/senseoznaka/<id>', methods=['POST'])
@oidc.require_login
def save_senseo_changes(id):
    username, role = userrole()
    sense = get_sense_for_id(id)
    params = post_req(request, ["label"])
    if sense.mysense:
        save_senseo_for_id(id, params)
    return "Измене су сачуване"

@app.route('/sense/<id>', methods=['POST'])
@oidc.require_login
def save_sense_changes(id):
    username, role = userrole()
    sense = get_sense_for_id(id)
    params = post_req(request, ["grammar_ijk", "grammar"])
    if sense.mysense:
        save_sense_for_id(id, params)
    return "Измене су сачуване"

@app.route('/newsense/<id>', methods=['GET'])
@oidc.require_login
def add_new_sense_to_entry(id):
    username, role = userrole()
    if role not in ["viewer", "editor", "redactor"]:
        return cfg["guest_msg"]
    sense = create_empty_sense(id)
    return redirect("/entry/" + str(id))

@app.route('/addnote/<eid>', methods=['POST'])
@oidc.require_login
def addnote(eid):
    username, role = userrole()
    if role not in ["viewer", "editor", "redactor"]:
        return cfg["guest_msg"]
    if myentry(eid):
        params = post_req(request, ["type", "note"])
        add_note(username, eid, params)
    return redirect("/entry/" + str(eid))

@app.route('/editnote/<eid>/<id>', methods=['POST'])
@oidc.require_login
def editnote(eid, id):
    username, role = userrole()
    if role not in ["viewer", "editor", "redactor"]:
        return cfg["guest_msg"]
    if myentry(eid):
        params = post_req(request, ["type", "note"])
        edit_note(eid, id, params)
    return redirect("/entry/" + str(eid))

@app.route('/delnote/<eid>/<id>', methods=['GET'])
@oidc.require_login
def delnote(eid, id):
    username, role = userrole()
    if role not in ["viewer", "editor", "redactor"]:
        return cfg["guest_msg"]
    if myentry(eid):
        delete_note(eid, id)
    return redirect("/entry/" + str(eid))

@app.route('/addgram/<id>', methods=['POST'])
@oidc.require_login
def add_grammar(id):
    username, role = userrole()
    if role not in ["viewer", "editor", "redactor"]:
        return cfg["guest_msg"]
    sense = get_sense_for_id(id)
    if sense.mysense:
        params = post_req(request, ["tag", "text", "id"])
        add_gram_sense(id, params)
    return redirect("/sense/" + str(id))

@app.route('/remgram/<sid>/<id>', methods=['GET'])
@oidc.require_login
def rem_gram(sid, id):
    username, role = userrole()
    if role not in ["viewer", "editor", "redactor"]:
        return cfg["guest_msg"]
    sense = get_sense_for_id(sid)
    if sense.mysense:
        rem_gram_sense(id)
    return redirect("/sense/" + str(sid))

@app.route('/add_def/<sid>', methods=['POST'])
@oidc.require_login
def adddef(sid):
    username, role = userrole()
    if role not in ["viewer", "editor", "redactor"]:
        return cfg["guest_msg"]
    sense = get_sense_for_id(sid)
    if sense.mysense:
        params = post_req(request, ["type", "text", "id"])
        add_def_sense(sid, params)
    return redirect("/sense/" + str(sid))

@app.route('/izraz/<sid>', methods=['POST'])
@oidc.require_login
def izraz(sid):
    username, role = userrole()
    if role not in ["viewer", "editor", "redactor"]:
        return cfg["guest_msg"]
    sense = get_sense_for_id(sid)
    if sense.mysense:
        params = post_req(request, ["val", "type"])
        add_expression(username, sid, params["val"], params["type"])
    return redirect("/entry/" + str(sense.eid))

@app.route('/addconn/<sid>', methods=['POST'])
@oidc.require_login
def addconn(sid):
    username, role = userrole()
    if role not in ["viewer", "editor", "redactor"]:
        return cfg["guest_msg"]
    sense = get_sense_for_id(sid)
    if sense.mysense:
        params = post_req(request, ["sid2", "type"])
        add_connection(sid, params["sid2"], params["type"])
    return redirect("/sense/" + str(sense.id))

@app.route('/remexp/<sid>/<rid>/<eid>/<xsid>', methods=['GET'])
@oidc.require_login
def remexp(sid, rid, eid, xsid):
    username, role = userrole()
    if role not in ["viewer", "editor", "redactor"]:
        return cfg["guest_msg"]
    sense = get_sense_for_id(sid)
    if myentry(eid):
        remove_expression(sid, rid, eid, xsid)
    return redirect("/entry/" + str(sense.eid))

@app.route('/remconn/<sid>/<rid>/<xsid>', methods=['GET'])
@oidc.require_login
def remconn(sid, rid, xsid):
    username, role = userrole()
    if role not in ["viewer", "editor", "redactor"]:
        return cfg["guest_msg"]
    sense = get_sense_for_id(sid)
    if myentry(sense.eid):
        remove_connection(sid, rid, xsid)
    return redirect("/sense/" + str(sense.id))

@app.route('/changeexp/<sid>/<rid>/<xsid>', methods=['POST'])
@oidc.require_login
def changeexp(sid, rid, xsid):
    username, role = userrole()
    if role not in ["viewer", "editor", "redactor"]:
        return cfg["guest_msg"]
    sense = get_sense_for_id(sid)
    if myentry(sense.eid):
        params = post_req(request, ["type"])
        change_expression_type(sid, rid, xsid, params)
    return redirect("/entry/" + str(sense.eid))

@app.route('/changeconn/<sid>/<rid>/<xsid>', methods=['POST'])
@oidc.require_login
def changeconn(sid, rid, xsid):
    username, role = userrole()
    if role not in ["viewer", "editor", "redactor"]:
        return cfg["guest_msg"]
    sense = get_sense_for_id(sid)
    if myentry(sense.eid):
        params = post_req(request, ["type"])
        change_expression_type(sid, rid, xsid, params)
    return redirect("/sense/" + str(sense.id))

@app.route('/rem_def/<sid>/<id>', methods=['GET'])
@oidc.require_login
def remdef(sid, id):
    username, role = userrole()
    if role not in ["viewer", "editor", "redactor"]:
        return cfg["guest_msg"]
    sense = get_sense_for_id(sid)
    if sense.mysense:
        rem_def_sense(id)
    return redirect("/sense/" + str(sid))

@app.route('/addex/<sid>', methods=['POST'])
@oidc.require_login
def addex(sid):
    username, role = userrole()
    if role not in ["viewer", "editor", "redactor"]:
        return cfg["guest_msg"]
    sense = get_sense_for_id(sid)
    if sense.mysense:
        params = post_req(request, ["label", "type", "text", "id"])
        add_ex_sense(sid, params)
    return redirect("/sense/" + str(sid))

@app.route('/remex/<sid>/<id>', methods=['GET'])
@oidc.require_login
def remex(sid, id):
    username, role = userrole()
    if role not in ["viewer", "editor", "redactor"]:
        return cfg["guest_msg"]
    sense = get_sense_for_id(sid)
    if sense.mysense:
        rem_ex_sense(id)
    return redirect("/sense/" + str(sid))

@app.route('/addqual/<sid>/<t>', methods=['POST'])
@oidc.require_login
def addqual(sid, t):
    username, role = userrole()
    if role not in ["viewer", "editor", "redactor"]:
        return cfg["guest_msg"]
    sense = get_sense_for_id(sid)
    if sense.mysense:
        params = post_req(request, ["val", "id"])
        add_val_to_sense(sid, t, params)
    return redirect("/sense/" + str(sid))

@app.route('/remqual/<sid>/<t>/<val>', methods=['GET'])
@oidc.require_login
def remqual(sid, t, val):
    username, role = userrole()
    if role not in ["viewer", "editor", "redactor"]:
        return cfg["guest_msg"]
    sense = get_sense_for_id(sid)
    if sense.mysense:
        params= {"val": val}
        rem_val_from_sense(sid, t, params)
    return redirect("/sense/" + str(sid))

@app.route('/addhead/<eid>', methods=['POST'])
@oidc.require_login
def addhead(eid):
    username, role = userrole()
    if role not in ["viewer", "editor", "redactor"]:
        return cfg["guest_msg"]
    if myentry(eid):
        params = post_req(request, ["val", "id"])
        add_headword(eid, params)
    return redirect("/entry/" + str(eid))

@app.route('/remhead/<eid>/<val>', methods=['GET'])
@oidc.require_login
def remhead(eid, val):
    username, role = userrole()
    if role not in ["viewer", "editor", "redactor"]:
        return cfg["guest_msg"]
    if myentry(eid):
        rem_headword(eid, {"val" :val})
    return redirect("/entry/" + str(eid))

@app.route('/addheadijk/<eid>', methods=['POST'])
@oidc.require_login
def addheadijk(eid):
    username, role = userrole()
    if role not in ["viewer", "editor", "redactor"]:
        return cfg["guest_msg"]
    if myentry(eid):
        params = post_req(request, ["val", "id"])
        add_headword(eid, params, True)
    return redirect("/entry/" + str(eid))

@app.route('/remheadijk/<eid>/<val>', methods=['GET'])
@oidc.require_login
def remheadijk(eid, val):
    username, role = userrole()
    if role not in ["viewer", "editor", "redactor"]:
        return cfg["guest_msg"]
    if myentry(eid):
        rem_headword(eid, {"val" :val}, True)
    return redirect("/entry/" + str(eid))

@app.route('/remove_sense/<sid>/<id>', methods=['GET'])
@oidc.require_login
def remsense(sid, id):
    username, role = userrole()
    if role not in ["viewer", "editor", "redactor"]:
        return cfg["guest_msg"]
    sense = get_sense_for_id(sid)
    if sense.mysense:
        rem_sense(sid, id)
    return redirect("/entry/" + str(id))

@app.route('/searchsense', methods=['POST'])
@oidc.require_login
def searchsense():
    username, role = userrole()
    if role not in ["viewer", "editor", "redactor"]:
        return cfg["guest_msg"]
    params = post_req(request, ["text"])
    sid, eid = get_sense_from_string(params["text"])
    if sid and eid:
        return Response('[' + str(sid) + ', '+ str(eid) +']', headers={'Content-Type': 'application/json'})
    else:
        return Response('[]', headers={'Content-Type': 'application/json'})

@app.route('/pretraga', methods=['GET', 'POST'])
@oidc.require_login
def search():
    username, role = userrole()
    if role not in ["viewer", "editor", "redactor"]:
        return cfg["guest_msg"]
    
    pretraga = None
    for_redact = None
    cont_edit = None
    recommended = None
    entries = None

    params = post_req(request, ["str", "type", "full"])
    if params:
        if params["str"] and params["type"]:
            pretraga = search_string(params["str"], params["type"], params["full"])

    entries = list_x_entries()
    recommended = get_x_entries()
    for_redact = []
    if role == "redactor":
        for_redact = get_edited()
    cont_edit = get_my()

    template = render_template('list_entries.html', for_redact=for_redact, input=params["str"], cont_edit=cont_edit, recommended=recommended, pretraga=pretraga, entries=entries)
    response = make_response(template)
    response.headers['Content-Type'] = 'text/html'
    return response

@app.route('/api', methods=['GET'])
def apiui():
    template = render_template('api.html', services=services, inputs=entries_heads)
    response = make_response(template)
    response.headers['Content-Type'] = 'text/html'
    return response

@app.route('/api/<service>', methods=['POST'])
def api(service):
    if service not in services:
        return cfg["invalid_service_msg"]
    f, open, mime = services[service]

    if not open:
        key = post_req(request, ["api_key"])
        if not key:
            return cfg["no_key_msg"]
        if not verify_api_key(key["api_key"]): 
            return cfg["invalid_key_msg"]

    params = post_req(request, ["input"])
    if not params["input"]:
        params["input"] = ""
    params["input"] = base_form(params["input"])
    return Response(f(params["input"]),  mimetype=mime,
                            headers={'Content-Type': mime})

@app.route('/dump/<service>', methods=['GET'])
@oidc.require_login
def restricted(service):
    if not user_can_dump():
        return cfg["guest_msg"]
    if service not in restricted_services:
        return cfg["invalid_service_msg"]
    f = restricted_services[service]
    outfile, mimetype = f()
    return Response(outfile, mimetype=mimetype,
                            headers={'Content-Disposition': 'inline;filename=rssj_dump.' + service})

@app.route("/logout")
def logout():
    oidc.logout()
    return redirect("/")

@app.errorhandler(500)
def internal_error(error):
    return cfg["error_msg"]


if __name__ == "__main__":
    port = int(environ.get("PORT", 5000))
    app.run(host='0.0.0.0', port=port)
