import re

from production.model import TagEvent
from typing import List
from collections import OrderedDict

from _OrderChangerProductionBox import OrderChangerProductionBox

JIT_SEPARATOR = '[><$]'


class SequencerTagItemBox(OrderChangerProductionBox):
    def __init__(self, name, sons, logger=None):
        # type: (str, Optional[Union[List[ProductionBox]]], Logger) -> None # noqa

        super(SequencerTagItemBox, self).__init__(name, sons, logger)

    def change_order(self, order):
        for item in order.items:
            self._reprocess_tags(item)

        return order

    def _reprocess_tags(self, item):
        for son in item.items:
            self._reprocess_tags(son)

        if item.displayed_jit:
            self._verify_previous_jit_sequences(item)
            self._process_displayed_jit_tags(item)
            self._verify_next_jit_sequences(item)
            return

        self._create_generic_tags(item)

    def _process_displayed_jit_tags(self, item):
        for tag in item.tags:
            for item_jit in item.displayed_jit:
                if tag.endswith("-{}".format(item_jit.lower())):
                    new_tag = tag.replace("-{}".format(item_jit.lower()), "")
                    self._create_generic_tag_info(item, new_tag, tag)

    def _verify_next_jit_sequences(self, item):
        for item_jit in item.displayed_jit:
            sequence_groups = item.jit_lines.split("<")
            jit_index = self._get_sequence_index(item_jit, sequence_groups) + 1

            while len(sequence_groups[jit_index:]) > 0:
                next_jit_sequences = sequence_groups[jit_index:]
                next_jit_sequences = re.split('[>$]', "+".join(next_jit_sequences))[0]
                next_jit_sequences = re.findall('\\w+', next_jit_sequences)
                tag_doing = self._check_any_tag_in_item(item, next_jit_sequences, ["doing", "done", "served"])
                if tag_doing is not None:
                    self._create_generic_tag_info(item, "doing", tag_doing)
                    self._create_generic_tag_info(item, "doing-{}".format(item_jit.lower()), tag_doing)

                tag_done = self._check_any_tag_in_item(item, next_jit_sequences, ["done", "served"])
                if tag_done is not None:
                    self._create_generic_tag_info(item, "done", tag_done)
                    self._create_generic_tag_info(item, "done-{}".format(item_jit.lower()), tag_done)

                tag_served = self._check_any_tag_in_item(item, next_jit_sequences, ["served"])
                if tag_served is not None:
                    self._create_generic_tag_info(item, "served", tag_served)
                    self._create_generic_tag_info(item, "served-{}".format(item_jit.lower()), tag_served)

                jit_index += 1

    def _verify_previous_jit_sequences(self, item):
        for item_jit in item.displayed_jit:
            done_previous_jit_sequence = self._get_done_previous_jit_sequence(item, item_jit)
            doing_previous_jit_sequence = self._get_doing_previous_jit_sequence(item, item_jit)

            if doing_previous_jit_sequence:
                tag_doing = self._check_any_tag_in_item(item, doing_previous_jit_sequence, ["doing", "done", "served"])
                if tag_doing is not None:
                    self._create_generic_tag_info(item, "doing", tag_doing)

            if done_previous_jit_sequence:
                block_command_separator = item.jit_lines.find('${}'.format(item_jit))
                if block_command_separator >= 0:
                    item.block_tags = True

                tag_done = self._check_all_tag_in_item(item, done_previous_jit_sequence, ["done", "served"])
                if tag_done is not None:
                    self._create_generic_tag_info(item, "done", tag_done)
                    item.block_tags = False

                tag_served = self._check_all_tag_in_item(item, done_previous_jit_sequence, ["served"])
                if tag_served is not None:
                    self._create_generic_tag_info(item, "served", tag_served)

        if item.is_combo():
            some_child_has_block_tags = any([x.block_tags for x in item.items])
            item.block_tags |= some_child_has_block_tags

    def _get_done_previous_jit_sequence(self, item, item_jit):
        if not item.jit_lines:
            return None

        sequence_groups = item.jit_lines.split("<")
        jit_index = self._get_sequence_index(item_jit, sequence_groups)
        if jit_index >= len(sequence_groups):
            return None

        jit_sequences = sequence_groups[jit_index]
        jit_sequences = re.split(JIT_SEPARATOR, jit_sequences)
        sub_jit_index = self._get_sequence_index(item_jit, jit_sequences)
        if sub_jit_index == 0:
            if jit_index == 0:
                return

            previous_jit_string = sequence_groups[jit_index - 1]
            return re.findall('\\w+', previous_jit_string)

        previous_jit_string = "+".join(jit_sequences[:sub_jit_index])
        return re.findall('\\w+', previous_jit_string)

    def _get_doing_previous_jit_sequence(self, item, item_jit):
        if not item.jit_lines:
            return None

        sequence_groups = re.split(JIT_SEPARATOR, item.jit_lines)
        jit_index = self._get_sequence_index(item_jit, sequence_groups)
        if jit_index >= len(sequence_groups):
            return None

        previous_jit_sequences = sequence_groups[:jit_index]
        if not previous_jit_sequences:
            return

        previous_jit_string = re.split(JIT_SEPARATOR, "+".join(previous_jit_sequences))[-1]
        return re.findall('\\w+', previous_jit_string)

    @staticmethod
    def _get_sequence_index(item_jit, sequence_groups):
        jit_index = 0
        for jit_elem in sequence_groups:
            if item_jit in re.findall('\\w+', jit_elem):
                return jit_index

            jit_index += 1

        return jit_index

    def _create_generic_tags(self, item):
        self._insert_dont_need_cook_necessary_tag(item)
        self._insert_generic_doing_tag(item)
        self._insert_full_done_served_tag(item)

    def _insert_generic_doing_tag(self, item):
        necessary_jit_lines = re.findall('\\w+', item.jit_lines)
        tag_doing = self._check_any_tag_in_item(item, necessary_jit_lines, ["doing", "done", "served"])
        if tag_doing is not None:
            self._create_generic_tag_info(item, "doing", tag_doing)

    def _insert_full_done_served_tag(self, item):
        sequence_groups = re.split(JIT_SEPARATOR, item.jit_lines)
        last_sequence_group = sequence_groups[-1]
        self._insert_item_tag(item, last_sequence_group, ["done", "served"], "done")
        self._insert_item_tag(item, last_sequence_group, ["served"], "served")

    def _insert_dont_need_cook_necessary_tag(self, item):
        if not item.has_tag("dont-need-cook"):
            return

        sequence_groups = re.split(JIT_SEPARATOR, item.jit_lines)
        last_sequence_group = sequence_groups[0]
        self._insert_item_tag(item, last_sequence_group, ["done", "served"], "done")
        self._insert_item_tag(item, last_sequence_group, ["served"], "served")

    def _insert_item_tag(self, item, sequence_group, necessary_tags, tag_to_insert):
        necessary_jit_lines = re.findall('\\w+', sequence_group)
        tag = self._check_all_tag_in_item(item, necessary_jit_lines, necessary_tags)
        if tag is not None:
            self._create_generic_tag_info(item, tag_to_insert, tag)

    def _create_generic_tag_info(self, item, tag_type, tag_name):
        new_tags = OrderedDict()
        for k, v in item.tags.iteritems():
            new_tags[k] = v
            if k == tag_name:
                new_tags[tag_type] = tag_type

        item.tags = new_tags
        tag_history_entry_list = [x for x in item.tag_history if x.tag == tag_name]
        if tag_history_entry_list:
            tag_history_entry_point = tag_history_entry_list[0]
            item.tag_history.append(TagEvent(
                tag=tag_type,
                action=tag_history_entry_point.action,
                date=tag_history_entry_point.date,
                hallow=True
            ))

        if item.is_product():
            for son in item.items:
                self._create_generic_tag_info(son, tag_type, tag_name)

    @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

    @staticmethod
    def _check_any_tag_in_item(item, necessary_jit_lines, tag_types):
        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:
                    return tag_name

        return None
