from flask import current_app
from time import sleep
from json import dumps, loads
import six
from enum import Enum
from datetime import datetime

from .. import db
from .database.object_model import Object as DBObject
from .tools import parse_text, parse_int, parse_style
from .item_model import Item
from .component_model import TicketStatus


class Ticket:
    """
    TODO: Add definition ... .
    """

    def __init__(self):
        """
        TODO: Add definition ... .
        """

        self.id = -1
        self.reference = ""
        self.reference_style = {}
        self.title = ""
        self.title_style = {}
        self.date = ""
        self.date_style = {}
        self.waiter = ""
        self.waiter_style = {}
        self.kitchenId = 0
        self.kitchen = ""
        self.kitchen_style = {}
        self.printer = ""
        self.printer_style = {}
        self.printerId = 0
        self.mode = ""
        self.mode_style = {}
        self.table = 0
        self.guests = 0
        self.lalivraison = ""
        self.lalivraison_style = {}
        self.client = ""
        self.client_style = {}
        self.note = ""
        self.note_style = {}
        self.status = ""
        self.items = []
        self.created_dt = 0
        self.updated_dt = 0

    def from_json(self, jsonObject):
        """
        Extracts the attributes of the jsonObject.

        Example of the expected json :
        {
            "reference":123456,
            "kitchenId":1,
            "kitchen":"The Kitchen",
            "kitchen_style":{},
            "table":123,
            "guests":2,

            "titre":"123456 ...",
            "titre_style": {},
            "date": "26/03/2021 09:01:34",
            "date_style": {},
            "imprimante": "",
            "imprimante_style": {},
            "type": "Sur Place",
            "type_style": {},
            "caissier": "Hamid",
            "caissier_style": {},
            "imprimanteId": 1,
            "lalivraison" : "123456",
            "lalivraison_style": {},
            "client" : "0612345678, adresse, ...",
            "client_style": {},
            "texte": "Un Texte",
            "texte_style": {},
            "lignes": [
                {
                    "designation": "Plat XXXXXXX",
                    "qte": 1,
                    "note": "Sans quelque chose",
                    "style": {}
                },
                {
                    "designation": "Formule XXXXXXX",
                    "qte": 2,
                    "note": "Note pour la formule",
                    "style": {},
                    "formule": [
                        {
                            "designation": "Plat XXXXXXX",
                            "qte": 1,
                            "note": "Sans quelque chose",
                            "style": {}
                        },
                        {
                            "designation": "Plat XXXXXXX",
                            "qte": 1,
                            "note": "Sans quelque chose",
                            "style": {}
                        }
                    ]
                }
            ]
        }

        Attributs Style :
        {
            "align":"center" | "left" | "right",
            "font":"a" | "b",
            "width": 1-8,
            "height":1-8,
            "invert":0-1,
            "text_type":"B" | "U" | "BU"
        }
        """

        self.id = parse_int(jsonObject.get("id"))
        self.reference = parse_text(jsonObject.get("reference"))
        self.reference_style = parse_style(jsonObject.get("reference_style"))

        if "title" in jsonObject:
            self.title = parse_text(jsonObject.get("title"))
        else:
            self.title = parse_text(jsonObject.get("titre"))
        if "title_style" in jsonObject:
            self.title_style = parse_style(jsonObject.get("title_style"))
        else:
            self.title_style = parse_style(jsonObject.get("titre_style"))

        self.date = parse_text(jsonObject.get("date"))
        self.date_style = parse_style(jsonObject.get("date_style"))

        if "waiter" in jsonObject:
            self.waiter = parse_text(jsonObject.get("waiter"))
        else:
            self.waiter = parse_text(jsonObject.get("caissier"))
        if "waiter_style" in jsonObject:
            self.waiter_style = parse_style(jsonObject.get("waiter_style"))
        else:
            self.waiter_style = parse_style(jsonObject.get("caissier_style"))

        self.kitchenId = parse_int(jsonObject.get("kitchenId"))
        self.kitchen = parse_text(jsonObject.get("kitchen"))
        self.kitchen_style = parse_style(jsonObject.get("kitchen_style"))

        if "printerId" in jsonObject:
            self.printerId = parse_int(jsonObject.get("printerId"))
        else:
            self.printerId = parse_int(jsonObject.get("imprimanteId"))
        if "printer" in jsonObject:
            self.printer = parse_text(jsonObject.get("printer"))
        else:
            self.printer = parse_text(jsonObject.get("imprimante"))
        if "printer_style" in jsonObject:
            self.printer_style = parse_style(jsonObject.get("printer_style"))
        else:
            self.printer_style = parse_style(jsonObject.get("imprimante_style"))

        if "mode" in jsonObject:
            self.mode = parse_text(jsonObject.get("mode"))
        else:
            self.mode = parse_text(jsonObject.get("type"))
        if "mode_style" in jsonObject:
            self.mode_style = parse_style(jsonObject.get("mode_style"))
        else:
            self.mode_style = parse_style(jsonObject.get("type_style"))

        self.table = parse_text(jsonObject.get("table"))
        self.guests = parse_int(jsonObject.get("guests"))

        if "lalivraison" in jsonObject:
            self.lalivraison = parse_text(jsonObject.get("lalivraison"))
        if "lalivraison_style" in jsonObject:
            self.lalivraison_style = parse_style(jsonObject.get("lalivraison_style"))

        if "client" in jsonObject:
            self.client = parse_text(jsonObject.get("client"))
        if "client_style" in jsonObject:
            self.client_style = parse_style(jsonObject.get("client_style"))

        if "note" in jsonObject:
            self.note = parse_text(jsonObject.get("note"))
        else:
            self.note = parse_text(jsonObject.get("texte"))
        if "note_style" in jsonObject:
            self.note_style = parse_style(jsonObject.get("note_style"))
        else:
            self.note_style = parse_style(jsonObject.get("texte_style"))

        self.status = parse_text(jsonObject.get("status"), default=str(TicketStatus.ordered))

        if "lignes" in jsonObject:
            if isinstance(jsonObject.get("lignes"), list):
                for jsonItem in jsonObject.get("lignes"):
                    item = Item()
                    item.from_json(jsonItem)
                    self.items.append(item)
                # end for
            # end if
        # end if

    # end def

    def to_json(self):
        """
        TODO: Add definition ... .
        """

        jsonObject = {
            "id": self.id,
            "reference": self.reference,
            "reference_style": self.reference_style,
            "title": self.title,
            "title_style": self.title_style,
            "date": self.date,
            "date_style": self.date_style,
            "waiter": self.waiter,
            "waiter_style": self.waiter_style,
            "kitchenId": self.kitchenId,
            "kitchen": self.kitchen,
            "kitchen_style": self.kitchen_style,
            "printer": self.printer,
            "printer_style": self.printer_style,
            "printerId": self.printerId,
            "mode": self.mode,
            "mode_style": self.mode_style,
            "table": self.table,
            "guests": self.guests,
            "lalivraison":self.lalivraison,
            "lalivraison_style":self.lalivraison_style,
            "client":self.client,
            "client_style":self.client_style,
            "note": self.note,
            "note_style": self.note_style,
            "status": self.status,
            "createdDate" : self.created_dt,
            "updatedDate" : self.updated_dt,
        }
        jsonObject["items"] = []

        for item in self.items:
            jsonObject["items"].append(item.to_json())

        return jsonObject

    # enf def

    def to_db(self):
        """
        TODO: Add definition ... .
        """

        db_ticket = DBObject()
        db_ticket.obj_kitchen_id = self.kitchenId
        db_ticket.obj_reference = self.reference
        db_ticket.obj_type = "Ticket"
        db_ticket.obj_json = dumps(
            {
                "reference": self.reference,
                "date": self.date,
                "waiter": self.waiter,
                "kitchen": self.kitchen,
                "kitchenId": self.kitchenId,
                "mode": self.mode,
                "table": self.table,
                "guests": self.guests,
                "lalivraison":self.lalivraison,
                "client":self.client,
                "note": self.note,
            }
        )
        db_ticket.obj_status = str(TicketStatus.ordered) #TODO: use self.status

        # Inserting a new record
        db.session.add(db_ticket)
        db.session.commit()
        # Setting id
        self.id = db_ticket.obj_object_id
        # Setting created_dt, updated_dt
        self.created_dt = datetime.timestamp(db_ticket.obj_create_dt)
        self.updated_dt = datetime.timestamp(db_ticket.obj_update_dt)

        for item in self.items:
            # Creating a new instance
            db_item = DBObject()
            db_item.obj_parent_id = db_ticket.obj_object_id
            db_item.obj_type = "Item"
            db_item.obj_json = dumps(
                {
                    "title": item.title,
                    "quantity": item.quantity,
                    "note": item.note,
                    "style": item.style,
                }
            )
            db_item.obj_status = str(TicketStatus.ordered) #TODO: use item.status

            # Inserting a new record
            db.session.add(db_item)
            db.session.commit()
            # Setting id, parentId
            item.id = db_item.obj_object_id
            item.parentId = db_ticket.obj_object_id
            # Setting created_dt, updated_dt
            item.created_dt = datetime.timestamp(db_item.obj_create_dt)
            item.updated_dt = datetime.timestamp(db_item.obj_update_dt)

            for component in item.components:
                # Creating a new instance
                db_component = DBObject()
                db_component.obj_parent_id = db_item.obj_object_id
                db_component.obj_type = "Component"
                db_component.obj_json = dumps(
                    {
                        "title": component.title,
                        "quantity": component.quantity,
                        "note": component.note,
                        "style": component.style,
                    }
                )
                db_component.obj_status = str(TicketStatus.ordered) #TODO: use component.status

                # Inserting a new record
                db.session.add(db_component)
                db.session.commit()
                # Setting id, parentId
                component.id = db_component.obj_object_id
                component.parentId = db_item.obj_object_id
                # Setting created_dt, updated_dt
                component.created_dt = datetime.timestamp(db_component.obj_create_dt)
                component.updated_dt = datetime.timestamp(db_component.obj_update_dt)

            # end for
        # end for

    # enf def

    def from_db(self, db_ticket: DBObject):
        """
        TODO: Add definition ... .
        """

        self.reference = db_ticket.obj_reference

        self.from_json(loads(db_ticket.obj_json))

        db_items = (
            db.session.query(DBObject)
            .filter(DBObject.obj_parent_id == db_ticket.obj_object_id)
            .all()
        )

        for db_item in db_items:
            item = Item()
            item.from_db(db_item)
            self.items.append(item)

        self.id = db_ticket.obj_object_id
        self.status = db_ticket.obj_status
        self.created_dt = datetime.timestamp(db_ticket.obj_create_dt)
        self.updated_dt = datetime.timestamp(db_ticket.obj_update_dt)

    # enf def

    def to_str(self):
        """
        TODO: Add definition ... .
        """

        out = "" + "\n"
        out += str(self.title) + "\n"
        out += str(self.date) + "\n"
        out += str(self.printer) + "\n"
        out += str(self.mode) + "\n"
        out += str(self.waiter) + "\n"
        out += "Printer Id: " + str(self.printerId) + "\n"
        out += str(self.note) + "\n"

        if self.items:
            out += str("-" * 48) + "\n"
            out += "QTE | ARTICLE" + "\n"
            out += str("-" * 48) + "\n"

            for item in self.items:
                out += (
                    "%2s%s%s" % (str(item.quantity)[:2], " " * 2, str(item.title))
                    + "\n"
                )
                out += (
                    "%s%s" % (" " * 4, str(item.note)[:44]) + "\n" if item.note else ""
                )
                for component in item.components:
                    out += (
                        "%s%2s%s%s"
                        % (
                            " " * 2,
                            str(component.quantity)[:2],
                            " " * 2,
                            str(component.title)[:42],
                        )
                        + "\n"
                    )
                    out += (
                        "%s%s" % (" " * 6, str(component.note)[:42]) + "\n"
                        if component.note
                        else ""
                    )
                # end for
            # enf for
        # end if

        return out

    # end def

    def to_printer(self, printer):
        """
        TODO: Add definition ... .
        """

        printer._raw(b"\x1b" + b"\x42" + six.int2byte(2) + six.int2byte(1))
        sleep(1)
        printer.set(**self.title_style)
        printer.text(str(self.title) + "\n")
        printer.set(**self.date_style)
        printer.text(str(self.date) + "\n")
        printer.set(**self.printer_style)
        printer.text(str(self.printer) + "\n")
        printer.set(**self.mode_style)
        printer.text(str(self.mode) + "\n")
        printer.set(**self.waiter_style)
        printer.text(str(self.waiter) + "\n")
        printer.set()
        printer.text("Printer Id: " + str(self.printerId) + "\n")
        printer.set(**self.note_style)
        printer.text(str(self.note) + "\n")

        if self.items:
            printer.set(text_type="B")
            printer.text(str("-" * 48) + "\n")
            printer.text("QTE | ARTICLE" + "\n")
            printer.text(str("-" * 48) + "\n")
            printer.set()

            for item in self.items:
                printer.set(**parse_style(item.style))
                printer.text(
                    "%2s%s%s" % (str(item.quantity)[:2], " " * 2, str(item.title))
                    + "\n"
                )

                if item.note:
                    printer.set()
                    printer.text("%s%s" % (" " * 4, str(item.note)[:44]) + "\n")

                printer.set()
                printer.text("\n")

                for component in item.components:
                    printer.set(**parse_style(component.style))
                    printer.text(
                        "%s%4s%s%s"
                        % (
                            " " * 2,
                            str(component.quantity)[:2],
                            " " * 2,
                            str(component.title)[:42],
                        )
                        + "\n"
                    )
                    if component.note:
                        printer.set()
                        printer.text(
                            "%s%s" % (" " * 6, str(component.note)[:42]) + "\n"
                        )

                    printer.set()
                    printer.text("\n")

                # end for
            # end for
        # enf if

        printer.set()
        printer.cut()
        printer._raw(b"\x1b" + b"\x42" + six.int2byte(2) + six.int2byte(2))
        sleep(current_app.config["TICKET_TIMEOUT"])

    # end def


# end class
