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

import pyscripts
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.common_lock import (
    acquire_user_lock,
    release_user_lock,
)
from actions.util import get_operator_info, get_user_level
from actions.void.do_void_order import do_void_order

from utilfunctions import wait_model_update
from ..functions.do_store_order import doStoreOrder
from ..functions.do_transfer import do_transfer, get_justification
from .. import logger, mb_context
from .utils import (
    close_user,
)

USECS_PER_SEC = 1000000


def close_order_from_another_pos(
    model,                                  # type: eTree.XML
    pos_id,                                 # type: str
    user_id,                                # type: str
    pos_to_close_user,                      # type: str
    is_quick_service,                       # type: bool
    table_service=None,                     # type: Any
    show_confirmation_message=True,         # type: Optional[bool]
    must_store_quick_servie_order=False,    # type: Optional[bool]
):
    # type: (...) -> bool

    if sysactions.has_current_order(model=model):
        order = sysactions.get_current_order(model=model)
        if is_quick_service:
            can_close_order = _can_close_quick_service_order(
                order=order,
                pos_id=pos_id,
                pos_to_close_user=pos_to_close_user,
                show_confirmation_message=show_confirmation_message,
                must_store_quick_servie_order=must_store_quick_servie_order,
            )
            if not can_close_order:
                return False
        else:
            can_close_order = _can_close_table_service_order(
                order=order,
                pos_id=pos_id,
                user_id=user_id,
                pos_to_close_user=pos_to_close_user,
                table_service=table_service,
                show_confirmation_message=show_confirmation_message,
            )
            if not can_close_order:
                return False

    _send_table_modified_event(
        pos_to_close_user=pos_to_close_user,
    )

    return True


def _can_close_quick_service_order(
    order,                                  # type: eTree.XML
    pos_id,                                 # type: str
    pos_to_close_user,                      # type: str
    show_confirmation_message=True,         # type: Optional[bool]
    must_store_quick_servie_order=False,    # type: Optional[bool]
):
    # type: (...) -> bool

    if order.get("state") in ("IN_PROGRESS", "TOTALED"):
        if must_store_quick_servie_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 True

        cancel_operation = _show_message_to_void_order(
            pos_id=pos_id,
            pos_to_close_user=pos_to_close_user,
            show_confirmation_message=show_confirmation_message,
        )
        if cancel_operation:
            return False

    return True


def _can_close_table_service_order(
    order,                                              # type: eTree.XML
    pos_id,                                             # type: str
    user_id,                                            # type: str
    pos_to_close_user,                                  # type: str
    table_service=None,                                 # type: Any
    show_confirmation_message=True,                     # type: Optional[bool]
):
    # type: (...) -> bool

    if order.get("state") == "IN_PROGRESS" and not order_was_already_saved(order=order):
        cancel_operation = _show_message_to_void_order(
            pos_id=pos_id,
            pos_to_close_user=pos_to_close_user,
            show_confirmation_message=show_confirmation_message,
        )
        if cancel_operation:
            return False
    else:
        if table_service is not None:
            all_tables = table_service().list_tables(pos_id=pos_to_close_user)
            for table in all_tables:
                _validate_if_table_has_order_opened(
                    pos_id=pos_id,
                    pos_to_close_user=pos_to_close_user,
                    table=table,
                    table_service=table_service,
                    user_id=user_id,
                )

    return True


def _send_table_modified_event(
    pos_to_close_user,  # type: str
):
    # type: (...) -> None

    create_table_modified_event(
        pos_id=pos_to_close_user,
        table_picture='0',
    )

    wait_model_update(
        pos_id=pos_to_close_user,
        model_update_validation=sysactions.has_current_order,
    )


def order_was_already_saved(
    order,  # type: eTree.XML
):
    # type: (...) -> bool

    state_history = order.findall(".//StateHistory/State")
    for state in state_history:
        if state.get("state") == "STORED":
            return True

    return False


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

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

        is_same_user = table_picture.get("userId") == user_id
        valid_status = table.status in (TableStatus.TOTALED, TableStatus.IN_PROGRESS)
        if is_same_user and valid_status 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,
            )


def _show_message_to_void_order(
    pos_id,                     # type: str
    pos_to_close_user,          # type: str
    show_confirmation_message,  # type: bool
):
    # type: (...) -> bool

    confirmation = 0
    if show_confirmation_message:
        confirmation = sysactions.show_messagebox(
            posid=pos_id,
            message="$CLOSE_USER_VOID_ORDER",
            timeout=720000,
            buttons="$OK|$CANCEL",
        )

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

    return True


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 create_table_modified_event(
    pos_id,         # type: str
    table_picture,  # type: str
):
    # type: (...) -> None

    event_xml = """<Event subject="POS{}" type="TABLE_MODIFIED">
                           <TABLE_MODIFIED>
                           {}
                       </TABLE_MODIFIED>
                   </Event>""".format(pos_id, table_picture)
    mbcontext = pyscripts.mbcontext
    mbcontext.MB_EasyEvtSend(
        subject="POS{}".format(int(pos_id)),
        type="TableSelected",
        xml=event_xml,
    )


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]
    table_service=None,         # type: Optional[Any]
):
    # type: (...) -> bool

    if not acquire_user_lock(user_id=user_id):
        sysactions.show_messagebox(
            posid=pos_id,
            message="$OPERATOR_IS_PERFORMING_AN_OPEN_OR_CLOSE_ACTION",
            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,
            table_service=table_service,
        )
    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]
    table_service=None,         # type: Optional[Any]
):
    # type: (...) -> bool

    from table_actions import considers_tef_on_bordereau

    if pos_to_close_user is None:
        pos_to_close_user = pos_id

    _, _, session = get_operator_info(
        model=model,
        operator_id=user_id,
    )
    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']")
        is_quick_service = table_id is None

        if not close_order_from_another_pos(
            model=model,
            pos_id=pos_id,
            user_id=user_id,
            pos_to_close_user=pos_to_close_user,
            is_quick_service=is_quick_service,
            table_service=table_service,
        ):
            return False

    period = sysactions.get_business_period(model=model)

    from table_actions import bordereau_enabled

    report_name = "close_operator_report"

    if pod_function != "OT":
        response = do_transfer(
            pos_id=pos_id,
            transfer_type=6,
            operator_id=user_id,
            authorizator=authorizer,
            pos_to_close_user=pos_to_close_user,
        )
        done_transfer = response['done_transfer']
        need_ask_justification = response['need_ask_justification']
        set_authorizer(pos_id, authorizer, is_quick_service)
        if not done_transfer:
            return False

        if 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,
            )

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

    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")
        return False

    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)
        sysactions.set_custom(pos_id, 'Authorization Level', get_user_level(authorizer))
