import logging

from model.customexception import ProductNotExistsException

from actions.do_complete_option import doCompleteOption
from actions.products.get_products import (
    get_product_by_barcode,
    get_product_price_from_cache,
)
from api.order import get_order_service
from order_api.dto.exception import CannotAddProductWithMandatoryCustomizationOnTotal
from sysactions import (
    action,
    show_messagebox,
)

logger = logging.getLogger("PosActions")


@action
def do_sell_product_by_barcode(
    pos_id,                     # type: str
    barcode_number,             # type: str
    check_consistency=False,    # type: bool
    received_qty="1",           # type: str
    seat="",                    # type: str
):
    # type: (...) -> str
    return sell_product_by_barcode(
        pos_id=pos_id,
        barcode_number=barcode_number,
        check_consistency=check_consistency,
        received_qty=received_qty,
        seat=seat,
    )


def sell_product_by_barcode(
    pos_id,                     # type: str
    barcode_number,             # type: str
    check_consistency=False,    # type: bool
    received_qty="1",           # type: str
    seat="",                    # type: str
):
    # type: (...) -> str

    try:
        if not barcode_number:
            return "False"

        barcode_number = str(barcode_number).zfill(13)
        if check_consistency and not _is_a_valid_ean13(barcode=barcode_number):
            logger.info("Barcode {0} is not a Valid EAN13".format(barcode_number))
            show_messagebox(
                posid=pos_id,
                message="$BARCODE_WRONG",
                buttons="$OK",
            )
            return "False"

        success = _sell_product(
            pos_id=pos_id,
            barcode_number=str(barcode_number),
            quantity=received_qty,
            seat=seat,
        )

        if not success:
            return "False"

        order_service = get_order_service(pos_id=pos_id)
        order_picture = order_service.get_order_picture()
        if order_picture is not None and order_picture.get("state") == "TOTALED":
            show_messagebox(
                posid=pos_id,
                message="$PRODUCT_SUCCESSFULLY_ADDED",
                timeout=500,
            )

        table_id_prop = order_picture.find(".//CustomOrderProperties/OrderProperty[@key='{}']".format("TABLE_ID"))
        if table_id_prop is not None:
            from msgbusboundary import MsgBusTableService as tableService
            from table_actions import create_table_modified_event

            table_id = table_id_prop.get("value", None)
            table_picture = tableService().get_table_picture(pos_id, table_id)
            create_table_modified_event(pos_id, table_picture)

        return "True"
    except ProductNotExistsException:
        return "Clear"
    except CannotAddProductWithMandatoryCustomizationOnTotal as ex:
        show_messagebox(
            posid=pos_id,
            message="$PRODUCT_WITH_MANDATORY_CUSTOMIZATIONS_ON_TOTAL_ORDER_ERROR|{}".format(ex.part_name),
        )
    except (Exception,):
        logger.exception("Error on [sell_product_by_barcode]")
        return "False"


def _sell_product(
    pos_id,             # typs: str
    barcode_number,     # type: str
    quantity,           # type: str
    seat,               # type: str
):
    # type: (...) -> bool

    if barcode_number.startswith("2"):
        logger.info("Barcode {0} is a Scale Type Code".format(barcode_number))
        plu, calc_qty = _ticket_from_scale(
            barcode=barcode_number,
            pos_id=pos_id,
        )
        doCompleteOption(
            pos_id=pos_id,
            context="1",
            part_code=plu,
            qty=calc_qty,
            seat=seat,
        )

        return True

    logger.info("Barcode {0} ".format(barcode_number))
    product_by_barcode = get_product_by_barcode(barcode=barcode_number)

    if product_by_barcode:
        response = doCompleteOption(
            pos_id=pos_id,
            context="1",
            part_code=product_by_barcode["plu"],
            qty=quantity,
            seat=seat,
            barcode_number=product_by_barcode["name"],
        )
        if response == "Clear":
            return False

        return True

    logger.info("Scanned barcode {0} not found on the product database!".format(barcode_number))
    show_messagebox(
        posid=pos_id,
        message="$BARCODE_NOT_FOUND|{}".format(barcode_number),
        buttons="$OK",
    )

    return False


def _is_a_valid_ean13(
    barcode,   # type: str
):
    # type: (...) -> bool

    if len(str(barcode)) > 13:
        return False

    barcode = list(barcode)
    index = 0
    value = 0
    dv = barcode.pop()
    for x in barcode:
        value = value + (int(x) * ((index % 2) * 2 + 1))
        index += 1
    value2 = (((int(value / 10) + 1) * 10) - value) % 10
    if value2 != int(dv):
        return False
    return True


def _ticket_from_scale(
    barcode,   # type: str
    pos_id,    # type: str
):
    # type: (...) -> (str, float)

    from posactions import (
        scale_ticket_mode,
        barcode_pattern_reading,
    )

    try:
        first_x = barcode_pattern_reading.find("X")
        first_0 = barcode_pattern_reading.find("0")
        first_y = barcode_pattern_reading.find("Y")
        first_w = barcode_pattern_reading.find("W")
        plu = int(barcode[first_x:first_0 if first_0 > -1 else first_y])
        param = int(barcode[first_y:first_w])

        if scale_ticket_mode == 'price':
            final_price = float(param) / 100
            price = float(get_product_price_from_cache(plu, pos_id))
            qtd = round((final_price / price), 3)
        else:
            qtd = round(float(param) / 1000, 3)

        return str(plu), qtd
    except ProductNotExistsException as ex:
        logger.info("The product {} in Barcode does not exists".format(ex.message))
        show_messagebox(pos_id, "$PRODUCT_DOES_NOT_EXISTS", "$ERROR")
        raise
