# -*- coding: utf-8 -*-
import logging

import utils
import actions.util

from threading import Lock
from xml.etree import cElementTree as eTree
from typing import (
    Optional,
    List,
)

from mw_helper import show_message_options_dialog

from actions.login.common_lock import release_user_lock
from actions.login.get_lock_message import get_lock_message
from actions.login.get_user_info import get_user_info
from actions.login.lock_user_actions import acquire_user_lock_close_operator
from actions.login.pause_user import pause_user
from actions.util import get_pos_users
from actions.util.list_users import list_all_opened_users
from model.customexception import (
    GettingUserInfoException,
    CanceledDialogException,
)

from sysactions import (
    action,
    get_model,
    show_messagebox,
    StopAction,
    AuthenticationFailed,
    get_poslist,
    close_asynch_dialog,
    force_send_updated_model_to_ui,
)

from login_user import sign_in_quick_user
from signin_user import sign_in_table_user
from actions.pos import pos_must_be_blocked

current_pos_login_users = {}
login_lock = Lock()
mbcontext = None
logger = logging.getLogger("PosActions")


@action
def common_login(
    pos_id,                     # type: str
    user_id=None,               # type: Optional[str]
):
    # type: (...) -> bool

    global current_pos_login_users

    user_id = str(int(user_id)) if user_id is not None else user_id
    model = get_model(pos_id)
    try:
        user_info = get_user_info(pos_id, user_id, model)
        utils.validate_user_level(pos_id, user_info)
    except CanceledDialogException:
        raise StopAction()
    except (AuthenticationFailed, GettingUserInfoException):
        raise StopAction()

    user_id = user_info.get("UserId")
    with login_lock:
        logged_pos_id = current_pos_login_users[user_id] if user_id in current_pos_login_users else None

    if logged_pos_id is not None:
        show_messagebox(
            posid=pos_id,
            message="$USER_IS_LOGIN_IN_POS|{}".format(logged_pos_id),
            icon="error",
        )
        raise StopAction()

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

    try:
        with login_lock:
            current_pos_login_users[user_id] = pos_id

        can_open_on_blocked_day = _can_open_on_blocked_day(
            pos_id,
            user_id,
        )

        working_mode = model.find("WorkingMode").get("usrCtrlType")

        _login_validations(
            pos_id=pos_id,
            user_id=user_id,
            can_open_on_blocked_day=can_open_on_blocked_day,
            working_mode=working_mode,
        )

        if working_mode == "QSR":
            return sign_in_quick_user(pos_id, user_info)
        else:
            return sign_in_table_user(pos_id, user_info)
    finally:
        with login_lock:
            current_pos_login_users[user_id] = None
            release_user_lock(user_id)


def _can_open_on_blocked_day(
    pos_id,                 # type: str
    user_id,                # type: str
):
    # type: (...) -> bool

    can_open_on_blocked_day = False
    users = get_pos_users(pos_id, [user_id])
    if len(users) > 0:
        current_user = users[0]
        can_open_on_blocked_day = current_user.get("openTime") is not None and current_user.get("closeTime") is None

    return can_open_on_blocked_day


def _login_validations(
    pos_id,                     # type: str
    user_id,                    # type: str
    can_open_on_blocked_day,    # type: bool
    working_mode,               # type: str
):
    if not can_open_on_blocked_day and pos_must_be_blocked(pos_id):
        show_messagebox(pos_id, message="$POS_BLOCKED", icon="error")
        raise StopAction()

    if not utils.check_current_day(pos_id):
        raise StopAction()

    actions.util.set_context(mbcontext)
    if not actions.util.is_user_already_logged(pos_id, user_id):
        return

    if working_mode != "TS":
        raise StopAction()

    if not can_change_user_pos(
        user_id=user_id,
        current_pos_id=pos_id,
    ):
        raise StopAction()


def can_change_user_pos(
    user_id,            # type: str
    current_pos_id,     # type: str
):
    # type: (...) -> bool

    user_info = _get_current_user_info(
        current_pos_id=current_pos_id,
        user_id=user_id,
    )
    if user_info is None:
        return False

    pos_to_pause_user = user_info.get("posId")
    model = get_model(posid=pos_to_pause_user)

    operator_state = _get_operator_state_from_model(
        model=model,
        user_id=user_id,
    )
    _log_message(message="Operator {} is {} in pos {}".format(str(user_id), operator_state, pos_to_pause_user))

    is_table_service = model.find("WorkingMode").get("usrCtrlType") == "TS"
    if not is_table_service:
        return False

    _show_confirm_message_open_pos(
        current_pos_id=current_pos_id,
        pos_to_pause_user=pos_to_pause_user,
    )

    utils.request_operator_confirmation(
        pos_id=current_pos_id,
        pos_to_close_user=pos_to_pause_user,
    )

    return _validate_orders(
        model=model,
        current_pos_id=current_pos_id,
        user_id=user_id,
        pos_to_pause_user=pos_to_pause_user,
    )


def _get_current_user_info(
    current_pos_id,     # type: str
    user_id,            # type: str
):
    # type: (...) -> Optional[eTree.Element]

    for pos_id in sorted(get_poslist()):
        user_info = _get_pos_user_info(
            pos_id_to_list=pos_id,
            current_pos_id=current_pos_id,
            user_id=user_id,
        )

        if user_info is not None:
            return user_info


def _get_pos_user_info(
    pos_id_to_list,     # type: str
    current_pos_id,     # type: str
    user_id,            # type: str
):
    # type: (...) -> Optional[eTree.Element]

    if str(pos_id_to_list) == current_pos_id:
        return None

    opened_users = list_all_opened_users(pos_id=str(pos_id_to_list))
    if len(opened_users) == 0:
        return None

    user_info = _get_user_info(
        current_pos_id=current_pos_id,
        opened_users=opened_users,
        user_id=user_id,
    )

    return user_info


def _get_user_info(
    current_pos_id,     # type: str
    opened_users,       # type: List[eTree.Element]
    user_id,            # type: str
):
    # type: (...) -> Optional[eTree.Element]

    for opened_user in opened_users:
        if _validate_user_parameters(
            current_pos_id=current_pos_id,
            user_id=user_id,
            user=opened_user,
        ):
            return opened_user

    return None


def _validate_user_parameters(
    current_pos_id,     # type: str
    user_id,            # type: str
    user,               # type: eTree.Element
):
    # type: (...) -> bool

    return user.get("id") == user_id and user.get("userControlType") == "TS" and user.get("posId") != current_pos_id


def _get_operator_state_from_model(
    model,      # type: eTree
    user_id,    # type: str
):
    # type: (...) -> Optional[str]

    operator = model.find("Operators/Operator[@id='{user_id}']".format(user_id=user_id))
    return operator.get('state') if operator is not None else None


def _show_confirm_message_open_pos(
    current_pos_id,     # type: str
    pos_to_pause_user,  # type: str
):
    # type: (...) -> None

    response = show_message_options_dialog(
        pos_id=current_pos_id,
        buttons="$OK|$PREVIOUS",
        message="$CONFIRM_OPENING_OPERATOR_LOGGED_IN_ANOTHER_POS|{}".format(pos_to_pause_user),
    )
    if response is None or response == 1:
        raise StopAction()


def _validate_orders(
    model,              # type: eTree.Element
    current_pos_id,     # type: str
    user_id,            # type: str
    pos_to_pause_user,  # type: str
):
    # type: (...) -> bool

    utils.close_current_order(
        model=model,
        current_pos_id=current_pos_id,
        pos_to_close_order=pos_to_pause_user,
        user_id=user_id,
    )

    _log_message(message="Init process pause_user")
    pause_user(
        pos_id=pos_to_pause_user,
        pos_id_to_show_message=current_pos_id,
    )

    close_asynch_dialog(
        posid=pos_to_pause_user,
        dlgid=-1,
    )

    force_send_updated_model_to_ui(pos_to_pause_user)
    _log_message(message="End process pause_user")

    return True


def _log_message(
    message,    # type: str
):
    # type: (...) -> None

    logger.info("[Change Login]: {}".format(message))
