# encoding: utf-8

import locale

from datetime import datetime
from typing import (
    List,
    Union,
)

from basereport import SimpleReport, ReportColumnDefinition, TableReport, AlignTypes
from report import Presenter, Part
from report.command import NewLineCommand, AlignCommand, RepeatCommand, FontCommand
from reportsdata.benefitsreport.dto import BenefitsBodyDto
from reportsdata.benefitsreport.builders import BenefitConsolidatedBuilder
from utils.report_formatters import (
    format_line,
    remove_accents,
    str_float,
    set_inline_text,
)

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.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=17,
                after_fill_text=': ',
                fill_with_char='.',
            ),
            ReportColumnDefinition(
                width=columns - 17,
                fill_with_char='',
                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=''),
        ]

        self.body_column_definition = [
            ReportColumnDefinition(
                width=20,
                after_fill_text='',
                fill_with_char='',
            ),
            ReportColumnDefinition(
                width=columns - 20,
                align=AlignTypes.RIGHT,
            ),
        ]

        self.center_definitions = [
            ReportColumnDefinition(
                width=columns,
                align=AlignTypes.CENTER,
            ),
        ]

        try:
            locale.setlocale(locale.LC_ALL, "portuguese_brazil")
        except:  # noqa:
            locale.setlocale(locale.LC_ALL, "pt_BR.UTF-8")

    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: BenefitsBodyDto
    ):
        # type: (...) -> List

        builder_method = "_build_items_{}".format(body.type_report)
        data = getattr(self, builder_method)(body)
        return data

    def _build_items_consolidated(
            self,
            body    # type: BenefitsBodyDto
    ):
        # type: (...) -> List

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

    def _build_items_detailed(
            self,
            body    # type: BenefitsBodyDto
    ):
        # type: (...) -> List

        data = []
        keys_size = len(body.items.keys())

        for period, applied_benefits in body.items.items():
            data.extend(self._get_detailed_period_header(period))
            keys_size -= 1
            items_size = len(applied_benefits)
            for benefit in applied_benefits:
                items_size -= 1
                self.set_row_body(
                    data,
                    title="$ORDER",
                    text=str(benefit["order_id"]).zfill(5),
                )
                self.set_row_body(
                    data,
                    title="$HOUR",
                    text=benefit["order_time"],
                )
                self.set_row_body(
                    data,
                    title="$OPERATOR",
                    text=str(benefit["operator"]),
                )
                self.set_row_body(
                    data,
                    title="$TYPE",
                    text=benefit["discount_type"],
                )
                self.set_row_body(
                    data,
                    title="$AMOUNT_DISCOUNT_BODY_REPORT",
                    text=str_float(benefit["benefit_total_value"]),
                )
                self.set_row_body(
                    data,
                    title="$AMOUNT_ORDER_BODY_REPORT",
                    text=str_float(benefit["total_gross"]),
                )
                if items_size > 0:
                    data.extend(self._get_divider(divider="-"))

            if keys_size > 0:
                data.extend(self._get_divider())

        return data

    def set_row_body(
        self,
        parts,      # type: List
        title,      # type: str
        text,       # type: str
    ):
        set_inline_text(
            parts=parts,
            title=str(title),
            text=str(text),
            columns=self.columns,
            definition=self.items_table_column_definitions
        )

    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):
        # type: (...) -> List[TableReport]

        current_datetime = datetime.now().strftime("%x %X")
        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)

    def _get_divider(
        self,
        divider="=",     # type: str
    ):
        # type: (...) -> List[Part]

        return [Part(None, [RepeatCommand(self.columns, divider), NewLineCommand()])]

    def _get_detailed_period_header(
            self,
            period,  # type: str
    ):
        # type: (...) -> List[Union[Part, TableReport]]

        period_date = datetime.strptime(period, "%Y%m%d")

        header = []
        header.extend(
            format_line(
                None,
                period_date.strftime('%x'),
                self.columns,
                self.center_definitions
            )
        )

        header.extend(self._get_divider())

        return header

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

    @staticmethod
    def _get_new_line():
        # type: (...) -> List[Part]

        return [Part(None, [NewLineCommand()])]
