import time

from abc import (
    ABCMeta,
    abstractmethod,
)
from logging import Logger
from threading import Thread
from typing import List

from mw_helper import ensure_list

from production.model import (
    ProductionOrder,
    InvalidOrderToPrint,
)
from production.view import ProductionView


class PrinterView(ProductionView):
    __metaclass__ = ABCMeta

    def __init__(
        self,
        name,                   # type: str
        tags_on_print=None,     # type: List[str]
        logger=None,            # type: Logger
    ):
        # type: (...) -> None

        super(PrinterView, self).__init__(name, logger)
        self.tags_on_print = ensure_list(tags_on_print)

    def handle_order(
        self,               # type: PrinterView
        order,              # type: ProductionOrder
        synch=False,        # type: bool
    ):
        # type: (...) -> Thread

        t = Thread(
            target=self.generate_and_print_report,
            args=[order]
        )
        t.daemon = True
        t.start()
        return t

    def generate_and_print_report(
        self,       # type: PrinterView
        order,      # type: ProductionOrder
    ):
        # type: (...) -> None

        report = self.generate_report_with_retry(order)
        self.print_report(report, order)

    def generate_report_with_retry(
        self,       # type: PrinterView
        order,      # type: ProductionOrder
    ):
        # type: (...) -> str

        report = None
        for _ in range(1, 5):
            try:
                report = self.generate_report(order)
                break
            except InvalidOrderToPrint:
                break
            except (Exception,):
                self.exception("Exception trying to generate picklist for Order {}", order.order_id)
                time.sleep(1)

        return report

    @abstractmethod
    def generate_report(
        self,       # type: PrinterView
        order,      # type: ProductionOrder
    ):
        # type: (...) -> str

        raise NotImplementedError()

    @abstractmethod
    def print_report(
        self,       # type: PrinterView
        report,     # type: str
        order,      # type: ProductionOrder
    ):
        # type: (...) -> None

        raise NotImplementedError()

    @abstractmethod
    def clear_printer_cache(
        self,       # type: PrinterView
    ):
        # type: (...) -> None

        raise NotImplementedError()

    def get_view_tags(
        self,       # type: PrinterView
    ):
        # type: (...) -> List[str]

        return self.tags_on_print
