# encoding: utf-8
from datetime import datetime

from systools import sys_log_exception
from typing import List

from basereport import (
    SimpleReport,
    TableReport,
    ReportColumnDefinition,
    AlignTypes,
)
from report import (
    Presenter,
    Part,
)

from report.command import (
    NewLineCommand,
    AlignCommand,
    RepeatCommand,
    FontCommand,
)

from reportsdata.justificationdiscountreport.dto import (
    JustificationDiscountGeneratorDto,
    JustificationDiscountHeaderDto,
    JustificationDiscountBodyDto,
)
from utils.report_formatters import (
    format_line,
    remove_accents,
    format_price,
)


class JustificationDiscountReportPresenter(Presenter):

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

        self.total = 0
        self.columns = columns
        self.title_column_definition = [
            ReportColumnDefinition(
                width=columns,
                align=AlignTypes.CENTER,
                fill_with_char='',
                before_text='',
                after_text='',
            ),
        ]

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

        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,
            ),
        ]

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

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

        return SimpleReport(
            parts=parts,
            width=self.columns,
        )

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

        parts = [
            self._get_separator(),
            self._get_title(
                header=header,
            ),
            self._new_line(),
            self._get_date_time(),
            self._get_period(
                header=header,
            ),
            self._get_operator(
                header=header,
            ),
        ]

        return parts

    def _get_title(
        self,
        header,     # type: JustificationDiscountHeaderDto
    ):
        # type: (...) -> str

        title = remove_accents(
           text=header.title,
        )

        return format_line(
            title=None,
            text=title,
            columns=self.columns,
            definition=self.title_column_definition,
        )[0]

    def _build_body(
        self,
        body,   # type: JustificationDiscountBodyDto
    ):
        # type: (...) -> List

        justifications = body.justifications
        if len(justifications) == 0:
            return [self._get_separator()]

        data = []
        for date, items in sorted(justifications.items()):
            data.append(
                self._get_separator(),
            )
            data.append(
                self._build_date_row(
                    date=date,
                ),
            )
            data.extend(
                self._build_rows(
                    items=items,
                ),
            )

        return data

    def _build_date_row(
        self,
        date,   # type: str
    ):
        # type: (...) -> str

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

        return self._get_line(
            title=None,
            text=date,
            definition=self.title_column_definition,
        )[0]

    def _build_rows(
        self,
        items,      # type: List
    ):
        # type: (...) -> List

        parts = []
        size = len(items)

        for item in items:
            size = size - 1
            self._set_row(
                parts=parts,
                title="$ORDER",
                text="$HOUR",
            )
            self._set_row(
                parts=parts,
                title=item.order_id.zfill(8),
                text=item.order_time,
            )
            self._set_row(
                parts=parts,
                title="$TITLE_REPORT_DISCOUNT_COD",
                text="$OPERATOR",
            )
            self._set_row(
                parts=parts,
                title=item.benefit_id,
                text=item.applied_by,
            )
            self._set_row(
                parts=parts,
                title="$DISCOUNT",
                text="$TOTAL",
            )
            self._set_row(
                parts=parts,
                title=format_price(
                    price=float(item.benefit_total_value),
                ),
                text=format_price(
                    price=float(item.order_value),
                ),
            )

            justification = remove_accents(
                text=item.justification,
            )
            self._set_row(
                parts=parts,
                title=None,
                text="$JUSTIFICATION {0}".format(str(justification)),
                definition=self.center_definitions,
            )

            if size > 0:
                parts.extend([
                    Part(
                        text=None,
                        commands=[
                            RepeatCommand(
                                count=self.columns,
                                text="-",
                            ),
                            NewLineCommand(),
                            AlignCommand(
                                alignment=AlignCommand.Alignment.center,
                            ),
                        ],
                    )
                ])

        return parts

    def _set_row(
        self,
        parts,          # type: List
        title,          # type: str
        text,           # type: str
        definition=None,     # type: ReportColumnDefinition
    ):
        # type: (...) -> None

        definition = definition if definition is not None else self.body_column_definition

        parts.extend(
            self._get_line(
                title=title,
                text=text,
                definition=definition,
            )
        )

    def _get_operator(
        self,
        header,   # type: JustificationDiscountHeaderDto
    ):
        # type: (...) -> List

        return self._get_line(
            title="$OPERATOR",
            text="{0} (POS # {1})".format(header.operator, header.pos_id.zfill(2)),
            definition=self.header_column_definition,
        )[0]

    def _get_date_time(
        self,
    ):
        # type: () -> TableReport

        current_datetime = datetime.now().strftime('%d/%m/%y %H:%M:%S')

        return self._get_line(
            title="$DATE_TIME",
            text=current_datetime,
            definition=self.header_column_definition,
        )[0]

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

        period = "{0} - {1}".format(
            header.start_date,
            header.end_date,
        )

        return self._get_line(
            title="$PERIOD",
            text=period,
            definition=self.header_column_definition,
        )[0]

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

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

        return data

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

        value = remove_accents(
           text=text,
        )

        return format_line(
            title=title,
            text=value,
            columns=self.columns,
            definition=definition,
        )

    def _get_separator(
        self,
    ):
        # type: (...) -> Part

        return Part(
            text=None,
            commands=[
                RepeatCommand(self.columns, "="),
                NewLineCommand(),
                AlignCommand(AlignCommand.Alignment.center)
            ],
        )

