from __version__ import __version__
from flask import Blueprint, request, current_app
from flask import render_template, jsonify, send_from_directory
from flask_socketio import emit
import sqlalchemy
import os
import json

from app.models.kitchen_model import Kitchen


from .. import db
from .. import socketio
from ..models.database.object_model import Object as DBObject
from ..models.database.kitchen_model import DBKitchen
from ..models.ticket_model import Ticket
from ..models.component_model import TicketStatus

bp = Blueprint("kds", __name__, url_prefix="")


@bp.route("/", methods=["GET"])
def index(kitchenId=None):
    """
    A route to display the tickets to be prepared in the kitchen.
    """

    # Updating static/assets/assets/config.json
    configFilePath = os.path.join(
        current_app.config["BASE_DIR"],
        "app/static/assets/assets/",
        "config.json",
    )

    # Reading from json file
    with open(configFilePath, "r") as configFile:
        jsonObject = json.load(configFile)

    # Updating config
    jsonObject["ws-server"] = current_app.config["KDS_API_SERVER"]
    jsonObject["ws-port"] = current_app.config["KDS_API_PORT"]

    # Writing from json file
    with open(configFilePath, "w") as configFile:
        json.dump(jsonObject, configFile)

    return render_template("index.html")
    # return "OK"


@bp.route("/<path:resource>", methods=["GET"])
def static(resource):
    """
    A route to serve files instead of prefixing their paths with "static/".

    Having "static/" in the URL prevent correct Flutter App reload.
    """

    staticPath = os.path.join(current_app.config["BASE_DIR"], "app/static/")
    return send_from_directory(staticPath, resource)


@bp.route("/configuration", methods=["POST"])
def configuration():
    """
    A route to handle configuration request.
    """
    req = request.json

    response = {}
    try:
        # Updating static/assets/assets/config.json
        configFilePath = os.path.join(
            current_app.config["BASE_DIR"],
            "app/static/assets/assets/",
            "config.json",
        )

        current_app.logger.info(configFilePath)
        # Initialize jsonObject
        jsonObject = {}

        # Check if the file exists
        if os.path.isfile(configFilePath):
            # Reading from json file
            with open(configFilePath, "r") as configFile:
                jsonObject = json.load(configFile)
        else:
            current_app.logger.warning(
                "%s not found! Creating one." % (configFilePath)
            )

        # Updating config
        for item in req.keys():
            jsonObject[item] = req[item]
            current_app.logger.info("%s = %s" % (item, req[item]))

        # Writing from json file
        with open(configFilePath, "w") as configFile:
            json.dump(jsonObject, configFile)

        response["status"] = 200

    except Exception as ex:
        response["status"] = 400
        response["error"] = str(ex)
        current_app.logger.error(ex)
        raise ex

    # Sending response
    return jsonify(response), response["status"]


@bp.route("/version", methods=["GET"])
def version():
    """
    A route to handle version request.
    """

    response = {}
    response["application"] = "KDS Server"
    response["version"] = __version__

    # Sending response
    return jsonify(response)


@bp.route("/kitchen-create", methods=["POST"])
def kitchen_create():
    """
    TODO: Add definition ... .
    """

    kitchen = Kitchen()
    kitchen.from_json(request.json)
    kitchen.to_db()

    socketio.emit("kitchen-created", kitchen.to_json(), broadcast=True)

    return {}, 200


@bp.route("/kitchen-update", methods=["POST"])
def kitchen_update():
    """
    TODO: Add definition ... .
    """

    return {}, 400
    # if "id" in request.json:
    #     db_kitchen = (
    #         db.session.query(DBKitchen)
    #         .filter(DBKitchen.kth_kitchen_id == request.json["id"])
    #         .one()
    #     )
    #     if db_kitchen:
    #         db_kitchen.kth_kitchen = request.json["title"]

    # else:
    #     pass

    # kitchen = Kitchen()
    # kitchen.from_json(request.json)
    # kitchen.to_db()

    # socketio.emit("kitchen-updated", kitchen.to_json(), broadcast=True)

    # return {}, 200


@bp.route("/kitchen-get-all", methods=["GET"])
def kitchen_get_all():
    """
    TODO: Add definition ... .
    """

    kitchens = []
    db_kitchens = db.session.query(DBKitchen).all()
    for db_kitchen in db_kitchens:
        kitchen = Kitchen()
        kitchen.from_db(db_kitchen)
        kitchens.append(kitchen.to_json())

    return jsonify(kitchens)


@bp.route("/kitchen-do-reset", methods=["GET"])
def kitchen_do_reset():
    """
    TODO: Add definition ... .
    """

    db.session.begin()
    db.session.execute(sqlalchemy.delete(DBKitchen))
    db.session.execute(
        "DELETE from main.sqlite_sequence where name = 'kds_kitchen'"
    )
    db.session.commit()

    socketio.emit("kitchen-reset", {}, broadcast=True)

    return {}, 200


@bp.route("/mqtt", methods=["POST"])
def ticket_create():
    """
    TODO: Add definition ... .
    """

    ticket = Ticket()
    ticket.from_json(request.json)
    ticket.to_db()

    socketio.emit("ticket-ordered", ticket.to_json(), broadcast=True)

    return {}, 200


@bp.route("/ticket-get-all", methods=["GET"])
def ticket_get_all():
    """
    A route to display the current queue (of tickets).
    """

    tickets = []
    db_tickets = (
        db.session.query(DBObject).filter(DBObject.obj_type == "Ticket").all()
    )
    for db_ticket in db_tickets:
        ticket = Ticket()
        ticket.from_db(db_ticket)
        tickets.append(ticket.to_json())

    return jsonify(tickets)


@bp.route("/ticket-do-reset", methods=["GET"])
def ticket_do_reset():
    """
    A route to reste the current queue.
    """

    db.session.begin()
    db.session.execute(sqlalchemy.delete(DBObject))
    db.session.execute(
        "DELETE from main.sqlite_sequence where name = 'kds_object'"
    )
    db.session.commit()

    # socketio.emit("reset", {}, broadcast=True)
    socketio.emit("ticket-reset", {}, broadcast=True)

    return {}, 200


@socketio.on("connect")
def on_connect():
    """
    An event handler to ... .
    """
    print("Display [%s] connected!" % (request.sid))


@socketio.on("kitchen-get-all")
def on_kitchen_get_all():
    """
    An event handler to ... .
    """

    kitchens = []
    db_kitchens = db.session.query(DBKitchen).all()
    for db_kitchen in db_kitchens:
        kitchen = Kitchen()
        kitchen.from_db(db_kitchen)
        kitchens.append(kitchen.to_json())

    emit(
        "kitchen-all", kitchens, broadcast=True
    )  # TODO: Remove ', broadcast=True'


@socketio.on("order-set-status")
def order_set_status(data):
    """
    TODO: Add definition ... .
    """

    print(data)
    print("Marking Order[%s] as %s" % (data["reference"], data["status"]))
    tickets = []
    db_tickets = (
        db.session.query(DBObject)
        .filter(DBObject.obj_reference == data["reference"])
        .all()
    )
    for db_ticket in db_tickets:
        ticket = Ticket()
        ticket.from_db(db_ticket)

        print("Marking Ticket[%d] as %s" % (ticket.id, data["status"]))
        db_ticket.obj_status = data["status"]
        ticket.status = data["status"]

        for item in ticket.items:
            db_item = (
                db.session.query(DBObject)
                .filter(DBObject.obj_object_id == item.id)
                .one()
            )
            print("Marking Item[%d] as %s" % (item.id, data["status"]))
            db_item.obj_status = data["status"]
            item.status = data["status"]

        tickets.append(ticket.to_json())

    db.session.commit()

    if data["status"] != str(TicketStatus.served):
        socketio.emit("ticket-updated", tickets, broadcast=True)
    else:
        socketio.emit("ticket-delete", tickets, broadcast=True)


@socketio.on("ticket-get-all")
def on_ticket_get_all():
    """
    An event handler to ... .
    """

    tickets = []
    db_tickets = (
        db.session.query(DBObject)
        .filter(DBObject.obj_type == "Ticket")
        .filter(DBObject.obj_status != str(TicketStatus.served))
        .all()
    )
    for db_ticket in db_tickets:
        ticket = Ticket()
        ticket.from_db(db_ticket)
        tickets.append(ticket.to_json())

    emit(
        "ticket-all", tickets, broadcast=True
    )  # TODO: Remove ', broadcast=True'


@socketio.on("ticket-set-status")
def ticket_set_status(data):
    """
    TODO: Add definition ... .
    """

    print(data)
    ticket = Ticket()
    db_ticket = (
        db.session.query(DBObject)
        .filter(DBObject.obj_object_id == data["id"])
        .one()
    )
    ticket.from_db(db_ticket)

    print("Marking Ticket[%d] as %s" % (data["id"], data["status"]))
    db_ticket.obj_status = data["status"]
    ticket.status = data["status"]

    for item in ticket.items:
        db_item = (
            db.session.query(DBObject)
            .filter(DBObject.obj_object_id == item.id)
            .one()
        )
        print("Marking Item[%d] as %s" % (item.id, data["status"]))
        db_item.obj_status = data["status"]
        item.status = data["status"]

    db.session.commit()
    socketio.emit("ticket-updated", [ticket.to_json()], broadcast=True)


@socketio.on("ticket-set-ticket-item-status")
def ticket_set_ticket_item_status(data):
    """
    TODO: Add definition ... .
    """

    print(data)
    print("Marking Item[%d] as %s" % (data["id"], data["status"]))
    db_item = (
        db.session.query(DBObject)
        .filter(DBObject.obj_object_id == data["id"])
        .one()
    )
    db_item.obj_status = data["status"]
    db.session.commit()

    # Get the whole (and updated) Ticket from DB
    db_ticket = (
        db.session.query(DBObject)
        .filter(DBObject.obj_object_id == db_item.obj_parent_id)
        .one()
    )
    ticket = Ticket()
    ticket.from_db(db_ticket)
    socketio.emit("ticket-updated", [ticket.to_json()], broadcast=True)
