# -*- coding: utf-8 -*-
import json
import datetime
import logging
import os
import time

import cfgtools
from functools import wraps
from typing import (
    List,
    Optional,
    Union
)
from xml.etree import cElementTree as eTree

import persistence
import sysactions
from typing import (
    Any,
    Optional
)

import reportactions

from actions.coupon.display_coupons_from_report import display_coupons_from_report
from actions.login.utils import get_user_info
from actions.models import TransferType
from old_helper import config_logger, convert_from_utf_to_localtime
from msgbus import TK_SYS_ACK, TK_PRN_PRINT, FM_PARAM, MBException
from sysactions import get_model, show_keyboard, is_valid_date, show_info_message, action, \
    get_user_information, generate_report, show_listbox, show_messagebox, print_text, sys_log_info

from actions.util import get_opened_pos_user
from actions.util.get_menu_user_logged import get_menu_manager_authenticated_user
from systools import sys_log_exception

from utilfunctions import get_device_service
from xml.etree import cElementTree as eTree

mbcontext = None
poslist = []
store_id = None
config_logger(os.environ["LOADERCFG"], 'ManagerReports')
report_logger = logging.getLogger("ManagerReports")
config_logger(os.environ["LOADERCFG"], 'ManagerReportsData')
report_data_logger = logging.getLogger("ManagerReportsData")

invalid_date_i18n = "$INVALID_DATE"
type_report_i18n = "$TYPE_REPORT_POS"
type_operator_id_i18n = "$TYPE_OPERATOR_ID"
invalid_user_id_i18n = "$INVALID_USER_ID"
invalid_date_future_i18n = "$INVALID_DATE_FUTURE"
invalid_date_max_period_i18n = "$INVALID_DATE_MAX_PERIOD"
invalid_date_range_i18n = "$INVALID_DATE_RANGE"


def report_action(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        try:
            report_logger.info('Requested report: {}'.format(f.func_name))
        except Exception as _: # noqa
            pass

        report = f(*args, **kwargs)
        if report:
            report_data_logger.info(
                "\n".join((
                    '\n',
                    '********************************************************************',
                    'Requested report: {}'.format(f.func_name),
                    'Args: {} | Kwargs: {}'.format(str(args), str(kwargs)),
                    str(report),
                    '********************************************************************'
                ))
            )
        return report

    return action(decorated_function)


def print_report(pos_id, model, preview, report_name, *report_params):
    report = generate_report(report_name, *report_params)
    if report:
        current_printer = get_device_service(
            pos_id=pos_id,
            device_type="printer",
        )
        print_text(
            posid=pos_id,
            model=model,
            text=report,
            preview=preview,
            force_printer=current_printer,
        )
        return report
    else:
        sys_log_info("Could not generate report: %s" % report_name)
    return None


@report_action
def speedOfServiceReport(pos_id, store_wide="false", *args):
    model = get_model(pos_id)

    period = show_keyboard(pos_id, "$TYPE_DATE", title="", mask="DATE", numpad=True)
    if period is None:
        return

    if not is_valid_date(period):
        show_messagebox(pos_id, invalid_date_i18n)
        return

    if is_date_old(period):
        show_messagebox(pos_id, "$PERIOD_GREATER_THAN_THIRTY_DAYS")
        return

    pos = show_keyboard(pos_id, type_report_i18n, title="", defvalue="0", mask="INTEGER", numpad=True)
    if pos in (None, "", "."):
        return

    return print_report(pos_id, model, True, "speedOfService", pos_id, period, pos, store_id)


@report_action
def hourlySalesReport(
    pos_id,                 # type: str
    ask_pos="ask",          # type: Optional[str]
    ask_operator="ask",     # type: Optional[str]
    operator_id=None,       # type: Optional[str]
    releases=False,         # type: Optional[bool]
):
    # type: (...) -> Any

    from msgbusboundary import MsgBusTableService as tableService

    model = get_model(pos_id)
    period = show_keyboard(pos_id, "$TYPE_DATE", title="", mask="DATE", numpad=True)
    if period is None:
        return
    if not is_valid_date(period):
        show_messagebox(pos_id, invalid_date_i18n)
        return

    if is_valid_period(period, pos_id) is False:
        return None

    if is_date_old(period):
        show_messagebox(pos_id, "$PERIOD_GREATER_THAN_THIRTY_DAYS")
        return

    pos_list = []
    if ask_pos == "ask":
        pos_list = show_keyboard(pos_id, type_report_i18n, title="", defvalue="0", mask="INTEGER", numpad=True)
        if pos_list in (None, "", "."):
            return

    pos_list = tableService().get_exclusive_pos_list() if int(pos_list) == 0 else [int(pos_list)]
    if ask_operator == "ask":
        operator_id = show_keyboard(pos_id, type_operator_id_i18n, title="", defvalue="0", mask="INTEGER", numpad=True)
        if operator_id in (None, "", "."):
            return

        if operator_id == "0":
            operator_id = ""
        else:
            if not get_user_information(operator_id):
                show_info_message(pos_id, invalid_user_id_i18n, msgtype="warning")
                return

            operator_id = int(operator_id)

    return print_report(
        pos_id,
        model,
        True,
        "hourlySales",
        pos_id,
        json.dumps(pos_list),
        str(operator_id),
        period,
        releases
    )


def _get_report_parameters(
    pos_id,                             # type: Union[str, int]
    model,                              # type: eTree.XML
    ask_operator,                       # type: str
    date_type,                          # type: str
    ask_pos,                            # type: Union[str, int]
    operator_id=None,                   # type: Optional[Union[str, int]]
    ask_date=True,                      # type: Optional[bool]
    ignore_operator_not_found=False,    # type: Optional[bool]
):
    # type: (...) -> List

    if date_type == "SessionId":
        operator = model.find("Operator")
        if operator is None or operator.get("state") != "LOGGEDIN":
            show_messagebox(pos_id, "$USER_NOT_OPENED")
            raise sysactions.StopAction()

        session_id = model.find("Operator").get("sessionId")
        initial_date = None
        end_date = None
        operator_id = None
        report_pos = 0

    else:
        session_id = None
        initial_date = get_report_date(pos_id, "$TYPE_INITIAL_DATE") if ask_date else None
        if ask_date and initial_date is None:
            raise sysactions.StopAction()

        end_date = get_report_date(pos_id, "$TYPE_FINAL_DATE") if ask_date else None
        if ask_date and end_date is None:
            raise sysactions.StopAction()

        if initial_date and end_date and is_valid_date_range(initial_date, end_date, pos_id) is False:
            raise sysactions.StopAction()

        if ask_pos == "ask" or type(ask_pos) == int:
            if type(ask_pos) == int:
                report_pos = str(ask_pos)
            else:
                report_pos = show_keyboard(pos_id, type_report_i18n, title="", defvalue="0", mask="INTEGER",
                                           numpad=True)
                if report_pos in (None, "", "."):
                    raise sysactions.StopAction()
            report_pos = int(0 if report_pos in (None, 'None') else report_pos)
            if report_pos not in poslist and report_pos not in (0,):
                show_messagebox(pos_id, "$POS_NOT_FOUND")
                raise sysactions.StopAction()
            report_pos = str(report_pos)
        elif ask_pos == "current":
            report_pos = str(pos_id)
        else:
            report_pos = 0

        if ask_operator == "ask":
            operator_id = show_keyboard(
                posid=pos_id,
                message=type_operator_id_i18n,
                title="",
                defvalue="0",
                mask="INTEGER",
                numpad=True,
            )
            if operator_id in (None, "", "."):
                raise sysactions.StopAction()
            elif (not ignore_operator_not_found
                    and not get_user_information(user_number=operator_id)):
                show_messagebox(
                    posid=pos_id,
                    message=invalid_user_id_i18n,
                )
                raise sysactions.StopAction()

        elif ask_operator == "current":
            try:
                current_operator_id = model.find('Operator').get('id')
                operator_id = current_operator_id
            except Exception as _:
                show_messagebox(
                    posid=pos_id,
                    message="$USER_NOT_OPENED",
                )
                raise sysactions.StopAction()

    return [date_type, pos_id, report_pos, initial_date, end_date, operator_id, session_id]


@report_action
def productMixReportByPeriod(
    pos_id,                         # type: str
    date_type="BusinessPeriod",     # type: Optional[str]
    ask_pos="ask",                  # type: Optional[str]
    ask_operator="ask",             # type: Optional[str]
    operator_id=False,              # type: Optional[str]
):
    # type: (...) -> bool

    model = get_model(pos_id)

    parameters = _get_report_parameters(
        pos_id=pos_id,
        model=model,
        ask_operator=ask_operator,
        date_type=date_type,
        ask_pos=ask_pos,
        operator_id=operator_id,
    )
    parameters.extend([store_id, get_pmix_report_model(pos_id)])

    return print_report(pos_id, model, True, "new_pmix_report", *parameters)


@report_action
def tipReport(
    pos_id,                 # type: str
    ask_operator="ask",     # type: Optional[str]
    operator_id=None,       # type: Optional[str]
):
    # type: (...) -> bool

    model = get_model(pos_id)

    parameters = _get_report_parameters(
        pos_id=pos_id,
        model=model,
        ask_operator=ask_operator,
        date_type="BusinessPeriod",
        ask_pos="notAsk",
        operator_id=operator_id,
    )
    parameters.extend([store_id])

    return print_report(pos_id, model, True, "tip_report", *parameters)


@report_action
def cashReport(
    pos_id,                         # type: str
    ask_operator="ask",             # type: Optional[str]
    date_type="BusinessPeriod",     # type: Optional[str]
    ask_pos="ask",                  # type: Optional[str]
    is_table_service=False,         # type: Optional[bool]
    operator_id=None,               # type: Optional[str]
    only_body=False,                # type: Optional[bool]
    only_payments=False,            # type: Optional[bool]
    is_integrated_delivery=False,   # type: Optional[bool]
):
    # type: (...) -> bool

    model = get_model(pos_id)

    parameters = _get_report_parameters(
        pos_id=pos_id,
        model=model,
        ask_operator=ask_operator,
        date_type=date_type,
        ask_pos=ask_pos,
        operator_id=operator_id,
    )
    parameters.extend([store_id, None, None, None, bool(is_table_service), only_body, only_payments, False,
                       is_integrated_delivery])
    return print_report(pos_id, model, True, "new_cash_report", *parameters)


@report_action
def sales_summary_report(
    pos_id,                         # type: str
    ask_operator="ask",             # type: Optional[str]
    date_type="BusinessPeriod",     # type: Optional[str]
    ask_pos="ask",                  # type: Optional[str]
    operator_id=None,               # type: Optional[str]
):
    # type: (...) -> bool

    model = get_model(pos_id)

    parameters = _get_report_parameters(
        pos_id=pos_id,
        model=model,
        ask_operator=ask_operator,
        date_type=date_type,
        ask_pos=ask_pos,
        operator_id=operator_id,
    )
    report_parameters = [pos_id, parameters[3], parameters[4], store_id]
    return print_report(pos_id, model, True, "sales_summary_report", *report_parameters)


@report_action
def tef_cancellation_report(
    pos_id,                         # type: str
    ask_operator="ask",             # type: Optional[str]
    date_type="BusinessPeriod",     # type: Optional[str]
    ask_pos="ask",                  # type: Optional[str]
    operator_id=None,               # type: Optional[str]
):
    # type: (...) -> None

    model = get_model(pos_id)
    parameters = _get_report_parameters(
        pos_id=pos_id,
        model=model,
        ask_operator=ask_operator,
        date_type=date_type,
        ask_pos=ask_pos,
        operator_id=operator_id,
    )
    report_parameters = [
        parameters[3],
        parameters[4]
    ]
    reportactions.print_report(
        pos_id,
        "tefcancellationreport",
        True,
        None,
        pos_id,
        *report_parameters
    )


@report_action
def benefit_report(
    pos_id,                         # type: str
    ask_operator="ask",             # type: Optional[str]
    date_type="BusinessPeriod",     # type: Optional[str]
    ask_pos="ask",                  # type: Optional[str]
    operator_id=None,               # type: Optional[str]
):
    # type: (...) -> None

    from msgbusboundary import MsgBusTableService as tableService

    model = get_model(pos_id)
    parameters = _get_report_parameters(
        pos_id=pos_id,
        model=model,
        ask_operator=ask_operator,
        date_type=date_type,
        ask_pos=ask_pos,
        operator_id=operator_id,
        ignore_operator_not_found=True,
    )
    initial_date = parameters[3]
    end_date = parameters[4]
    operator_id = parameters[5] if parameters[5] != '0' else None
    benefit_selected = json.dumps(display_coupons_from_report(pos_id, initial_date, end_date, operator_id))
    pos_list = json.dumps(tableService().get_exclusive_pos_list())
    parameters.extend([benefit_selected, store_id, None, None, None, pos_list])
    reportactions.print_report(
        pos_id,
        "benefitsreport",
        True,
        None,
        pos_id,
        *parameters
    )

@report_action
def voidedOrdersReport(
    pos_id,                         # type: str
    ask_operator="ask",             # type: Optional[str]
    date_type="BusinessPeriod",     # type: Optional[str]
    ask_pos="ask",                  # type: Optional[str]
    operator_id=None,               # type: Optional[str]
):
    # type: (...) -> bool

    model = get_model(pos_id)

    parameters = _get_report_parameters(
        pos_id=pos_id,
        model=model,
        ask_operator=ask_operator,
        date_type=date_type,
        ask_pos=ask_pos,
        operator_id=operator_id,
    )
    parameters.extend([store_id])

    return print_report(pos_id, model, True, "voided_orders_report", *parameters)


def get_report_date(pos_id, message):
    # type (str, str) -> str
    date = show_keyboard(pos_id, message, title="", mask="DATE", numpad=True)

    if date is None:
        return None

    if not is_valid_date(date):
        show_messagebox(pos_id, invalid_date_i18n)
        raise sysactions.StopAction()

    if is_valid_period(date, pos_id) is False:
        raise sysactions.StopAction()

    return date


def is_valid_period(date, pos_id):
    # type (str) -> bool
    chosen_date = datetime.datetime.strptime(date, '%Y%m%d').date()
    days_from_today = datetime.date.today() - datetime.timedelta(days=90)

    if chosen_date < days_from_today:
        show_messagebox(pos_id, invalid_date_max_period_i18n)
        return False

    if chosen_date > datetime.date.today():
        show_messagebox(pos_id, invalid_date_future_i18n)
        return False


def is_valid_date_range(initial_date, end_date, pos_id):
    # type (str, str) -> bool
    initial_date = datetime.datetime.strptime(initial_date, '%Y%m%d').date()
    end_date = datetime.datetime.strptime(end_date, '%Y%m%d').date()

    if end_date < initial_date:
        show_messagebox(pos_id, invalid_date_range_i18n)
        return False


def get_pmix_report_model(pos_id):
    ret = show_messagebox(pos_id, "$CHOOSE_PMIX_PRODUCT_TYPE", title="", buttons="$ALL_PRODUCTS|$PRICED|$UNPRICED")

    if ret == 1:
        return "PRICED"
    elif ret == 2:
        return "UNPRICED"
    else:
        return "ALL_PRODUCTS"


@report_action
def checkoutReport(posid, *args):
    def get_option(session):
        close_time = datetime.datetime.strptime(session['closeTime'], "%Y-%m-%d %H:%M:%S").strftime("%H:%M:%S")
        return "%s - %s" % (close_time, session['operatorName'])

    def get_operator_sessions(operatorid, period, posid):
        sessions = []
        options = []
        session_cols = ('openTime', 'closeTime', 'sessionId', 'operatorName')

        conn = None
        try:
            conn = persistence.Driver().open(mbcontext, dbname=str(posid))
            conn.transaction_start()
            cursor = conn.pselect("drawerChangeReport", startPeriod=period, endPeriod=period, instance=posid)
            for row in cursor:
                if row.get_entry("closeTime") and (
                        int(row.get_entry("operatorId")) == int(operatorid) or int(operatorid) == 0):
                    sessions.append(dict([(col, row.get_entry(col)) for col in session_cols]))
                    options.append(get_option(sessions[-1]))

            conn.transaction_end()
        finally:
            if conn:
                conn.close()
        return sessions, options

    model = get_model(posid)
    period = show_keyboard(posid, "$ENTER_DATE_OR_PRESS_OK", title="", mask="DATE", numpad=True)
    if period is None:
        return
    if not is_valid_date(period):
        show_info_message(posid, invalid_date_i18n, msgtype="error")
        return

    operatorid = show_keyboard(posid, type_operator_id_i18n, title="", defvalue="0", mask="INTEGER", numpad=True)
    if operatorid in (None, "", "."):
        return
    if not get_user_information(operatorid):
        show_info_message(posid, invalid_user_id_i18n, msgtype="warning")
        return

    sessions, options = get_operator_sessions(operatorid, period, posid)
    if sessions:
        index = show_listbox(posid, options)
        if index is None:
            return  # The user cancelled - or timed-out

        session_id = sessions[index]['sessionId']
        menu_manager_user = get_menu_manager_authenticated_user(int(posid))
        return print_report(posid, model, True, "cash", posid, period, operatorid, "False", "0", "Check Out Report",
                            session_id, menu_manager_user)
    else:
        show_messagebox(posid, message="$THERE_NO_SHIFT_OPERATOR")


def is_date_old(period, *args):
    consult_days = abs((datetime.datetime.strptime(period, "%Y%m%d") - datetime.datetime.now()).days)

    return consult_days > 30


def is_current_date(period, *args):
    return datetime.datetime.strptime(period, "%Y%m%d").date() == datetime.date.today()


@report_action
def transfers_report(pos_id, *args):
    model = get_model(pos_id)
    options = [sysactions.translate_message(model, "CASH_IN"), sysactions.translate_message(model, "CASH_OUT")]
    transfer_type = show_listbox(pos_id, options, message="$SELECT_OPTION")
    if transfer_type is None:
        return
    transfer_type = int(TransferType.TRANSFER_CASH_IN) if transfer_type == 0 else int(TransferType.TRANSFER_CASH_OUT)

    period = show_keyboard(pos_id, "$ENTER_DATE_OR_PRESS_OK", title="", mask="DATE",
                           numpad=True)
    if period is None:
        return
    if not is_valid_date(period):
        show_messagebox(pos_id, message=invalid_date_i18n, icon="error")
        return

    if is_valid_period(period, pos_id) is False:
        return

    report = generate_report("transfer_report_by_period", pos_id, "", transfer_type, period)
    reports = json.loads(report)
    data = []
    index = 0
    for report in reports:
        report_id = index
        index += 1
        for key, value in report.items():
            data.append((report_id, key, value))

    if not data:
        show_messagebox(pos_id, message="$HAS_NO_ITEMS_TO_SHOW", icon="error")
        return

    transfer_title = '$SELECT_OPTION'

    if transfer_type == 3:
        transfer_title = '$SELECT_SUPPLY'

    elif transfer_type == 4:
        transfer_title = '$SELECT_BLEED'

    selected_transfer = sysactions.show_text_preview(
        posid=pos_id,
        texts=data,
        title=transfer_title,
        buttons='$CANCEL|$PRINT',
        defvalue='',
        onthefly=False,
        timeout=120000,
    )

    if selected_transfer is None:
        return

    if int(selected_transfer[0]) == 0:
        try:
            message = None
            index = 0
            for msg in data:
                try:
                    if index == int(selected_transfer[1]):
                        message = str(msg[2])
                    index += 1
                except:
                    continue

            if message is not None:
                printer = None
                try:
                    printer = get_device_service(
                        pos_id=pos_id,
                        device_type="printer",
                    )
                    msg = mbcontext.MB_EasySendMessage(printer, TK_PRN_PRINT, FM_PARAM, message)
                except MBException as _:
                    time.sleep(0.5)
                    msg = mbcontext.MB_EasySendMessage(printer, TK_PRN_PRINT, FM_PARAM, message)
                finally:
                    if msg.token == TK_SYS_ACK:
                        show_info_message(pos_id, "$OPERATION_SUCCEEDED", msgtype="success")
                    else:
                        show_info_message(pos_id, "$OPERATION_FAILED", msgtype="error")
        except Exception as _:
            show_info_message(pos_id, "$ERROR_PRINT_REPORT", msgtype="error")


@report_action
def opened_tables_report(pos_id):
    model = get_model(pos_id)
    return print_report(pos_id, model, True, "openedtablesreport", pos_id)


@report_action
def doPrintLogoffReport(pos_id):
    from table_actions import considers_tef_on_bordereau

    model = get_model(pos_id)
    sessions = []

    period = show_keyboard(pos_id, "$TYPE_DATE", title="", mask="DATE", numpad=True)

    if period in (None, ''):
        return

    conn = None
    try:
        date_input_format = "%Y-%m-%d %H:%M:%S"
        date_output_format = "%d/%m/%Y %H:%M:%S"

        conn = persistence.Driver().open(mbcontext, dbname=str(pos_id))
        conn.transaction_start()

        sql = """SELECT * FROM posctrl.UserSession
                  WHERE BusinessPeriod = '%s'
                  ORDER BY CloseTime DESC
                  """ % period
        cursor = conn.select(sql)

        for row in cursor:
            session_id = row.get_entry("SessionId")
            pos = session_id.split(",")[0].split("=")[1]
            close_time = row.get_entry("CloseTime")
            if close_time is not None:
                open_formatted_time = datetime.datetime.strptime(row.get_entry("OpenTime"), date_input_format)
                open_time = convert_from_utf_to_localtime(open_formatted_time).strftime(date_output_format)

                close_formatted_time = datetime.datetime.strptime(close_time, date_input_format)
                close_time = convert_from_utf_to_localtime(close_formatted_time).strftime(date_output_format)

                operator_name = row.get_entry("OperatorName")
                description = operator_name + "  " + open_time
                msg = "Loja: %s" \
                      "\nPOS: %s" \
                      "\nNome do operador: %s" \
                      "\nHora de entrada: %s" \
                      "\nHora de saída: %s" % (store_id, pos, operator_name, open_time, close_time)
                sessions.append((session_id, description, msg))

    except Exception as e:
        sys_log_exception('Exception printing logoff report: {}'.format(e))
        show_info_message(pos_id, "$OPERATION_FAILED", msgtype="error")
        return
    finally:
        if conn:
            conn.close()

    if not sessions:
        show_messagebox(pos_id, "$THERE_ARE_NO_CLOSED_SESSIONS", title="$ERROR", icon="error", buttons="$OK")
        return

    selected = sysactions.show_text_preview(
        posid=pos_id,
        texts=sessions,
        title='$SELECT_AN_OPTION',
        buttons='$CANCEL|$PRINT',
        defvalue='',
        onthefly=False,
        timeout=120000
    )

    if selected is not None and selected[0] == '1':
        try:
            period = selected[4][7:15]
            userid = selected[2][5:]
            session_id = selected[1] + "," + selected[2] + "," + selected[3] + "," + selected[4]
            select_pos_id = selected[1][4:]

            from table_actions import bordereau_enabled
            report_name = "close_operator_report"
            return print_report(
                pos_id,
                model,
                True,
                report_name,
                select_pos_id,
                period,
                userid,
                session_id,
                "",
                bordereau_enabled,
                considers_tef_on_bordereau,
            )
        except Exception as e:
            show_info_message(pos_id, "$ERROR_TRYING_TO_REPRINT_REPORT", msgtype="error")


@report_action
def get_voided_lines_report(pos_id):
    start_date = get_report_date(pos_id, "$TYPE_INITIAL_DATE")
    if start_date is None:
        raise sysactions.StopAction()
    end_date = get_report_date(pos_id, "$TYPE_FINAL_DATE")
    if end_date is None:
        raise sysactions.StopAction()

    if is_valid_date_range(start_date, end_date, pos_id) is False:
        return

    report_pos = sysactions.show_keyboard(pos_id, "$TYPE_REPORT_POS", "", defvalue="0", mask="INTEGER", numpad=True)
    if report_pos is None:
        return

    operator_id = sysactions.show_keyboard(pos_id, "$TYPE_OPERATOR_ID", "", defvalue="0", mask="INTEGER", numpad=True)
    if operator_id is None:
        return

    model = sysactions.get_model(pos_id)

    report_name = "generate_voided_lines_report"
    return print_report(pos_id, model, True, report_name, report_pos, start_date, end_date, False, operator_id)


def sale_challenge_report(
    pos_id,                 # type: Any[Union[str, int]]
    sale_challenge_id,      # type: int
    ask_operator="ask",     # type: Optional[str]
    is_manager=False,       # type: Optional[bool]
    operator_id=None,       # type: Optional[str]
):
    # type: (...) -> Optional[bool]

    model = get_model(pos_id)

    ask_operator = ask_operator if is_manager else "current"
    operator_id = _get_report_parameters(
        pos_id=pos_id,
        model=model,
        ask_operator=ask_operator,
        date_type="",
        ask_pos="",
        operator_id=operator_id,
        ask_date=False,
    )[5]
    return reportactions.print_report(
        pos_id,
        "sale_challenge_report",
        True,
        None,
        pos_id,
        operator_id,
        sale_challenge_id
    )


@report_action
def show_justification_discount_report(
    pos_id,                         # type: str
    ask_operator="ask",             # type: str
    operator_id=None,               # type: Optional[str]
    date_type="BusinessPeriod",     # type: Optional[str]
    ask_pos="current",              # type: Optional[str]
):
    # type: (...) -> None

    model = get_model(
        posid=pos_id,
    )

    parameters = _get_report_parameters(
        pos_id=pos_id,
        model=model,
        ask_operator=ask_operator,
        date_type=date_type,
        ask_pos=ask_pos,
        operator_id=operator_id,
    )

    initial_date = parameters[3]
    end_date = parameters[4]
    operator_id = parameters[5] if parameters[5] != '0' else None

    parameters.extend([initial_date, end_date, operator_id])
    reportactions.print_report(
        pos_id,
        "justificationdiscountreport",
        True,
        None,
        pos_id,
        *parameters
    )


def justification_report(
    pos_id,                                 # type: str
    justification,                          # type: str
    discount_description=None,              # type: Optional[str]
    justification_type="APPLY_DISCOUNT",    # type: Optional[str]
    authorized_by=None,                     # type: Optional[str]
    order_id=None,                          # type: Optional[str]
):
    # type: (...) -> None

    if justification in ['', None]:
        return

    if not _has_config_print_justification(
        justification_type=justification_type,
    ):
        return

    model = get_model(pos_id)
    operator = model.find("Operator")
    operator_id = operator.get("id") if operator is not None else None

    authorized_by = _get_user_authorization(
        operator_id=operator_id,
        authorized_by=authorized_by,
    )

    if order_id is None:
        order = sysactions.get_current_order(
            model=model,
        )
        order_id = order.get("orderId")

    data = {
        "orderId": order_id,
        "operator": operator_id,
        "AuthorizedBy": authorized_by,
        "justificationType": justification_type,
        "discountUserDescription": discount_description,
        "justification": justification,
    }

    data = json.dumps(
        obj=data,
    )

    reportactions.print_report(
        pos_id,
        "justificationreport",
        False,
        None,
        data,
    )


def _get_user_authorization(
    operator_id,           # type: str
    authorized_by=None,    # type: Optional[str]
):
    if authorized_by is not None:
        return authorized_by

    user_xml_str = get_user_information(
        user_number=operator_id,
    )

    if user_xml_str is None:
        return operator_id

    user_element = eTree.XML(
        text=user_xml_str
    ).find("user")

    return user_element.attrib["UserName"]


def _has_config_print_justification(
    justification_type="APPLY_DISCOUNT",    # type: str
):
    # type: (...) -> bool

    config = cfgtools.read(
        file_or_path=os.environ["LOADERCFG"],
    )

    if justification_type == "APPLY_DISCOUNT":
        return str(config.find_value(
            path="Customizations.PrintApplyDiscountJustifyCoupon"
        ) or 'false'.lower()) == "true"

    order_state_cancel_print = config.find_values(path="Customizations.PrintCancelJustifyCoupon")

    if order_state_cancel_print is None:
        return False

    return justification_type in order_state_cancel_print

