# encoding: utf-8

from typing import Dict, Optional, Tuple, List

from benefitsreport.commons import discount_in_all_items, check_item_in_action
from benefitsreport.model import OrderBenefit
from benefitsreport.repository import BenefitsOrderRepository


class BenefitsReport(object):
    def __init__(self, order_repository):
        # type: (BenefitsOrderRepository) -> None

        self.order_repository = order_repository

    def get_benefits_orders_detailed(self, initial_date, end_date, operator_id, benefits):
        # type: (str, str, str, Dict) -> Dict

        orders = self.order_repository.get_orders_benefit_detailed(initial_date, end_date, operator_id, benefits)
        result = {}
        for period in list(dict.fromkeys([item.business_period for item in orders])):
            filtered_orders = filter(lambda x: self._filter_item(x, period), orders)
            if len(filtered_orders) > 0:
                result[period] = filtered_orders

        return result

    def get_benefits_orders_consolidated(self, initial_date, end_date, operator_id):
        # type: (str, str, str) -> Dict

        orders = self.order_repository.get_orders_benefit_consolidated(initial_date, end_date, operator_id)
        benefits_keys = [item.applied_benefits.benefit_name for item in orders]
        result = {}
        for benefits_key in list(dict.fromkeys(benefits_keys)):
            items = filter(lambda x: self._filter_item_consolidated(x, benefits_key), orders)
            if len(items) > 0:
                values = [self._get_data_collum(line) for line in items]
                result[str(benefits_key)] = self._sum_vales(values)

        return result

    def _filter_item(self, item, period):
        # type: (OrderBenefit, str) -> Optional[OrderBenefit]

        if item.business_period != period:
            return
        item = self._get_discount_item(item)
        if item.discount_value > 0:
            return item

    @staticmethod
    def _filter_item_consolidated(item, benefits_key):
        # type: (OrderBenefit, str) -> Optional[OrderBenefit]

        benefit_total_value = item.applied_benefits.benefit_total_value
        if item.applied_benefits.benefit_name == benefits_key and benefit_total_value > 0:
            discount_items = item.applied_benefits.discount_item_values
            line_number = item.line_number
            items = [item for key in discount_items.keys() if line_number in discount_items[key].keys()]
            if len(items) == 0:
                return

            return item

    def _get_discount_item(self, item):
        # type: (OrderBenefit) -> Optional[OrderBenefit]

        try:
            self._filter_promotion_tender(item)
            discount_items = item.applied_benefits.discount_item_values
            for _, value in discount_items.iteritems():
                if item.line_number in value.keys():
                    value_discount = value[item.line_number]
                    item.discount_value += item.ordered_qty * value_discount
        except KeyError as _:
            pass
        return item

    def _filter_promotion_tender(self, item):
        # type: (OrderBenefit) -> OrderBenefit

        try:
            burnt_items = item.applied_benefits.burnt_items
            item_burn = burnt_items[item.key_burn]
            if item_burn < 0:
                raise KeyError()

            item.discount_value = sum([float(x.get("amount")) for x in item.applied_benefits.promotion_tender])
        except KeyError as _:
            item = self._check_item_in_other_line(item)
        return item

    @staticmethod
    def _check_item_in_other_line(item):
        # type: (OrderBenefit) -> OrderBenefit

        benefit = item.applied_benefits.benefit
        part_code = item.part_code
        discount_all_items = discount_in_all_items(benefit)
        if not discount_all_items and check_item_in_action(benefit, part_code):
            promotion_tender = item.applied_benefits.promotion_tender
            item.discount_value = sum([float(x.get("amount")) for x in promotion_tender])

        return item

    @staticmethod
    def _sum_vales(values):
        # type: (List) -> Tuple

        qty = 0
        default_value = 0.0
        discount_value = 0.0
        for value in list(values):
            qty += value[0]
            default_value += value[1] if value[0] == 1 else value[0] * value[1]
            discount_value += value[2]

        benefit_description = values[0][3]
        return qty, default_value, discount_value, benefit_description

    def _get_data_collum(self, item):
        # type: (OrderBenefit) -> List

        benefit_applied = item.applied_benefits
        benefit_description = benefit_applied.benefit_description
        item = self._get_discount_item(item)
        return [item.ordered_qty, item.default_unit_price, item.discount_value, benefit_description]
