from typing import List, Optional, Dict
from xml.etree.ElementTree import Element

from application.model.benefit import Benefit
from application.model.order import SaleLine, SaleTypes
from application.repository import NDiscountRepository
from application.service import OrderService


class Restrictions(object):
    def __init__(self, pos_id, order_picture, benefit, repository, order_service):
        # type: (int, Element, Benefit, NDiscountRepository, OrderService) -> None

        self.pos_id = pos_id
        self.order_picture = order_picture
        self.benefit = benefit
        self.repository = repository
        self.order_service = order_service
        self.price_keys = self._get_price_keys_from_benefit(self.pos_id, self.benefit, self.order_picture)
        self.exclude_products = self._get_excluded_products_from_benefit(self.benefit)
        self.valid_sale_types = self._get_sale_types(self.pos_id, self.benefit, self.order_picture)
        self.valid_brands = benefit.conditions.brands

    def _get_sale_types(self, pos_id, benefit, order_picture):
        # type: (int, Benefit, Element) -> Optional[List[str]]

        order = self.order_service.get_order(pos_id, order_picture=order_picture)
        if order.table_id is None:
            return None

        if benefit.conditions.sale_type is None:
            return None

        sale_types = []
        for sale_type in benefit.conditions.sale_type:
            sale_type = SaleTypes.get_name(sale_type)
            if sale_type:
                sale_types.append(sale_type)

        return sale_types

    def _get_price_list_from_service_period(self, service_period_ids):
        # type: (List) -> List

        if service_period_ids is None:
            return []

        price_lists = []
        services_periods = self.repository.get_all_service_periods()
        for current_id in service_period_ids:
            if current_id in services_periods:
                price_lists.append(services_periods[current_id])
        return price_lists

    def _get_price_keys_from_benefit(self, pos_id, benefit, order_picture):
        # type: (int, Benefit, Element) -> Optional[List]

        order = self.order_service.get_order(pos_id, order_picture=order_picture)
        service_period = benefit.conditions.service_period

        if not service_period:
            return None

        price_list = self._get_price_list_from_service_period(service_period)
        if not price_list:
            return []

        all_price_keys = self.repository.get_all_service_periods_price_keys()
        price_keys = []
        for sale_line in order.sale_lines:
            price_key = sale_line.price_key
            if price_key and all_price_keys[price_key] in price_list and price_key not in price_keys:
                price_keys.append(price_key)

        return price_keys

    def _get_excluded_products_from_benefit(self, benefit):
        # type: (Benefit) -> Optional[List]

        all_product_groups = self.get_products_groups()
        products = benefit.exclusions.items if benefit.exclusions.items else []
        groups = benefit.exclusions.groups if benefit.exclusions.groups else []
        for group in groups:
            if group in all_product_groups:
                products += all_product_groups[group]

        if len(products) == 0:
            return None

        return products

    def exists(self):
        # type: () -> bool

        if self.price_keys is not None or self.exclude_products or self.valid_sale_types:
            return True

        return False

    def validate(self, sale_line):
        # type: (SaleLine) -> bool

        if not self._sale_type_is_valid(sale_line):
            return False

        if not self._service_period_is_valid(sale_line):
            return False

        if self.exclude_products:
            sale_line_keys = sale_line.key.split(".")
            if any([item_key for item_key in self.exclude_products if str(item_key) in sale_line_keys]):
                return False

        if not self._brand_is_valid(sale_line):
            return False

        return True

    def get_products_groups(self):
        # type: () -> Dict

        return self.repository.get_all_product_groups()

    def _service_period_is_valid(self, sale_line):
        # type: (SaleLine) -> bool

        if not self.benefit.conditions.service_period:
            return True

        if not sale_line.service_period:
            return False

        if not sale_line.service_period.id or sale_line.service_period.id not in self.benefit.conditions.service_period:
            return False

        return True

    def _sale_type_is_valid(self, sale_line):
        # type: (SaleLine) -> bool

        if not self.valid_sale_types:
            return True

        if not sale_line.sale_type:
            return False

        if sale_line.sale_type not in self.valid_sale_types:
            return False

        return True

    def _brand_is_valid(self, sale_line):
        # type: (SaleLine) -> bool

        if not self.valid_brands:
            return True

        if not sale_line.brand:
            return False

        if sale_line.brand not in self.valid_brands:
            return False

        return True
