import json
import logging

from helper import remove_accents
from mw_helper import (
    show_keyboard,
    show_message_dialog,
    show_tab_customer_dialog,
)
from sysactions import (
    action,
    show_messagebox,
)
from api.table import get_table_service
from application.interactor import (
    StartServiceInteractor,
    TabsInteractor,
)
from application.interactor.service.impl import TableReleaserImpl
from custom_sysactions import (
    block_action_if_pos_is_blocked,
    user_must_be_logged,
    user_authorized_to_execute_action,
)
from msgbusboundary import MsgBusTableService as tableService
from msgbusboundary.ui import MsgBusScreenChanger
from table_manager_api.dto.exception import InvalidTabMask
from typing import (
    Dict,
    Optional,
    Union,
)
from utilfunctions import is_valid_string

logger = logging.getLogger("TableActions")


@action
def show_open_tabs(
    pos_id,     # type: int
):
    # type: (...) -> bool

    tabs_interactor = TabsInteractor(int(pos_id))
    opened_tabs = tabs_interactor.get_opened_tabs()
    if not opened_tabs:
        show_messagebox(pos_id, "$EMPTY_OPENED_TABS")
        return False

    tabs_interactor.show_opened_tabs(opened_tabs)

    return True


@action
@block_action_if_pos_is_blocked
@user_must_be_logged
@user_authorized_to_execute_action
def create_tab(
    pos_id,         # type: Union[str, int]
    tab_info=None,  # type: Optional[Dict[str, Union[str, int]]]
):

    return open_tab(
        pos_id=pos_id,
        tab_info=tab_info,
    )


@action
def open_tab(
    pos_id,                 # type: Union[str, int]
    parent_table_id=None,   # type: Optional[str]
    tab_info=None,          # type: Optional[Dict[str, Union[str, int]]]
):
    # type: (...) -> Union[str, bool]

    from table_actions import (
        allowed_to_create_table,
        create_table_modified_event,
    )
    from select_tab_by_barcode import (
        get_tab_by_name,
        get_pos_function,
    )

    if parent_table_id is None and not allowed_to_create_table(
        pos_id=pos_id,
        table_id="",
        is_tab=True,
    ):
        show_message_dialog(
            pos_id=pos_id,
            message="$OPERATOR_DOES_NOT_BELONG_TO_TABLE_SECTOR",
        )
        return False

    if not tab_info:
        tab_info = get_tab_info(pos_id=pos_id)
    if not tab_info:
        return False

    tab_name = tab_info.pop("TAB_NAME", None)
    if not tab_name:
        return False

    tab_service_dto, stop_process = get_tab_by_name(
        pos_id=pos_id,
        name=tab_name,
        pos_function=get_pos_function(),
        table_service=get_table_service(pos_id=pos_id),
    )
    if stop_process:
        return False

    if tab_service_dto:
        show_messagebox(
            posid=pos_id,
            message="$TAB_ALREADY_EXISTS",
        )
        logger.warning("Tab {} already exists".format(tab_name))
        return False

    table_releaser = TableReleaserImpl(table_service=tableService())
    screen_changer = MsgBusScreenChanger()

    interactor = StartServiceInteractor(
        screen_changer=screen_changer,
        table_service=tableService(),
        table_releaser=table_releaser,
    )
    parent_table_id = parent_table_id or ""
    tab_picture = None
    try:
        tab_picture = interactor.execute(
            pos_id=pos_id,
            name=tab_name,
            source_table_id=parent_table_id,
            custom_properties=tab_info,
        )
    except InvalidTabMask:
        show_messagebox(
            posid=pos_id,
            message="$TAB_NOT_FOUND",
        )
    except (Exception, ):
        logger.exception("Unmapped error")
        raise

    create_table_modified_event(
        pos_id=pos_id,
        table_picture=tab_picture,
    )

    return tab_picture


def get_tab_info(
    pos_id,     # type: str
):
    # type: (...) -> Optional[Dict[str, str]]

    message = None

    while True:
        if message:
            show_message_dialog(
                pos_id=pos_id,
                message=message,
            )

        customer_data = _get_tab_input(pos_id=pos_id)
        if not customer_data:
            return None

        tab_name = customer_data["TAB_NAME"]
        if tab_name is None:
            return None

        message = _get_invalid_data_message(
            customer_data=customer_data,
            tab_name=tab_name,
        )
        if message:
            continue

        break
    return customer_data


def _get_tab_input(
    pos_id,         # type: str
):
    # type: (...) -> Optional[Dict[str, str]]

    from table_actions import customer_info_tab

    if customer_info_tab:
        customer = show_tab_customer_dialog(pos_id=pos_id)
        if customer is None:
            return None

        customer_data = _format_customer_data(customer=customer)
    else:
        customer_data = {"TAB_NAME": None}
        tab_name = show_keyboard(
            posid=pos_id,
            message="$TYPE_TAB_NAME",
            defvalue="",
            title="",
            numpad=False,
            buttons="$OK|$PREVIOUS",
        )
        if tab_name is not None:
            customer_data["TAB_NAME"] = remove_accents(text=tab_name)

    return customer_data


def _are_all_values_filled(
    obj_dict,   # type: Dict[str, Optional[str]]
):
    # type: (...) -> bool

    return all([False for value in obj_dict.values() if len(value) == 0])


def _get_invalid_data_message(
    customer_data,  # type: Dict[str, str]
    tab_name,       # type: str
):
    # type: (...) -> str

    from table_actions import (
        customer_info_tab,
        max_number_of_tab_digits,
    )

    has_all_values = _are_all_values_filled(obj_dict=customer_data)
    if not has_all_values:
        return "$INVALID_COSTUMER_INFO"

    if not is_valid_string(string=tab_name) or len(tab_name) > max_number_of_tab_digits:
        return "$INVALID_TAB_NAME|{}".format(max_number_of_tab_digits)

    if "customer_phone" in customer_info_tab and len(customer_data["CUSTOMER_PHONE"]) < 10:
        return "$INVALID_CUSTOMER_PHONE"

    if "customer_name" in customer_info_tab and not is_valid_string(string=customer_data["CUSTOMER_NAME"]):
        return "$INVALID_CUSTOMER_NAME"

    return ""


def _format_customer_data(
    customer,   # type: str
):
    # type: (...) -> Dict[str, str]

    from table_actions import customer_info_tab

    customer_data = json.loads(customer)
    if "customer_name" in customer_info_tab:
        customer_data["CUSTOMER_NAME"] = remove_accents(customer_data["CUSTOMER_NAME"])

    customer_data["TAB_NAME"] = remove_accents(customer_data["TABLE_NAME"])

    return customer_data
