# -*- coding: utf-8 -*-
import json

from msgbus import MBEasyContext
from pos_model import OrderParser, SaleItem
from xml.etree import cElementTree as eTree

from _GeneralTaxCalculator import GeneralTaxCalculator
from _OrderFormatter import OrderFormatter
from taxcalculator import ReducedBaseAmountTaxCalculator
from typing import Dict


class TaxCalculatorService(object):
    def __init__(self, order_parser, general_tax_calculator, order_formatter, mb_context):
        # type: (OrderParser, GeneralTaxCalculator, OrderFormatter, MBEasyContext) -> None

        self.order_parser = order_parser
        self.general_tax_calculator = general_tax_calculator
        self.order_formatter = order_formatter
        self.mb_context = mb_context
        self.calculators = {}

    def calculate_taxes(self, order_str):
        # type: (str) -> unicode
        xml = eTree.XML(order_str)
        root = xml if xml.tag == "Order" else None
        order_xml = root or xml.find(".//Order")
        if not order_xml:
            raise Exception("Invalid order XML")

        order = self.order_parser.parse_order(order_xml)
        for sale_item in order.sale_items:
            tax_items = self.general_tax_calculator.calculate_all_taxes(sale_item)
            sale_item.taxes = tax_items

        formatted_order = self.order_formatter.format_order(order)

        return formatted_order

    def insert_tx_idx(self, order_str):
        # type: (str) -> unicode
        order_xml = eTree.XML(order_str).find("Order")
        for sale_line in order_xml.findall("SaleLine"):
            if float(sale_line.get("qty") or 1) == 0:
                continue

            sale_item = self.order_parser.create_sale_item(sale_line)
            tax_items = self.general_tax_calculator.calculate_all_taxes(sale_item)
            sale_line_price = sale_item.unit_price if sale_item.unit_price > 0 else sale_item.added_unit_price
            if tax_items and float(sale_line_price) > 0:
                sale_item.taxes = tax_items
                tax_item = sale_item.get_tax_item("ICMS")
                if tax_item is None:
                    continue

                sale_line.set("taxIndex", tax_item.tax_index)
        return eTree.tostring(order_xml)

    def calculate_taxes_item(self, sale_line):
        # type: (str) -> unicode

        sale_line = json.loads(sale_line)

        if float(sale_line.get("qty") or 1) == 0:
            return []
        part_code = str(sale_line.get("partCode"))
        sale_item = SaleItem(
                part_code=part_code,
                product_name=sale_line.get("productName"),
                quantity=sale_line.get("quantity"),
                unit_price=sale_line.get("unitPrice"),
                item_price=sale_line.get("itemPrice")
        )

        calculators = self._get_taxes_calculator(sale_line, part_code)
        taxes_items = self.general_tax_calculator.calculate_all_taxes(sale_item, calculators)

        return json.dumps(taxes_items, default=lambda x: x.__dict__)

    def _get_taxes_calculator(self, sale_line, part_code):
        # type: (Dict, str) -> []

        if part_code in self.calculators.keys():
            return self.calculators[part_code]

        taxes_calculate = json.loads(sale_line.get("taxesCalc"))
        base_reduction_dict = self._get_sale_line_tax_dictionary(sale_line, "baseReduction")
        cst_dict = self._get_sale_line_tax_dictionary(sale_line, "cst")
        try:
            data = []
            for tax in taxes_calculate:
                index = str(tax.get("fiscalIndex"))
                code = tax.get("code")
                name = str(tax.get("name"))
                products = dict([(str(key), str(value)) for key, value in tax.get("product").items()])
                rate = float(tax.get("rate"))
                params = 'ICMS' if name in ['PIS', 'COFINS'] else None
                calculator = ReducedBaseAmountTaxCalculator(
                    self.mb_context,
                    code,
                    name,
                    index,
                    rate,
                    products,
                    params,
                    cst_dict,
                    base_reduction_dict
                )
                data.append(calculator)
            self.calculators[part_code] = data
            return data
        except Exception as _:
            return {}

    @staticmethod
    def _get_sale_line_tax_dictionary(
        sale_line,
        tax_name
    ):
        # type: (...) -> Dict

        response = {}
        sale_line_tax = sale_line.get(tax_name)
        if sale_line_tax is not None:
            response = sale_line_tax.items()
            response = dict([(str(key), str(value)) for key, value in response])

        return response

