from flask import render_template, Blueprint, redirect, url_for, request, abort, flash, make_response, current_app
from flask_user import login_required, current_user
from datatables import ColumnDT, DataTables
import time
from app.models import CharacterInfo, CharacterXML, Account, db
from app.schemas import CharacterInfoSchema
from app.forms import RescueForm, CharXMLUploadForm
from app import gm_level, log_audit
from app.luclient import translate_from_locale
import xmltodict
import xml.etree.ElementTree as ET
import json


character_blueprint = Blueprint('characters', __name__)

character_schema = CharacterInfoSchema()


@character_blueprint.route('/', methods=['GET'])
@login_required
@gm_level(3)
def index():
    return render_template('character/index.html.j2')


@character_blueprint.route('/approve_name/<id>/<action>', methods=['GET'])
@login_required
@gm_level(3)
def approve_name(id, action):
    character = CharacterInfo.query.filter(CharacterInfo.id == id).first()

    if action == "approve":
        if character.pending_name:
            character.name = character.pending_name
            character.pending_name = ""
            log_audit(f"Approved ({character.id}){character.pending_name} from {character.name}")
            flash(
                f"Approved ({character.id}){character.pending_name} from {character.name}",
                "success"
            )
        else:
            log_audit("Cannot make character name empty")
            flash(
                "Cannot make character name empty",
                "danger"
            )
        character.needs_rename = False

    elif action == "rename":
        character.needs_rename = True
        log_audit(
            f"Marked character ({character.id}){character.name} \
                (Pending Name: {character.pending_name if character.pending_name else 'None'}) as needing Rename")
        flash(
            f"Marked character {character.name} \
                (Pending Name: {character.pending_name if character.pending_name else 'None'}) as needing Rename",
            "danger"
        )

    character.save()
    return redirect(request.referrer if request.referrer else url_for("main.index"))


@character_blueprint.route('/view/<id>', methods=['GET'])
@login_required
def view(id):

    character_data = CharacterInfo.query.filter(CharacterInfo.id == id).first()

    if character_data == {}:
        abort(404)
        return

    if current_user.gm_level < 3:
        if character_data.account_id and character_data.account_id != current_user.id:
            abort(403)
            return
    character_json = xmltodict.parse(
        CharacterXML.query.filter(
            CharacterXML.id == id
        ).first().xml_data.replace("\"stt=", "\" stt="),
        attr_prefix="attr_"
    )

    # print json for reference
    # with open("errorchar.json", "a") as file:
    #     file.write(
    #         json.dumps(character_json, indent=4)
    #     )

    # stupid fix for jinja parsing
    character_json["obj"]["inv"]["holdings"] = character_json["obj"]["inv"].pop("items")
    # sort by items slot index
    if type(character_json["obj"]["inv"]["holdings"]["in"]) == list:
        for inv in character_json["obj"]["inv"]["holdings"]["in"]:
                if "i" in inv.keys() and type(inv["i"]) == list:
                    inv["i"] = sorted(inv["i"], key=lambda i: int(i['attr_s']))

    return render_template(
        'character/view.html.j2',
        character_data=character_data,
        character_json=character_json
    )


@character_blueprint.route('/view_xml/<id>', methods=['GET'])
@login_required
def view_xml(id):

    character_data = CharacterInfo.query.filter(CharacterInfo.id == id).first()

    if character_data == {}:
        abort(404)
        return

    if current_user.gm_level < 3:
        if character_data.account_id and character_data.account_id != current_user.id:
            abort(403)
            return

    character_xml = CharacterXML.query.filter(
        CharacterXML.id == id
    ).first().xml_data.replace("\"stt=", "\" stt=")

    response = make_response(character_xml)
    response.headers.set('Content-Type', 'text/xml')
    return response


@character_blueprint.route('/get_xml/<id>', methods=['GET'])
@login_required
def get_xml(id):

    character_data = CharacterInfo.query.filter(CharacterInfo.id == id).first()

    if character_data == {}:
        abort(404)
        return
    if current_user.gm_level < 3:
        if character_data.account_id and character_data.account_id != current_user.id:
            abort(403)
            return

    character_xml = CharacterXML.query.filter(
        CharacterXML.id == id
    ).first().xml_data

    response = make_response(character_xml)
    response.headers.set('Content-Type', 'attachment/xml')
    response.headers.set(
        'Content-Disposition',
        'attachment',
        filename=f"{character_data.name}.xml"
    )
    return response


@character_blueprint.route('/restrict/<bit>/<id>', methods=['GET'])
@login_required
@gm_level(3)
def restrict(id, bit):

    # restrict to bit 4-6
    if 6 < int(bit) < 3:
        abort(403)
        return

    character_data = CharacterInfo.query.filter(CharacterInfo.id == id).first()

    if character_data == {}:
        abort(404)
        return

    log_audit(f"Updated ({character_data.id}){character_data.name}'s permission map to \
        {character_data.permission_map ^ (1 << int(bit))} from {character_data.permission_map}")

    character_data.permission_map ^= (1 << int(bit))
    character_data.save()

    return redirect(request.referrer if request.referrer else url_for("main.index"))


@character_blueprint.route('/rescue/<id>', methods=['GET', 'POST'])
@login_required
@gm_level(3)
def rescue(id):

    form = RescueForm()

    character_data = CharacterXML.query.filter(
        CharacterXML.id == id
    ).first()

    character_xml = ET.XML(character_data.xml_data.replace("\"stt=", "\" stt="))
    for zone in character_xml.findall('.//r'):
        if int(zone.attrib["w"]) % 100 == 0:
            form.save_world.choices.append(
                (
                    zone.attrib["w"],
                    translate_from_locale(f"ZoneTable_{zone.attrib['w']}_DisplayDescription")
                )
            )

    if form.validate_on_submit():
        new_zone = character_xml.find(f'.//r[@w="{form.save_world.data}"]')
        char = character_xml.find(".//char")
        char.attrib["lzx"] = new_zone.attrib["x"]
        char.attrib["lzy"] = new_zone.attrib["y"]
        char.attrib["lzz"] = new_zone.attrib["z"]
        char.attrib["lwid"] = form.save_world.data

        character_data.xml_data = ET.tostring(character_xml)
        character_data.save()

        return redirect(url_for('characters.view', id=id))

    return render_template("character/rescue.html.j2", form=form)


@character_blueprint.route('/upload/<id>', methods=['GET', 'POST'])
@login_required
@gm_level(8)
def upload(id):
    if not current_app.config["ENABLE_CHAR_XML_UPLOAD"]:
        flash("You must enable this setting to do this", "danger")
        return redirect(url_for('characters.view', id=id))

    form = CharXMLUploadForm()

    character_data = CharacterXML.query.filter(
        CharacterXML.id == id
    ).first()

    if form.validate_on_submit():
        character_data.xml_data = form.char_xml.data
        character_data.save()
        flash("You accept all consequences from these actions", "danger")
        log_audit(f"Updated {character_data.id}'s xml data")
        return redirect(url_for('characters.view', id=id))
    form.char_xml.data = character_data.xml_data
    return render_template("character/upload.html.j2", form=form)


@character_blueprint.route('/get/<status>', methods=['GET'])
@login_required
@gm_level(3)
def get(status):
    columns = [
        ColumnDT(CharacterInfo.id),                 # 0
        ColumnDT(Account.username),                 # 1
        ColumnDT(CharacterInfo.name),               # 2
        ColumnDT(CharacterInfo.pending_name),       # 3
        ColumnDT(CharacterInfo.needs_rename),       # 4
        ColumnDT(CharacterInfo.last_login),         # 5
        ColumnDT(CharacterInfo.permission_map),     # 6
    ]

    query = None
    if status == "approved":
        query = db.session.query().select_from(CharacterInfo).join(Account).filter((CharacterInfo.pending_name == "") & (CharacterInfo.needs_rename == False))
    elif status == "unapproved":
        query = db.session.query().select_from(CharacterInfo).join(Account).filter((CharacterInfo.pending_name != "") | (CharacterInfo.needs_rename == True))
    else:
        query = db.session.query().select_from(CharacterInfo).join(Account)

    params = request.args.to_dict()

    rowTable = DataTables(params, query, columns)

    data = rowTable.output_result()
    for character in data["data"]:
        id = character["0"]
        character["0"] = f"""
            <div class="d-none">{id}</div>
            <a role="button" class="btn btn-primary btn btn-block"
                href='{url_for('characters.view', id=id)}'>
                View
            </a>
        """

        if not character["4"]:
            character["0"] += f"""
            <a role="button" class="btn btn-danger btn btn-block"
                href='{url_for('characters.approve_name', id=id, action="rename")}'>
                Needs Rename
            </a>
        """

        if character["3"] or character["4"]:
            character["0"] += f"""
            <a role="button" class="btn btn-success btn btn-block"
                href='{url_for('characters.approve_name', id=id, action="approve")}'>
                Approve Name
            </a>
        """

        character["1"] = f"""
            <a role="button" class="btn btn-primary btn btn-block"
                href='{url_for('accounts.view', id=Account.query.filter(Account.username==character["1"]).first().id)}'>
                View {character["1"]}
            </a>
        """

        if character["4"]:
            character["4"] = '''<h1 class="far fa-check-square text-danger"></h1>'''
        else:
            character["4"] = '''<h1 class="far fa-times-circle text-success"></h1>'''

        character["5"] = time.ctime(character["5"])

        perm_map = character["6"]
        character["6"] = ""

        if perm_map & (1 << 4):
            character["6"] += "Restricted Trade</br>"

        if perm_map & (1 << 5):
            character["6"] += "Restricted Mail</br>"

        if perm_map & (1 << 6):
            character["6"] += "Restricted Chat</br>"

    return data
