import re

from logging import Logger
from typing import (
    List,
    Optional,
    Union,
)

from production.box import ProductionBox
from production.model import Item
from production.repository import ProductRepository

from ._JitItemFilterBox import JitItemFilterBox


class JitSequencerItemFilterBox(JitItemFilterBox):
    def __init__(
            self,
            name,                   # type: str
            sons,                   # type: Optional[Union[List[ProductionBox]], ProductionBox]
            repository,             # type: ProductRepository
            allowed_jit_lines,      # type: Union[str, List[str]]
            forbidden_jit_lines,    # type: Union[str, List[str]]
            logger=None,            # type: Logger
    ):
        # type: (...) -> None

        super(JitSequencerItemFilterBox, self).__init__(
                name,
                sons,
                repository,
                allowed_jit_lines,
                forbidden_jit_lines,
                logger,
        )

    def change_order(self, order):
        filtered_items = []
        for item in order.items:
            self.handle_item(item, filtered_items)

        new_items = []
        for item in filtered_items:
            self._filter_sequencer_item(item, new_items)

        order.items = new_items
        return order

    def _filter_sequencer_item(self, item, new_items):
        # type: (Item, List[Item]) ->  None

        if self._must_filter_part_code(item):
            return

        if len(item.items) > 0:
            sons = []
            for son in item.items:
                if not son.displayed_jit and item.displayed_jit:
                    son.displayed_jit = item.displayed_jit

                self._filter_sequencer_item(son, sons)

            if item.item_type in ("OPTION",) and not sons:
                return

            item.items = sons

        new_items.append(item)

    def _must_filter_part_code(self, item):
        # type: (Item) -> bool

        part_code_jit = item.jit_lines
        if self.allowed_jit_lines is None or not part_code_jit or item.has_tag("dont-need-cook"):
            return False

        for jit in self.allowed_jit_lines:
            if jit not in part_code_jit:
                continue

            item.displayed_jit.append(jit)
            jit_sequence = [x for x in re.split('[><$]', part_code_jit) if jit in x][0]
            jit_sequence_list = jit_sequence.split("-")
            jit_index = 0
            for jit_elem in jit_sequence_list:
                if jit in jit_elem.split("|"):
                    break

                jit_index += 1

            if jit_index == 0:
                return False

            previous_jit = jit_sequence_list[jit_index-1]
            necessary_jit_lines = re.findall('(\\w+)', previous_jit)
            done_tag = self._check_all_tag_in_item(item, necessary_jit_lines, ["done", "served"])
            return done_tag is None

        return True

    @staticmethod
    def _check_all_tag_in_item(item, necessary_jit_lines, tag_types):
        last_tag_name = None
        for jit in necessary_jit_lines:
            for tag_type in tag_types:
                tag_name = "{}-{}".format(tag_type, jit.lower())
                if tag_name in item.tags:
                    last_tag_name = tag_name
                    break
            else:
                return None

        return last_tag_name
