# encoding: utf-8
import sys
import locale

from typing import List, Any

from systools import sys_log_exception

from basereport import TableReport, ReportColumnDefinition, SimpleReport, AlignTypes
from report import Presenter, Part
from report.command import NewLineCommand, AlignCommand, RepeatCommand, FontCommand
from helper import remove_accents

from reportsdata.salechallengereport.dto import SaleChallengeHeaderDto, SaleChallengeGeneratorDto
from salechallengereport.model import OperatorSaleItem, ProductSaleItem


class SaleChallengePresenter(Presenter):
    def __init__(self, columns):
        self.columns = columns

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

        self.body_operator_title_column_definition = [
            ReportColumnDefinition(width=32, align=AlignTypes.LEFT),
            ReportColumnDefinition(width=columns - 32, align=AlignTypes.CENTER)
        ]

        self.body_operator_item_column_definition = [
            ReportColumnDefinition(width=32),
            ReportColumnDefinition(
                    width=columns - 32, before_fill_text="[", after_fill_text="]", align=AlignTypes.RIGHT
            )
        ]

        self.body_product_title_column_definition = [
            ReportColumnDefinition(width=18, align=AlignTypes.LEFT),
            ReportColumnDefinition(width=columns - 18, before_fill_text=" ", align=AlignTypes.LEFT)
        ]

        self.body_product_item_column_definition = [
            ReportColumnDefinition(width=18),
            ReportColumnDefinition(width=6, before_fill_text="[", after_fill_text="]", align=AlignTypes.RIGHT),
            ReportColumnDefinition(width=14, before_fill_text=" R$", fill_with_char=" ", align=AlignTypes.RIGHT)
        ]

        try:
            locale.setlocale(locale.LC_ALL, "portuguese_brazil")
        except Exception as _:
            locale.setlocale(locale.LC_ALL, "pt_BR.UTF-8")

    def present(self, dto):
        # type: (SaleChallengeGeneratorDto) -> SimpleReport
        try:
            parts = [Part(commands=[FontCommand(FontCommand.Font.B)])]
            parts.extend(self._build_header(dto.header))
            parts.extend(self._build_items_title(dto.columns))
            parts.extend(self._build_items(dto.items, dto.columns))
            parts.append(Part(None, [NewLineCommand()]))
        except Exception as _:
            sys_log_exception("Error building Sale Challenge Report")
            raise

        return SimpleReport(parts, self.columns)

    def _build_header(self, header):
        parts = [
            Part(None, [RepeatCommand(self.columns, "="), NewLineCommand()]),
            self._get_title_report(),
            Part(None, [NewLineCommand()])
        ]
        parts.extend(self._get_sale_challenge_name(header))
        parts.extend(self._get_sale_challenge_period(header))
        parts.extend(self._get_sale_challenge_operator(header))
        parts.append(Part(None, [NewLineCommand()]))

        return parts

    def _build_items_title(self, columns):
        # type: (int) -> List[TableReport]

        parts = [
            Part(None, [RepeatCommand(self.columns, "="), NewLineCommand()]),
            self._get_items_title(columns),
            Part(None, [RepeatCommand(self.columns, "="), NewLineCommand()])
        ]

        return parts

    def _build_items(self, items, columns):
        # type: (Any[List[OperatorSaleItem], List[ProductSaleItem]], int) -> List[TableReport]

        rows = []
        two_columns = columns == 2
        column_definition = \
            self.body_operator_item_column_definition if two_columns else self.body_product_item_column_definition

        total_price = 0
        total_quantity = 0
        for item in items:
            total_quantity += item.quantity
            total_price += item.total_price if not two_columns else 0
            name = item.operator_name if two_columns else item.name
            name = remove_accents(name)
            quantity = self._str_int(item.quantity)
            price = self._str_float(item.total_price) if not two_columns else 0
            row = [[name, quantity]] if two_columns else [[name, quantity, price]]
            rows.append(TableReport(row, column_definition))

        rows.append(Part(None, [RepeatCommand(self.columns, "="), NewLineCommand()]))

        total_row = ["$TOTAL", self._str_int(total_quantity)]
        if not two_columns:
            total_row.append(self._str_float(total_price))
        rows.append(TableReport([total_row], column_definition))

        return rows

    def _format_line(self, title, text, definition):
        # type: (str, str, List[ReportColumnDefinition]) -> List[TableReport]

        if not text:
            return []

        lines = []
        current_string_start = 0
        current_string_end = definition[1].width if title else self.columns

        while len(text) > 0:
            if title and current_string_start == 0:
                lines.append(TableReport([[title, text[:current_string_end]]], definition))
            else:
                lines.append(TableReport([[text[current_string_start:current_string_end]]],
                                         [ReportColumnDefinition(width=self.columns)]))

            if current_string_end > len(text):
                break

            current_string_start = current_string_end
            current_string_end += self.columns

        return lines

    @staticmethod
    def _get_title_report():
        return Part("$CHALLENGE_REPORT", [AlignCommand(AlignCommand.Alignment.center)])

    def _get_sale_challenge_name(self, header):
        return self._format_line("$CHALLENGE", remove_accents(header.sale_challenge_name),
                                 self.header_column_definition)

    def _get_sale_challenge_period(self, header):
        # type: (SaleChallengeHeaderDto) -> List[TableReport]

        return self._format_line("$PERIOD", "{} - {}".format(header.start_date.strftime("%d/%m/%y"),
                                                             header.end_date.strftime("%d/%m/%y")),
                                 self.header_column_definition)

    def _get_items_title(self, columns):
        if columns == 2:
            return TableReport([["$NAME", "$QUANTITY_SHORT"]], self.body_operator_title_column_definition)
        return TableReport([["$NAME", "$QUANTITY_SHORT"]], self.body_product_title_column_definition)

    def _get_sale_challenge_operator(self, header):
        # type: (SaleChallengeHeaderDto) -> List[TableReport]

        if header.operator_name is None:
            return self._format_line("$OPERATOR", "$ALL_PRODUCTS", self.header_column_definition)
        return self._format_line("$OPERATOR", remove_accents(header.operator_name), self.header_column_definition)

    def _get_order_number(self, header):
        return self._format_line("$ORDER_DELIVERY_REPORT", header.order_number, self.header_column_definition)

    @staticmethod
    def _str_float(float_num):
        return locale.format("%.2f", float_num, True).replace(".", ",")

    @staticmethod
    def _str_int(float_num):
        return locale.format("%.0f", float_num, True)
