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

import msgbus
import sysactions

from actions.models import UserLevels
from bustoken import TK_FPR_OK, TK_FPR_IDENTIFY_USER
from model.customexception import GettingUserInfoException, CanceledDialogException
from msgbus import MBMessage
from systools import sys_log_info, sys_log_warning
from typing import Dict, Optional
from utilfunctions import get_device_service

from .. import mb_context


def get_user_info(
        pos_id,         # type: str
        user_id=None,   # type: Optional[str]
        password=None,  # type: Optional[str]
        model=None      # type: eTree.XML
):
    # type: (...) -> Dict

    if model is None:
        model = sysactions.get_model(pos_id)

    user_got_from_finger_print = False
    if user_id is None:
        user_id = _get_username_from_finger_print_reader(pos_id)
        if user_id is not None:
            user_got_from_finger_print = True

    if user_id is None:
        user_id = _get_user_id_from_ui(pos_id, model)

    if not user_got_from_finger_print:
        if password is None:
            password = _get_user_password_from_ui(pos_id, model, user_id)

        sysactions.authenticate_user(user_id, password)

    user_xml_str = _get_user_xml(user_id)
    user_info = _build_user_info_from_user_xml(user_id, password, user_xml_str)
    return user_info


def _get_username_from_finger_print_reader(
        pos_id  # type: str
):
    # type: (...) -> Optional[str]

    try:
        if _is_finger_print_device_available(pos_id):
            finger_print_data = _get_finger_print_data(pos_id)
            if _was_finger_print_successfully_acquired(finger_print_data) or not _was_user_acquired(finger_print_data):
                sysactions.show_messagebox(pos_id, "$ERROR_READING_FINGERPRINT", "$ERROR")
                raise GettingUserInfoException()
            else:
                return finger_print_data.data

    except msgbus.MBException as ex:
        if ex.errorcode == 2:  # NOT_FOUND, FingerPrint not Available
            sys_log_info("FingerPrint Service Not Available")
        else:
            sys_log_warning("Error get_user_information - %s" % str(ex))

    return None


def _is_finger_print_device_available(
        pos_id  # type: str
):
    # type: (...) -> bool

    finger_print = get_device_service(
        pos_id=pos_id,
        device_type="fingerReader",
    )
    if not finger_print:
        return False

    ret = mb_context.MB_EasySendMessage("FingerPrintReader{0}".format(pos_id), TK_FPR_OK)
    return ret.token == msgbus.TK_SYS_ACK


def _get_user_id_from_ui(
        pos_id,     # type: str
        model       # type: eTree.XML
):
    # type: (...) -> int

    level_name = sysactions.translate_message(model, UserLevels.OPERATOR.name)
    display_title = "$USER_AUTHENTICATION_WITH_PARAMETER|{}".format(level_name)
    username = sysactions.show_keyboard(pos_id, "$ENTER_USER_ID", title=display_title, mask="INTEGER", numpad=True)
    if username in (None, ""):
        raise CanceledDialogException()

    return username


def _get_user_password_from_ui(
        pos_id,     # type: str
        model,      # type: eTree.XML
        user_id     # type: str
):
    # type: (...) -> int

    level_name = sysactions.translate_message(model, UserLevels.OPERATOR.name)
    display_title = "$USER_AUTHENTICATION_WITH_PARAMETER|{}".format(level_name)
    message = "$ENTER_PASSWORD|{}".format(user_id)
    password = sysactions.show_keyboard(pos_id, message, title=display_title, mask="PASSWORD", numpad=True)
    if password in (None, ""):
        raise CanceledDialogException()

    return password


def _get_user_xml(
        user_id     # type: str
):
    # type: (...) -> str

    try:
        user_xml_str = sysactions.get_user_information(user_id)
    except Exception as _:
        raise GettingUserInfoException()

    if user_xml_str is None:
        raise GettingUserInfoException()

    return user_xml_str


def _build_user_info_from_user_xml(
        user_id,        # type: str
        password,       # type: str
        user_xml_str    # type: str
):
    # type: (...) -> Dict

    user_xml = eTree.XML(user_xml_str)
    user_element = user_xml.find("user")
    return {
        'Level': user_element.attrib["Level"],
        'UserName': user_element.attrib["UserName"],
        'LongName': user_element.attrib["LongName"],
        'UserId': user_id,
        'Password': password
    }


def _get_finger_print_data(
        pos_id  # type: str
):
    # type: (...) -> MBMessage

    dest_name = "FingerPrintReader{0}".format(pos_id)
    return mb_context.MB_EasySendMessage(dest_name, TK_FPR_IDENTIFY_USER, format=msgbus.FM_PARAM, data=pos_id)


def _was_finger_print_successfully_acquired(
        finger_print_data   # type: MBMessage
):
    # type: (...) -> bool

    return finger_print_data.token != msgbus.TK_SYS_ACK


def _was_user_acquired(
        finger_print_data   # type: MBMessage
):
    # type: (...) -> bool

    return finger_print_data.data != ""
