import base64
import logging
import time

from logging import Logger

from messagebus import MessageBus
from typing import List

from nreports_api import NReports
from production.model import ProductionOrder
from production.service import PrinterService

from ._OrderXml import OrderXml
from ._PrinterView import PrinterView

MAX_RETRY = 10
printer_logger = logging.getLogger("ProductionPrinter")


class ReportsPrinterView(PrinterView):
    def __init__(
        self,                   # type: ReportsPrinterView
        name,                   # type: str
        report_name,            # type: str
        printer_name,           # type: str
        pick_list_name,         # type: str
        production_manager,     # type: ProductionManager
        timeout,                # type: int
        message_bus,            # type: MessageBus
        tags_on_print=None,     # type: List[str]
        logger=None,            # type: Logger
    ):
        # type: (...) -> None

        super(ReportsPrinterView, self).__init__(
            name=name,
            tags_on_print=tags_on_print,
            logger=logger,
        )

        self.report_name = report_name
        self.printer_name = printer_name
        self.pick_list_name = pick_list_name
        self.production_manager = production_manager

        self.nreports = NReports(message_bus=message_bus)
        self.printer_service = PrinterService(
            message_bus=message_bus,
            timeout=timeout,
        )

    def get_printer_name(
        self,  # type: ReportsPrinterView
    ):
        # type: (...) -> str

        return self.printer_name

    def generate_report(
        self,       # type: ReportsPrinterView
        order,      # type: ProductionOrder
    ):
        # type: (...) -> str

        production_order_xml = OrderXml().to_xml(order)
        return self.nreports.request_report(
            pos_id=None,
            report_name=self.report_name,
            production_order=base64.b64encode(production_order_xml).decode("utf-8")
        )

    def print_report(
        self,       # type: ReportsPrinterView
        report,     # type: str
        order,      # type: ProductionOrder
    ):
        # type: (...) -> bool

        for attempt in range(1, MAX_RETRY):
            try:
                printer_logger.info("Sending to printer: {} - Attempt: {}".format(self.printer_name, attempt))

                if attempt == 1:
                    printer_logger.info("Printer: {} - \nPrinting:\n {}".format(self.printer_name, report))

                self.printer_service.send_nreport_to_printer(
                    printer_name=self.printer_name,
                    report_data=report,
                )
                return True
            except (Exception,):
                if attempt >= MAX_RETRY - 1:
                    log_message = "Printer {} unavailable - Failed to Print Report"
                    printer_logger.warning(
                        log_message.format(self.printer_name),
                        exc_info=True,
                    )
                    return False
                else:
                    log_message = "Error when trying to print on Printer {} in attempt {}"
                    printer_logger.warning(
                        log_message.format(self.printer_name),
                        attempt,
                        exc_info=True,
                    )

            time.sleep(2 * attempt)

    def save_report(
        self,               # type: PrinterView
        report,             # type: str
        order,              # type: ProductionOrder
        print_status,       # type: bool
    ):
        # type: (...) -> None

        if self.pick_list_name:
            order.jit_printer_name = self.pick_list_name

        self.production_manager.save_pick_list_order(
            order=order,
            receipt=report,
            status=print_status,
        )

    def clear_printer_cache(
        self,       # type: ReportsPrinterView
    ):
        # type: (...) -> None

        return
