# -*- coding: utf-8 -*-

from typing import (
    Tuple,
    List,
    Dict,
)

from xml.etree import cElementTree as eTree
from xml.etree.ElementTree import Element

from msgbus import (
    TK_POS_LISTUSERS,
    FM_PARAM,
    TK_SYS_NAK,
)
from mw_helper import show_message_dialog
from old_helper import BaseRepository
from persistence import Connection
from sysactions import (
    UiOutOfSync,
    send_message,
    get_model,
    get_podtype,
    get_user_information,
    show_messagebox,
    StopAction,
    get_poslist,
)

from userhelper import UserHelper
from .delivery_functions import get_pos_id_of_remote_order
from utilfunctions import get_pos_id_from_session

from .. import (
    USECS_PER_SEC,
    logger,
)

mb_context = None


def list_opened_users(
    pos_id     # type: str
):
    # type: (...) -> List[Element]

    pos_id_format = _format_pos_id(pos_id=pos_id)
    msg = send_message(
        dest_name=pos_id_format,
        token=TK_POS_LISTUSERS,
        format=FM_PARAM,
        data="%s" % pos_id,
        timeout=600 * USECS_PER_SEC,
    )
    if msg.token == TK_SYS_NAK:
        logger.error("Logoff POS %d - Error listing logged operators" % int(pos_id))
        raise Exception("Error listing logged operators")

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

    xml = eTree.XML(msg.data)
    opened_users = [tag for tag in xml.findall("User") if not tag.get("closeTime")]
    working_users = [tag for tag in opened_users if tag.get("userControlType") == working_mode]

    if working_mode == "QSR":
        working_users = [tag for tag in working_users if tag.get("posId") == pos_id]

    return working_users


def list_all_opened_users(
    pos_id,     # type: str
):
    # type: (...) -> List[Element]

    pos_id_format = _format_pos_id(pos_id=pos_id)
    msg = send_message(
        dest_name=pos_id_format,
        token=TK_POS_LISTUSERS,
        format=FM_PARAM,
        data="%s" % pos_id,
        timeout=600 * USECS_PER_SEC,
    )

    if msg.token == TK_SYS_NAK:
        logger.error("Logoff POS %d - Error listing logged operators" % int(pos_id))
        raise Exception("Error listing logged operators")

    xml = eTree.XML(msg.data)
    opened_users = [tag for tag in xml.findall("User") if not tag.get("closeTime")]
    working_users = [tag for tag in opened_users]

    return working_users


def list_ids_opened_users():
    with UserHelper(mb_context) as user_helper:
        users_id = []
        users = user_helper.get_all_logged_users()
        for x in users:
            users_id.append(int(x))

        return users_id


def check_valid_user(pos_id, user_id):
    user_xml = get_user_information(user_id)
    if user_xml is None:
        show_message_dialog(pos_id, message="$INVALID_USER_OR_PASSWORD")
        return False

    user_list = eTree.XML(user_xml).findall("user")
    user_filter = filter(lambda x: x.get("UserId") == str(user_id), user_list)
    if user_filter in [None, [], ""]:
        show_message_dialog(pos_id, message="$INVALID_USER_OR_PASSWORD")
        return False

    return True


def get_opened_users():
    # type: (...) -> List[Tuple[int, Element]]

    is_ts_listed = False
    pos_ids_to_ignore = [get_pos_id_of_remote_order()]
    pos_user_list = []
    for pos_id_to_list in sorted(get_poslist()):
        if pos_id_to_list in pos_ids_to_ignore:
            continue

        opened_users = list_opened_users(pos_id=str(pos_id_to_list))
        if len(opened_users) == 0:
            continue

        opened_users.sort(key=lambda x: x.get("id"))
        model = get_model(posid=pos_id_to_list)
        pod_type = get_podtype(model=model)
        if pod_type == "TT":
            pos_ids_to_ignore.append(pos_id_to_list)
            continue

        working_mode = model.find("WorkingMode").get("usrCtrlType")
        if working_mode == "TS":
            if is_ts_listed:
                continue

            is_ts_listed = True

        for user in opened_users:
            user_pos = int(user.get("posId", pos_id_to_list))
            pos_user_list.append((user_pos, user))

    return pos_user_list


def is_user_already_logged(
    pos_id,           # type: str
    user_id,          # type: str
    try_fix=True,     # type: bool
):
    # type: (...) -> bool

    params = "\0".join(["%s" % pos_id, "true"])

    pos_id_format = _format_pos_id(pos_id=pos_id)
    msg = send_message(
        dest_name=pos_id_format,
        token=TK_POS_LISTUSERS,
        format=FM_PARAM,
        data=params,
        timeout=600 * USECS_PER_SEC,
    )
    if msg.token == TK_SYS_NAK:
        logger.error("Logoff POS %d - Error getting logged users" % int(pos_id))
        return True

    xml = eTree.XML(msg.data)
    opened_users = [tag for tag in xml.findall("User") if not tag.get("closeTime")]
    working_users = [tag for tag in opened_users if tag.get("id") == user_id]

    if len(working_users) > 0:
        model = get_model(pos_id)
        _user_has_same_working_mode(model, opened_users, pos_id, user_id)

        user_info = [x for x in opened_users if x.get("id") == user_id][0]
        operator = [op for op in model.findall("Operators/Operator") if op.get("id") == user_id]

        if _has_opened_user(
            operator=operator,
            user_info=user_info,
            pos_id=pos_id,
        ):
            if operator[0].get("state") != "LOGGEDIN":
                return False

            user_logged_in_current_pos = user_info.get("posId") == str(pos_id)
            if user_logged_in_current_pos:
                logger.info("Send event update model ui. POS {} - USER {}".format(pos_id, user_id))
                raise UiOutOfSync(
                    pos_id=pos_id,
                    event_data=user_id,
                )

        old_login = model.find("PosState").get("period") != user_info.get("sessionId").split(",period=")[-1]
        if try_fix and old_login:
            _fix_opened_users(pos_id, user_id)
            return is_user_already_logged(pos_id, user_id, False)

        _show_already_logged_message(
            pos_id=pos_id,
            user_info=user_info,
        )
        return True

    return False


def _has_opened_user(
    operator,    # type: List[Dict[str]]
    user_info,   # type: Dict[str]
    pos_id,      # type: str
):
    # type: (...) -> bool

    user_is_logged_in_other_pos = _is_logged_in_other_qsr_pos(
        user_info=user_info,
        pos_id=pos_id,
    )
    return len(operator) > 0 and not user_is_logged_in_other_pos


def _format_pos_id(
    pos_id,    # type: str
):
    # type: (...) -> str

    return "POS%d" % int(pos_id)


def _show_already_logged_message(
    pos_id,        # type: str
    user_info,     # type: Dict[str]
):
    # type: (...) -> None

    session_id = user_info.get("sessionId")
    logged_pos_id = get_pos_id_from_session(session_id=session_id) or user_info.get("posId")
    show_message_dialog(
        pos_id=pos_id,
        message="$OPERATOR_ALREADY_LOGGED|{}".format(logged_pos_id),
    )


def _user_has_same_working_mode(model, opened_users, pos_id, user_id):
    user_info = [x for x in opened_users if x.get("id") == user_id]
    if user_info:
        pos_working_mode = model.find("WorkingMode").get("usrCtrlType")
        operator_working_mode = "QSR" if "pos" in user_info[0].get("sessionId") else "TS"
        if pos_working_mode != operator_working_mode:
            show_messagebox(pos_id, "$USER_LOGGED_IN_ANOTHER_POS_TYPE|{}".format(user_info[0].get("posId")))
            raise StopAction()


def _fix_opened_users(pos_id, user_id):
    # type: (str, str) -> None
    def inner_func(conn):
        # type: (Connection) -> None
        query = _fix_opened_users_query.format(user_id)
        conn.query(query)

    repository = BaseRepository(mb_context)
    repository.execute_with_connection(inner_func, pos_id)


def _is_logged_in_other_qsr_pos(
    user_info,    # type: str
    pos_id,       # type: str
):
    # type: (...) -> bool

    user_control_type = user_info.get("userControlType")
    if user_control_type != "QSR":
        return False

    session_id = user_info.get("sessionId")
    logged_pos_id = get_pos_id_from_session(session_id=session_id)
    return "closeTime" not in user_info and pos_id != logged_pos_id


def set_context(context):
    global mb_context
    mb_context = context


_fix_opened_users_query = """
    UPDATE UserCount SET CloseTime = (
        SELECT MAX(OpenTime) FROM UserCount WHERE OperatorId = '{0}'
    ) WHERE OperatorId = '{0}' AND CloseTime is NULL AND OpenTime < (
        SELECT MAX(CloseTime) FROM UserCount WHERE OperatorId = '{0}'
    )
"""
