# encoding: utf-8
from datetime import (
    datetime,
    timedelta
)

from systools import sys_log_exception
from typing import (
    List,
    Dict
)

from basereport import (
    SimpleReport,
    TableReport,
    ReportColumnDefinition,
    AlignTypes
)
from report import (
    Presenter,
    Part
)
from report.command import (
    NewLineCommand,
    AlignCommand,
    RepeatCommand,
    FontCommand
)
from reportsdata.tefcancellationreport.dto import TefCancellationGeneratorDto
from reportsdata.tefcancellationreport.dto import TefCancellationHeaderDto
from reportsdata.tefcancellationreport.dto import TefCancellationBodyDto
from utils.report_formatters import (
    format_line,
    remove_accents,
    format_price
)


class TefCancellationReportPresenter(Presenter):

    def __init__(
        self,
        columns  # type: int
    ):
        # type: (...) -> None

        self.total = 0
        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=' '
            ),
            ReportColumnDefinition(
                width=31
            )
        ]
        self.items_table_column_definitions = [
            ReportColumnDefinition(
                width=38
            )
        ]
        self.footer_table_column_definitions = [
            ReportColumnDefinition(
                width=29,
                after_text=' ',
                align=AlignTypes.RIGHT
            ),
            ReportColumnDefinition(
                width=9,
                align=AlignTypes.RIGHT
            )
        ]

    def present(
        self,
        dto     # type: TefCancellationGeneratorDto
    ):
        # type: (...) -> SimpleReport

        try:
            parts = [Part(commands=[FontCommand(FontCommand.Font.B)])]
            parts.extend(self._build_header(dto.header))
            parts.extend(self._build_table(dto.body))
            parts.extend([self._new_line(2)])
        except Exception as _:
            sys_log_exception("Error building Benefits Report")
            raise

        return SimpleReport(parts, self.columns)

    def _build_header(
        self,
        header  # type: TefCancellationHeaderDto
    ):
        # type: (...) -> List

        parts = [
            self._new_line(),
            self._print_text(
                text=header.title,
                align='center'
            ),
            self._new_line(2),
            self._get_store_name(header),
            self._get_date_time(),
            self._get_period(header),
            self._new_line(2)
        ]

        return parts

    def _build_table(
        self,
        body    # type: TefCancellationBodyDto
    ):
        # type: (...) -> List

        tenders_by_date = body.tenders_by_date
        if len(tenders_by_date) == 0:
            return self._build_nothing_found()

        data = []
        for date, tenders in sorted(tenders_by_date.items()):
            data.extend(self._build_table_header(date))
            data.extend(self._build_table_rows(
                items=tenders,
                card_brands=body.card_brands
            ))
            data.extend(self._build_table_footer())

        return data

    def _build_table_header(
        self,
        date
    ):
        # type: (...) -> List

        date = datetime.strptime(
            date,
            "%Y-%m-%d"
        ).strftime("%d/%m/%Y")

        header = [
            TableReport(
                [[
                    "$TEF_CANCELLATION_REPORT_DATE",
                    date
                ]],
                self.header_table_column_definitions
            ),
            self._repeat_char(
                "-",
                self.columns
            )
        ]

        return header

    def _build_table_rows(
        self,
        items,          # type: List
        card_brands     # type: Dict
    ):
        # type: (...) -> List

        rows = []
        self.total = 0
        for item in items:
            value = float(item.tender_detail["tenderAmount"])
            self.total += value
            date_time = datetime.strptime(
                item.timestamp,
                "%Y-%m-%dT%H:%M:%S.%f"
            )
            row_1 = [
                self._convert_to_local_time(date_time).strftime("%H:%M"),
                remove_accents(item.tender_descr),
                remove_accents(card_brands[int(item.tender_detail["Bandeira"])].description)
            ]
            row_2 = [
                self._get_card_number(item.tender_detail["CardNumber"], 6),
                item.tender_detail["NsuHost"],
                format_price(value)
            ]
            rows.extend([
                self._print_text(" ".join(row_1)),
                self._new_line(),
                self._print_text(" ".join(row_2)),
                self._new_line(),
                self._repeat_char(
                    "-",
                    self.columns
                )
            ])

        return rows

    def _build_nothing_found(self):
        # type: () -> List

        parts = [
            self._repeat_char(
                "-",
                self.columns
            ),
            self._new_line(),
            self._print_text("$NO_CANCELLATION_ERRORS", align='center'),
            self._new_line(2),
            self._repeat_char(
                "-",
                self.columns
            )
        ]
        return parts

    def _build_table_footer(self):
        # type: () -> List

        data = [
            TableReport(
                [[
                    "$TEF_CANCELLATION_REPORT_TOTAL",
                    format_price(self.total)
                ]],
                self.footer_table_column_definitions
            ),
            self._new_line()
        ]

        return data

    @staticmethod
    def _new_line(repeat=1):
        # type: (...) -> Part

        data = Part(
            None,
            [NewLineCommand() for i in range(0, repeat)]
        )

        return data

    @staticmethod
    def _repeat_char(char, columns):
        # type: (...) -> Part

        data = Part(
            None,
            [
                RepeatCommand(
                    columns,
                    char
                ),
                NewLineCommand()
            ]
        )

        return data

    @staticmethod
    def _print_text(text, align='left'):
        # type: (...) -> Part

        if type(text) == list:
            text = " ".join(text)

        if align == 'center':
            align_command = AlignCommand(AlignCommand.Alignment.center)
        elif align == 'right':
            align_command = AlignCommand(AlignCommand.Alignment.right)
        else:
            align_command = AlignCommand(AlignCommand.Alignment.left)

        data = Part(
            remove_accents(text),
            [align_command]
        )

        return data

    @staticmethod
    def _convert_to_local_time(
        date_time   # type: datetime
    ):
        # type: (...) -> datetime

        return date_time - timedelta(hours=3)

    @staticmethod
    def _get_card_number(card_number, digits):
        card_number = card_number[:digits].ljust(8, 'X')
        card_number = card_number[:4] + "." + card_number[4:]
        return card_number


    def _get_store_name(
        self,
        header  # type: TefCancellationHeaderDto
    ):
        # type: (...) -> List

        data = format_line(
            remove_accents("$STORE"),
            header.store_name,
            self.columns,
            self.header_column_definition
        )

        return data[0]

    def _get_date_time(self):
        # type: () -> List

        current_datetime = datetime.now().strftime('%d/%m/%y %H:%M:%S')
        data = format_line(
            remove_accents("$DATE_TIME"),
            current_datetime,
            self.columns,
            self.header_column_definition
        )

        return data[0]

    def _get_period(
        self,
        header  # type: TefCancellationHeaderDto
    ):
        # type: (...) -> List

        period = "{0} - {1}".format(
            header.start_date.strftime('%d/%m/%y'),
            header.end_date.strftime('%d/%m/%y')
        )
        data = format_line(
            remove_accents("$PERIOD"),
            period,
            self.columns,
            self.header_column_definition
        )

        return data[0]
