# encoding: utf-8
from datetime import datetime
from typing import List, Any, Dict

from basereport import SimpleReport, ReportColumnDefinition, TableReport, AlignTypes
from benefitsreport.model import OrderBenefit
from report import Presenter, Part
from report.command import NewLineCommand, AlignCommand, RepeatCommand, FontCommand
from reportsdata.benefitsreport.builders import BenefitConsolidatedBuilder
from utils.report_formatters import format_line, remove_accents, format_price

from old_helper import convert_from_utf_to_localtime
from systools import sys_log_exception


class BenefitsPresenter(Presenter):
    def __init__(self, columns):
        # type: (int) -> None

        self.columns = columns
        self.header_column_definition = [
            ReportColumnDefinition(width=16, after_fill_text=': ', fill_with_char='.'),
            ReportColumnDefinition(width=columns - 16)
        ]

        self.period_column_definition = [
            ReportColumnDefinition(width=10, after_fill_text=' - ', fill_with_char=''),
            ReportColumnDefinition(width=columns - 10)
        ]

        self.header_table_column_definitions = [
            ReportColumnDefinition(width=7, after_text=' ', fill_with_char='', align=AlignTypes.LEFT),
            ReportColumnDefinition(width=12, after_text=' ', fill_with_char=''),
            ReportColumnDefinition(width=4, after_text=' ', fill_with_char='', align=AlignTypes.CENTER),
            ReportColumnDefinition(width=8),
            ReportColumnDefinition(width=7),
        ]

        self.sub_header_table_column_definitions = [
            ReportColumnDefinition(width=7),
            ReportColumnDefinition(width=12, after_text='', fill_with_char=''),
            ReportColumnDefinition(width=4, after_text='', fill_with_char='', align=AlignTypes.CENTER),
            ReportColumnDefinition(width=7, align=AlignTypes.RIGHT),
            ReportColumnDefinition(width=8, align=AlignTypes.RIGHT),
        ]

        self.items_table_column_definitions = [
            ReportColumnDefinition(width=7),
            ReportColumnDefinition(width=12),
            ReportColumnDefinition(width=4, after_text='', fill_with_char='', align=AlignTypes.CENTER),
            ReportColumnDefinition(width=7, align=AlignTypes.RIGHT),
            ReportColumnDefinition(width=8, align=AlignTypes.RIGHT),
        ]

        self.header_table_column_consolidated_definitions = [
            ReportColumnDefinition(width=16, align=AlignTypes.CENTER, fill_with_char=''),
            ReportColumnDefinition(width=4, align=AlignTypes.CENTER, fill_with_char=''),
            ReportColumnDefinition(width=18, align=AlignTypes.CENTER, fill_with_char=''),
            ReportColumnDefinition(width=18, align=AlignTypes.CENTER, fill_with_char=''),
        ]

    def present(self, dto):
        try:
            parts = [Part(commands=[FontCommand(FontCommand.Font.B)])]
            parts.extend(self._build_header(dto.header))
            parts.extend(self._build_items(dto.body))
            parts.extend([Part(None, [NewLineCommand(), NewLineCommand()])])
        except Exception as _:
            sys_log_exception("Error building Benefits Report")
            raise

        return SimpleReport(parts, self.columns)

    def _build_header(self, header):
        parts = [
            Part(None, [NewLineCommand()]),
            self._get_title(header),
            Part(None, [NewLineCommand(), NewLineCommand()])
        ]
        parts.extend(self._get_promotion_name(header))
        parts.extend(self._get_store_name(header))
        parts.extend(self._get_date_time())
        parts.extend(self._get_period(header))
        parts.extend(self._get_operator_name(header))
        parts.extend([Part(None, [RepeatCommand(self.columns, "="), NewLineCommand()])])

        return parts

    def _build_items(self, body):
        type_report = body.type_report
        items = body.items
        if type_report == "detailed":
            return self._build_items_detailed(items)

        return BenefitConsolidatedBuilder(body, self.columns).generate()

    def _build_items_detailed(self, lines):
        # type: (Dict) -> List

        data = []
        total_qty = 0
        total_default_price = 0.0
        total_discount_value = 0.0
        for item in lines:
            items = []
            qty = 0
            default_price = 0.0
            discount_value = 0.0
            orders = []
            total_products = []
            for product in lines[item]:
                data_table = self._build_product(product)
                self._append_list_header(data_table, orders, total_products)
                qty += float(data_table[2])
                default_price += float(data_table[3].replace(",", ""))
                discount_value += float(data_table[4].replace(",", ""))
                items.extend([TableReport([data_table], self.items_table_column_definitions)])

            orders = str(len(orders)).zfill(5)
            total_products = str(len(total_products)).zfill(3)
            formatted_default_price = format_price(default_price)
            formatted_discount_value = format_price(discount_value)
            totals = [orders, total_products, str(qty).zfill(4), formatted_default_price, formatted_discount_value]

            total_qty += qty
            total_default_price += default_price
            total_discount_value += discount_value
            data.extend(self._build_header_and_items(items, item, totals))

        total_default_price = format_price(total_default_price)
        total_discount_value = format_price(total_discount_value)
        grand_total = [str(total_qty).zfill(4), total_default_price,  total_discount_value]
        data.extend(self._build_footer_table(grand_total))

        return data

    @staticmethod
    def _append_list_header(data_table, orders, total_products):

        if data_table[0] not in orders:
            orders.append(data_table[0])
        if data_table[1] not in total_products:
            total_products.append(data_table[1])

    def _build_header_and_items(self, items, item, totals):
        # type: (List, Any, List) -> List

        data = []
        period = datetime.strptime(item, '%Y%m%d').strftime('%d/%m/%Y')
        data.extend([Part(None, [NewLineCommand()])])
        data.extend(format_line("$BENEFIT_REPORT_PERIOD", period, self.columns, self.period_column_definition))
        header = self._build_header_table(totals)
        data.extend([Part(None, [NewLineCommand()])])
        data.extend(header)
        data.extend(items)
        data.extend([Part(None, [NewLineCommand()])])
        data.extend([Part(None, [RepeatCommand(self.columns, "-"), NewLineCommand(), NewLineCommand()])])

        return data

    def _build_header_table(self, totals):
        # type: (List) -> List

        header = [
            TableReport([["$BENEFIT_REPORT_HEADER_ORDER",
                          "$BENEFIT_REPORT_HEADER_ITEMS",
                          "$QTY",
                          "$BENEFIT_REPORT_HEADER_DEFAULT_VALUE",
                          "$BENEFIT_REPORT_HEADER_DISCOUNT_VALUE"]],
                        self.header_table_column_definitions),
            ]
        header.extend([TableReport([totals], self.sub_header_table_column_definitions)])
        header.extend([Part(None, [RepeatCommand(self.columns, "="), NewLineCommand()])])
        return header

    def _build_footer_table(self, totals):
        # type: (List) -> List

        return [TableReport([["$TOTAL", " ", totals[0], totals[1], totals[2]]], self.items_table_column_definitions)]

    @staticmethod
    def _build_product(product):
        # type: (OrderBenefit) -> List

        order_id = product.order_id.zfill(5)
        product_name = product.product_name
        default_price = format_price(product.ordered_qty * product.default_unit_price)
        discount_value = format_price(product.discount_value)

        return [order_id, product_name, str(product.ordered_qty).zfill(4), default_price, discount_value]

    @staticmethod
    def _get_title(header):
        return Part(header.title, [AlignCommand(AlignCommand.Alignment.center)])

    def _get_promotion_name(self, header):
        promotion_name = header.promotion_name
        title = "$BENEFIT_REPORT_PROMOTION_NAME"
        return format_line(title, promotion_name, self.columns, self.header_column_definition)

    def _get_store_name(self, header):
        return format_line("$STORE", header.store_name, self.columns, self.header_column_definition)

    def _get_date_time(self):
        current_datetime = convert_from_utf_to_localtime(datetime.now()).strftime('%d/%m/%y %H:%M:%S')
        return format_line("$BENEFIT_REPORT_DATE_TIME", current_datetime, self.columns, self.header_column_definition)

    def _get_period(self, header):
        period = "{0} - {1}".format(header.start_date, header.end_date)
        return format_line("$BENEFIT_REPORT_PERIOD", period, self.columns, self.header_column_definition)

    def _get_operator_name(self, header):
        operator = remove_accents(header.operator).upper()
        return format_line("$OPERATOR", operator, self.columns, self.header_column_definition)
