# -*- coding: utf-8 -*-
from xml.etree import cElementTree as eTree
from xml.etree.ElementTree import Element

import sysactions
from msgbus import (
    TK_SYS_ACK,
    TK_SLCTRL_OMGR_ORDERPICT,
    FM_PARAM,
)
from pos_model import TableStatus
from typing import (
    Optional,
    Union,
    Any,
)

from application.repository import AccountRepository
from actions.login.pause_user import pause_user
from actions.login.common_lock import release_user_lock
from actions.functions.do_store_order import doStoreOrder
from actions.functions.do_transfer import (
    do_transfer,
    get_justification,
    get_transfer_values,
)
from actions.config import get_close_operator_conference_type
from actions.util import get_operator_info, get_user_level
from actions.void.do_void_order import do_void_order

from model.customexception import (
    CloseOrderByOperatorCloseException,
    NotFoundUserError,
)

from utilfunctions import get_user_id_from_session
from .lock_user_actions import acquire_user_lock_close_operator
from actions.login.get_lock_message import get_lock_message
from .. import logger, mb_context
from .utils import (
    close_user,
)
from actions.functions.dto import ConferenceTypes
from actions.table.store_table_service import store_table_service
from actions.order.order_was_already_saved import order_was_already_saved

USECS_PER_SEC = 1000000


def close_quick_service_order_from_another_pos(
    pos_id,                                 # type: str
    order,                                  # type: eTree.Element
    user_id,                                # type: str
    pos_to_close_user,                      # type: str
    must_store_quick_service_order=False,   # type: Optional[bool]
):
    # type: (...) -> None

    if order.get("state") not in ("IN_PROGRESS", "TOTALED"):
        return

    user_id_from_session = get_user_id_from_session(session_id=order.get("sessionId"))
    if user_id_from_session != user_id:
        return

    if must_store_quick_service_order and order_was_already_saved(order=order):
        doStoreOrder(
            pos_id=pos_to_close_user,
            pos_id_to_show_message=pos_id,
            show_popup=False,
        )
        return

    order_voided = _voided_quick_service_order_after_confirmation(
        pos_id=pos_id,
        pos_to_close_user=pos_to_close_user,
    )
    if not order_voided:
        raise CloseOrderByOperatorCloseException(
            service_type="Quick Service",
            pos_id=pos_id,
        )


def close_table_service_from_another_pos(
    pos_id,                             # type: str
    order,                              # type: eTree.Element
    user_id,                            # type: str
    pos_to_close_user,                  # type: str
):
    # type: (...) -> None

    _void_in_progress_table_order(
        order=order,
        pos_id=pos_id,
        pos_to_close_user=pos_to_close_user,
        user_id=user_id,
    )

    store_table_service(
        pos_id=pos_to_close_user,
    )


def _void_in_progress_table_order(
    order,                              # type: eTree.Element
    pos_id,                             # type: str
    pos_to_close_user,                  # type: str
    user_id,                            # type: str
):
    # type: (...) -> None

    if order.get("state") != "IN_PROGRESS":
        return

    user_id_from_session = get_user_id_from_session(session_id=order.get("sessionId"))
    if user_id_from_session != user_id:
        return

    if not order_was_already_saved(order=order):
        table_id = order.find(".//CustomOrderProperties/OrderProperty[@key='{}']".format("TABLE_ID"))
        order_voided = _voided_table_order_after_confirmation(
            pos_id=pos_id,
            pos_to_close_user=pos_to_close_user,
            order_id=order.get("orderId"),
            table_id=table_id.get("value"),
        )
        if not order_voided:
            raise CloseOrderByOperatorCloseException(
                service_type="Table Service",
                pos_id=pos_id,
            )


def _store_order_in_table_to_close_user(
    pos_id,             # type: str
    pos_to_close_user,  # type: str
    table,              # type: Any
    table_service,      # type: Any
    user_id,            # type: str
):
    # type: (...) -> bool

    if not user_id or table.status not in (TableStatus.TOTALED, TableStatus.IN_PROGRESS):
        return False

    table_picture = eTree.XML(table_service().get_table_picture(
        pos_id=pos_to_close_user,
        table_id=table.id,
    ))

    orders_xml = table_picture.findall("Orders/Order")
    for order_xml in orders_xml:
        if order_xml.get("state") not in ["IN_PROGRESS", "TOTALED"]:
            continue

        user_id_from_session = get_user_id_from_session(session_id=order_xml.get("sessionId"))

        is_same_user = user_id_from_session == user_id
        if is_same_user and table.current_pos_id is not None:
            doStoreOrder(
                pos_id=table.current_pos_id,
                pos_id_to_show_message=pos_id,
                show_popup=False,
            )
            return True

    return False


def _voided_quick_service_order_after_confirmation(
    pos_id,                 # type: str
    pos_to_close_user,      # type: str
    order_id=None,              # type: Optional[str]
):
    # type: (...) -> bool

    confirmation = _get_confirmation_close_user_and_voided_order(pos_id=pos_id)
    if confirmation == 0:
        do_void_order(
            pos_id=pos_to_close_user,
            received_void_reason="CANCELLATION",
            show_cancel_message=False,
            order_id=order_id,
        )
        return True

    return False


def _voided_table_order_after_confirmation(
    pos_id,                 # type: str
    pos_to_close_user,      # type: str
    order_id,               # type: str
    table_id,               # type: str
):
    # type: (...) -> bool

    confirmation = _get_confirmation_close_user_and_voided_order(pos_id=pos_id)
    if confirmation == 0:
        from table_actions import void_table_order_without_void_reason

        return void_table_order_without_void_reason(
            pos_id=pos_to_close_user,
            order_id=order_id,
            table_id=table_id,
        )

    return False


def get_order_picture(
    pos_id,         # type: str
    order_id=None,  # type: str
):
    # type: (Union[str, int], Optional[str]) -> Optional[Element]

    data = "{}\0{}".format(str(pos_id), str(order_id or ''))
    reply = sysactions.send_message(
        dest_name="ORDERMGR{}".format(str(pos_id)),
        token=TK_SLCTRL_OMGR_ORDERPICT,
        format=FM_PARAM,
        data=data,
    )

    if reply.token == TK_SYS_ACK:
        try:
            split_reply = reply.data.split("\0")
            if not split_reply[2]:
                return None
            order_picture = eTree.XML(split_reply[2])
            if order_id:
                order_picture = order_picture.findall('.//Order')[0]
            return order_picture
        except AttributeError:
            return None

    raise Exception("Error getting OrderPicture: {} - {}".format(reply.token, reply.data))


def common_close_user(
    pos_id,                     # type: str
    model,                      # type: eTree.XML
    user_id,                    # type: str
    authorizer=None,            # type: Optional[str]
    pos_to_close_user=None,     # type: Optional[str]
):
    # type: (...) -> bool

    if not acquire_user_lock_close_operator(user_id=user_id):
        sysactions.show_messagebox(
            posid=pos_id,
            message=get_lock_message(user_id=user_id),
            icon="warning",
        )
        raise sysactions.StopAction()

    try:
        return _inner_common_close_user(
            pos_id=pos_id,
            model=model,
            user_id=user_id,
            authorizer=authorizer,
            pos_to_close_user=pos_to_close_user,
        )
    finally:
        release_user_lock(user_id=user_id)


def _inner_common_close_user(
    pos_id,                     # type: str
    model,                      # type: eTree.XML
    user_id,                    # type: str
    authorizer=None,            # type: Optional[str]
    pos_to_close_user=None,     # type: Optional[str]
):
    # type: (...) -> bool

    from table_actions import considers_tef_on_bordereau

    if pos_to_close_user is None:
        pos_to_close_user = pos_id

    is_logged_in, session = get_operator_info(
        model=model,
        operator_id=user_id,
    )
    if not session:
        sysactions.show_messagebox(pos_id, "$USER_NOT_OPENED", "$ERROR")
        raise NotFoundUserError()

    pod_function = sysactions.get_posfunction(model=model)
    current_order = sysactions.get_current_order(model=model)

    pod_type = sysactions.get_cfg(pos_id).key_value("pod") or "FL"
    is_quick_service = pod_type in ["FC", "FL"]

    if current_order is not None:
        table_id = current_order.find("CustomOrderProperties/OrderProperty/[@key='TABLE_ID']")

        try:
            order = sysactions.get_current_order(model=model)

            if table_id is None:
                close_quick_service_order_from_another_pos(
                    pos_id=pos_id,
                    order=order,
                    user_id=user_id,
                    pos_to_close_user=pos_to_close_user,
                )
            else:
                close_table_service_from_another_pos(
                    pos_id=pos_id,
                    order=order,
                    user_id=user_id,
                    pos_to_close_user=pos_to_close_user,
                )
        except (CloseOrderByOperatorCloseException, Exception) as ex:
            logger.info("Operation not complete because : {}".format(ex.message))
            raise sysactions.StopAction()

        if is_logged_in:
            pause_user(
                pos_id=str(pos_to_close_user),
                pos_id_to_show_message=pos_id,
            )

    period = sysactions.get_business_period(model=model)

    transfer_values = {}
    if pod_function != "OT":
        transfer_values = get_transfer_values(
            pos_id=pos_id,
            period=period,
            session=session,
        )

    report_name = "close_operator_report"

    bordereau_enabled = False
    if pod_function != "OT":
        bordereau_enabled = get_close_operator_conference_type() == ConferenceTypes.BORDEREAU.value
        response = do_transfer(
            pos_id=pos_id,
            transfer_type=6,
            operator_id=user_id,
            authorizer=authorizer,
            pos_to_close_user=pos_to_close_user,
            transfer_values=transfer_values,
        )
        set_authorizer(pos_id, authorizer, is_quick_service)

        if response['need_ask_justification']:
            sysactions.print_report(
                pos_id,
                None,
                True,
                report_name,
                pos_to_close_user,
                period,
                user_id,
                session,
                authorizer,
                bordereau_enabled,
                considers_tef_on_bordereau,
            )

            account_repository = AccountRepository(mb_context=mb_context)
            get_justification(
                account_repository=account_repository,
                pos_id=pos_id,
                session=session,
            )

    if close_user(
        pos_id=pos_id,
        user_id=user_id,
        pos_to_close_user=pos_to_close_user,
    ):
        set_authorizer(
            pos_id=pos_id,
            authorizer=authorizer,
            is_quick_service=is_quick_service
        )
        logger.debug("Logoff POS %d - Operador %s deslogado com sucesso" % (int(pos_to_close_user), user_id))
    else:
        logger.error("Logoff POS %d - Falha ao deslogar operador %s" % (int(pos_to_close_user), user_id))
        sysactions.show_messagebox(pos_id, "$OPERATION_FAILED", "$ERROR")
        raise sysactions.StopAction()

    logger.debug("Logoff POS %d - Deslogando operador %s" % (int(pos_id), user_id))

    sysactions.print_report(
        pos_id,
        None,
        True,
        report_name,
        pos_to_close_user,
        period,
        user_id,
        session,
        authorizer,
        bordereau_enabled,
        considers_tef_on_bordereau,
    )

    return True


def set_authorizer(pos_id, authorizer, is_quick_service):
    if authorizer is not None and is_quick_service is False:
        sysactions.set_custom(pos_id, 'Last Manager ID', authorizer, True)
        sysactions.set_custom(pos_id, 'Authorization Level', get_user_level(authorizer), True)


def _get_confirmation_close_user_and_voided_order(
    pos_id,     # type: str
):
    # type: (...) -> Union[None, int]

    return sysactions.show_messagebox(
        posid=pos_id,
        message="$CLOSE_USER_VOID_ORDER",
        timeout=720000,
        buttons="$OK|$CANCEL",
    )
