import logging

from table_manager_api.dto.exception import (
    TableError,
    TabNameAlreadyExists, InvalidTabName,
)
from typing import (
    Any,
    List,
    Optional,
    Tuple,
    Union,
)
from xml.etree import cElementTree as eTree

from mw_helper import (
    show_message_dialog,
    show_message_options_dialog,
)
from pos_model import (
    TableStatus,
    TableOperationMode,
)
from sysactions import (
    StopAction,
    action,
    show_confirmation,
    get_model,
)

from api.table import get_table_service
from actions.config import get_tab_enabled
from actions.table.create_tab import get_tab_info
from actions.table.unlink_tabs import unlink_multiple_tabs
from application.domain import Table

from msgbusboundary import MsgBusTableService as tableService
from tablemgrapi import (
    TableServiceException,
    get_pos_ts,
)

logger = logging.getLogger("TableActions")


@action
def deselect_table(
    pos_id,                     # type: Union[str, int]
    pre_selected_option=False,  # type: bool
    do_raise=True               # type: bool
):
    # type: (...) -> bool

    try:
        from table_actions import (
            has_table_service,
            create_table_modified_event,
        )

        if not has_table_service():
            return True

        tables = list(tableService().list_tables(pos_id=pos_id))
        selected_table = tableService().get_selected_table(
            pos_id=pos_id,
            tables=tables,
        )
        if not selected_table:
            create_table_modified_event(
                pos_id=pos_id,
                table_picture="0",
            )
            return True

        table_picture = eTree.XML(tableService().get_table_picture(
            pos_id=pos_id,
            table_id=selected_table.id,
        ))
        is_a_tab = tableService().is_a_tab(
            pos_id=pos_id,
            table_id=selected_table.id,
            table_picture=table_picture,
        )

        if is_a_tab:
            table_picture, tables = confirm_operation_on_mode_tabs_with_tabs(
                pos_id=pos_id,
                table_picture=table_picture,
                tables=tables,
            )

        try:
            if is_a_tab and not pre_selected_option:
                finish_action, element = _deselect_tab_action(
                    pos_id=pos_id,
                    table_picture=table_picture,
                    do_raise=do_raise,
                )
                if finish_action:
                    return element

            check_if_tab_name_is_required(
                pos_id=pos_id,
                table_id=table_picture.get("id"),
                table_picture=table_picture,
            )

        except TableServiceException:
            logger.exception("Error getting table picture")

        try:
            for table in tables:
                if pos_id == table.current_pos_id:
                    tableService().store_service(
                        pos_id=pos_id,
                        table_id=str(table.id),
                    )
        except TableServiceException:
            show_message_dialog(
                pos_id=pos_id,
                message="$COULD_NOT_DESELECT_TABLE",
            )
            logger.exception("Error getting table picture")

            return False

        create_table_modified_event(
            pos_id=pos_id,
            table_picture="0",
        )

        return True

    except StopAction:
        return False


def check_can_return_to_original_table(
    pos_id,                 # type: str
    slice_source_id,        # type: str
    current_table_picture,  # type: eTree.Element
):
    # type: (...) -> bool

    if not slice_source_id:
        return False

    source_table_picture = eTree.XML(tableService().get_table_picture(
        pos_id=pos_id,
        table_id=slice_source_id,
    ))
    status = int(source_table_picture.get("status"))
    if status in [TableStatus.CLOSED, TableStatus.ABANDONED]:
        return False

    if status == TableStatus.AVAILABLE:
        return True

    return _check_if_source_table_still_has_the_same_service(
        current_table_picture=current_table_picture,
        source_table_picture=source_table_picture,
    )


def _check_if_source_table_still_has_the_same_service(
    current_table_picture,  # type: eTree.Element
    source_table_picture,   # type: eTree.Element
):
    # type: (...) -> bool

    return current_table_picture.get("sourceServiceId") == source_table_picture.get("serviceId")


def check_if_tab_name_is_required(
    pos_id,                 # type: str
    table_id,               # type: str
    table_picture=None,     # type: Optional[eTree.XML]
):
    # type: (...) -> bool

    if not table_picture:
        table_picture = eTree.fromstring(tableService().get_table_info(
            pos_id=pos_id,
            table_id=table_id,
        ))

    table_description = table_picture.get("typeDescr")

    if table_description == "Tab":
        tab_name = table_picture.get("name")
        if tab_name:
            return False

        customer_data = get_tab_info(pos_id=pos_id)
        if not customer_data:
            raise StopAction()

        table_picture, changed_user = check_tab_user_sector(
            table_picture=table_picture,
            pos_id=pos_id,
        )
        if table_picture is None:
            raise StopAction()

        tab_name = customer_data.pop("TAB_NAME", None)
        if not tab_name:
            raise StopAction()

        try:
            get_table_service(pos_id=pos_id).create_tab_from_split_service(
                table_id=table_id,
                table_name=tab_name,
                properties=customer_data,
            )
        except TableError as ex:
            message = "$CREATE_TAB_FROM_SPLIT_SERVICE_ERROR"
            if isinstance(ex, TabNameAlreadyExists):
                message = "$TAB_ALREADY_EXISTS"
            elif isinstance(ex, InvalidTabName):
                message = "$EXISTS_AS_A_TABLE"

            show_message_dialog(
                pos_id=pos_id,
                message=message,
            )
            message = "Error trying to set info tab_name: {}, properties: {} into table_id: {}"
            logger.exception(message.format(tab_name, customer_data, table_id))
            raise StopAction()

        return changed_user


def check_tab_user_sector(
    table_picture,  # type: Union[str, eTree.XML]
    pos_id,         # type: str
    reopen=False,   # type: bool
):
    # type: (...) -> (Tuple[Union[str, eTree.XML, None]], bool)

    from table_actions import table_operation_mode

    sector_mode = table_operation_mode in [TableOperationMode.SHARED_SECTOR, TableOperationMode.INDIVIDUAL_SECTOR]
    if not sector_mode:
        return table_picture, False

    model = get_model(posid=pos_id)
    pos_ts = get_pos_ts(model=model)
    table_picture_xml = eTree.XML(table_picture) if isinstance(table_picture, str) else table_picture
    table_user_id = int(table_picture_xml.get("userId"))
    table_id = table_picture_xml.get("id")

    if reopen:
        changed_user = check_if_tab_name_is_required(
            pos_id=pos_id,
            table_id=table_id,
        )
    else:

        from table_actions import show_list_operators

        new_operator = show_list_operators(
            pos_id=pos_id,
            sector="$TABS",
            table_user=table_user_id,
        )
        if new_operator[0] < 0:
            return None, False

        new_operator = new_operator[1][new_operator[0]]

        pos_ts.changeTableUser(
            user_id=new_operator,
            table_id=table_id,
        )

        changed_user = True if new_operator != table_user_id and sector_mode else False

    table_picture = tableService().get_table_picture(
        pos_id=pos_id,
        table_id=table_id,
    )

    return table_picture, changed_user


def _deselect_tab_action(
    pos_id,             # type: str
    table_picture,      # type: eTree.XML
    do_raise,           # type: bool
):
    # type: (...) -> Tuple[bool, Any]

    slice_source_id = table_picture.get("sliceSourceTableId")
    tab_name = table_picture.get("name")
    can_return_to_original_table = check_can_return_to_original_table(
        pos_id=pos_id,
        slice_source_id=slice_source_id,
        current_table_picture=table_picture,
    )

    if slice_source_id and not tab_name:
        option_back_from_source_table = 0
        if get_tab_enabled():
            options = "$BACK_FROM_SOURCE_TABLE|$DESELECT_TABLE|$CANCEL"
            option_cancel = 2
        else:
            options = "$BACK_FROM_SOURCE_TABLE|$CANCEL"
            option_cancel = 1

        ret = show_message_options_dialog(
            pos_id=pos_id,
            buttons=options,
            message="$CHOOSE_DESELECT_TYPE",
        )
        if ret == option_back_from_source_table:

            from table_actions import do_transfer_table

            return True, do_transfer_table(
                pos_id=pos_id,
                parent_table_id=table_picture.get("id"),
                destination_table_id=slice_source_id if can_return_to_original_table else None,
                select_destination_table=False,
                ignore_totaled_orders=True,
            )

        if ret == option_cancel:
            if not do_raise:
                return True, False

            raise StopAction()

    return False, False


def confirm_operation_on_mode_tabs_with_tabs(
    pos_id,         # type: str
    table_picture,  # type: eTree.Element
    tables=None,    # type: Optional[List[Table]]
):
    # type: (...) -> Tuple[eTree.Element, List[Table]]

    services_children = table_picture.findall(".//ServiceChildren/ServiceChild")
    if len(services_children) == 0:
        return table_picture, tables

    from table_actions import check_if_table_have_tenders
    has_tenders = check_if_table_have_tenders(
        pos_id=pos_id,
        table_picture=table_picture,
        message="$TABLE_HAS_TENDERS",
    )
    if has_tenders:
        raise StopAction()

    confirm = True
    if len(services_children) > 1:
        confirm = show_confirmation(
            posid=pos_id,
            message="$TO_PROCEED_THE_LINKED_TABS_MUST_BE_SEPARATED",
        )

    if not confirm:
        raise StopAction()

    to_unlink = []
    for child in services_children:
        to_unlink.append(child.get("tableId"))

    table_picture_dto = unlink_multiple_tabs(
        pos_id=pos_id,
        current_tab_id=table_picture.get("id"),
        tabs_to_unlink=to_unlink,
    )

    if table_picture_dto is None:
        raise StopAction()

    tables = list(tableService().list_tables(pos_id=pos_id))
    return table_picture_dto.to_xml(), tables
