from msgbus import MBEasyContext
from old_helper import round_half_away_from_zero
from pos_model import (
    Order,
    SaleItem,
)
from typing import (
    Dict,
    List,
)

from common import (
    CalculateTaxesItemCommon,
    FiscalParameterController,
    build_item_taxes,
)


class TipBuilderCommon(object):
    def __init__(self, tip_config, tip_config_range, mb_context, fiscal_parameter_controller, calculate_taxes_common):
        # type: (Dict, Dict, MBEasyContext, FiscalParameterController, CalculateTaxesItemCommon) -> None

        self.mb_context = mb_context
        self.tip_config = tip_config
        self.tip_config_range = tip_config_range
        self.fiscal_parameter_controller = fiscal_parameter_controller
        self.calculate_taxes_common = calculate_taxes_common

    def get_tip_items(self, order):
        # type: (Order) -> List[SaleItem]

        if order.tip:
            if len(self.tip_config_range) > 0:
                return self._build_tip_by_range(order)

            if len(self.tip_config) > 0 and self.tip_config['TipCode'] and self.tip_config['TipName']:
                price = order.tip
                return [self._get_sale_line_tip(self.tip_config['TipCode'], self.tip_config['TipName'].upper(), price)]

        return []

    def _build_tip_by_range(self, order):
        # type: (Order) -> List[SaleItem]

        items = []
        tip_ranges = self.get_percentage_by_tip(order.total_gross, order.tip)

        for key, value in tip_ranges.items():
            items.append(self._get_sale_line_tip(key, value.get("TipName"), value.get("TipValue")))

        return items

    def get_percentage_by_tip(self, total_gross, tip):
        # type: (float, float) -> Dict

        result = {}
        remaining_tip = tip
        for item in self.tip_config_range:
            code = item.get("TipCode")
            percentage = abs(item.get("PercentageUpper"))
            value = (percentage * total_gross) / 100.0
            current_tip = value if value <= remaining_tip else remaining_tip
            current_tip = round_half_away_from_zero(current_tip, 2)
            result[code] = {"TipName": item.get("TipName"), "TipValue": current_tip}
            remaining_tip -= current_tip

            if remaining_tip <= 0:
                break

        return self._check_and_adjust_tip_final_value(tip, result)

    def _check_and_adjust_tip_final_value(
            self,
            tip_value,      # type: float
            list_tip        # type: Dict
    ):
        # type:(...) -> Dict

        if len(list_tip) > 0:
            sum_added_tip = round_half_away_from_zero(sum([item.get("TipValue") for item in list_tip.values()]), 2)
            tip_difference = sum_added_tip != tip_value
            if abs(tip_difference) > 0.01:
                last_key_item = list_tip.keys()[-1]
                new_value = list_tip[last_key_item]['TipValue'] + (tip_value - sum_added_tip)
                list_tip[last_key_item]['TipValue'] = round_half_away_from_zero(new_value, 2)
                if list_tip[last_key_item]['TipValue'] <= 0:
                    list_tip.pop(last_key_item)

                list_tip = self._check_and_adjust_tip_final_value(tip_value, list_tip)

        return list_tip

    def _get_sale_line_tip(
        self,           # type: TipBuilderCommon
        part_code,      # type: str
        product_name,   # type: str
        price,          # type: float
    ):
        # type: (...) -> SaleItem

        sale_item = SaleItem()
        sale_item.part_code = str(part_code)
        sale_item.product_name = str(product_name.upper())
        sale_item.quantity = 1
        sale_item.unit_price = price
        sale_item.item_price = price
        sale_item.taxes = build_item_taxes(
            sale_item=sale_item,
            fiscal_parameter_controller=self.fiscal_parameter_controller,
            calculate_taxes_common=self.calculate_taxes_common,
        )
        return sale_item
