from logging import Logger

import iso8601
from production.model import (
    ProductionOrder,
    StateEvent,
    Item,
)
from typing import (
    List,
    Optional,
    Set,
)
from .._OrderChangerProductionBox import OrderChangerProductionBox


class StoredOrderSplitter(OrderChangerProductionBox):

    def __init__(
        self,           # type: StoredOrderSplitter
        name,           # type: str
        sons,           # type: object
        logger=None,    # type: Optional[Logger]
    ):
        # type: (...) -> None

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

    def change_order(
        self,       # type: StoredOrderSplitter
        order,      # type: ProductionOrder
    ):
        # type: (...) -> List[ProductionOrder]

        visited_items = set()
        orders = self._get_splitted_stored_orders(
            order=order,
            visited_items=visited_items,
        )

        self._add_not_visited_items_to_new_order(
            order=order,
            cloned_orders=orders,
            visited_items=visited_items,
        )

        return orders

    def _get_splitted_stored_orders(
        self,             # type: StoredOrderSplitter
        order,            # type: ProductionOrder
        visited_items,    # type: Set[Item]
    ):
        # type: (...) -> List[ProductionOrder]

        orders = []
        order_index = 0
        states_stored = self._get_stored_states(order=order)
        for state in states_stored:
            order_clone = self._clone_order_to_split(
                order=order,
                order_index=order_index,
                new_display_time=state.timestamp,
            )
            state_datetime = iso8601.parse_date(state.timestamp_gmt)
            for item in order.items:
                created_at = iso8601.parse_date(item.created_at)
                if created_at <= state_datetime and item not in visited_items:
                    order_clone.items.append(item)
                    visited_items.add(item)
                    if order_index > 0:
                        item.add_tag(tag="item-new-added")

            if len(order_clone.items) > 0:
                orders.append(order_clone)
                order_index += 1

        return orders

    @staticmethod
    def _clone_order_to_split(
        order,               # type: ProductionOrder
        order_index,         # type: int
        new_display_time,    # type: str
    ):
        # type: (...) -> ProductionOrder

        order_clone = order.clone(
            items=[],
            index=order_index,
        )
        order_clone.display_time = iso8601.parse_date(new_display_time).isoformat()
        order_clone.stored_split_order = True

        return order_clone

    @staticmethod
    def _add_not_visited_items_to_new_order(
        order,              # type: ProductionOrder
        cloned_orders,      # type: List[ProductionOrder]
        visited_items,      # type: Set[Item]
    ):
        # type: (...) -> None

        order_clone = order.clone(
            items=[],
            index=len(cloned_orders),
        )
        order_clone.stored_split_order = True
        for item in order.items:
            if item not in visited_items:
                order_clone.items.append(item)

        if len(order_clone.items) > 0:
            cloned_orders.append(order_clone)

    @staticmethod
    def _get_stored_states(
        order,    # type: ProductionOrder
    ):
        # type: (...) -> List[StateEvent]

        stored_states = []
        for state in order.state_history:
            if state.state == "STORED":
                stored_states.append(state)

        return sorted(stored_states, key=lambda item: item.timestamp_gmt)
