# -*- coding: utf-8 -*-

import locale
import collections

from datetime import datetime
from typing import Optional

from repository import OrderRepository
from helper import remove_accents


DOUBLE_LINE = u"======================================\n"


class DiscountReport(object):
    TYPE_REAL_DATE = "RealDate"
    TYPE_BUSINESS_PERIOD = "BusinessPeriod"
    TYPE_SESSION_ID = "SessionId"

    def __init__(self, order_repository, store_id):
        # type: (OrderRepository, unicode) -> None
        self.order_repository = order_repository
        self.store_id = store_id

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

    def generate_discount_report_by_real_date(self, pos_id, report_pos, initial_date, end_date, operator_id):
        # type: (int, Optional[unicode], datetime, datetime, Optional[unicode]) -> str
        return self._generate_discount_report(self.TYPE_REAL_DATE, pos_id, report_pos, initial_date, end_date,
                                              operator_id, None)

    def generate_discount_report_by_business_period(self, pos_id, report_pos, initial_date, end_date, operator_id):
        # type: (int, Optional[unicode], datetime, datetime, unicode) -> str

        return self._generate_discount_report(self.TYPE_BUSINESS_PERIOD, pos_id, report_pos, initial_date, end_date,
                                              operator_id, None)

    def generate_discount_report_by_session_id(self, pos_id, session_id):
        # type: (int, Optional[unicode]) -> str

        return self._generate_discount_report(self.TYPE_SESSION_ID, pos_id, None, None, None, None, session_id)

    def _generate_discount_report(self, report_type, pos_id, report_pos, initial_date, end_date, operator_id,
                                  session_id):
        # type: (unicode, int, Optional[unicode], Optional[datetime], Optional[datetime], Optional[unicode], Optional[unicode]) -> str # noqa

        discount_orders = None
        if report_type == self.TYPE_REAL_DATE:
            discount_orders = self.order_repository.get_discount_orders_by_real_date(initial_date, end_date,
                                                                                     operator_id, report_pos)

        elif report_type == self.TYPE_BUSINESS_PERIOD:
            discount_orders = self.order_repository.get_discount_orders_by_business_period(initial_date, end_date,
                                                                                           operator_id, report_pos)

        elif report_type == self.TYPE_SESSION_ID:
            discount_orders = self.order_repository.get_discount_orders_by_session_id(session_id)
            start_index = session_id.find("user=")
            end_index = session_id.find(",", start_index)
            operator_id = session_id[start_index + 5:end_index]
            start_index = session_id.find("period=")
            initial_date = end_date = datetime.strptime(session_id[start_index + 7:], "%Y%m%d")

        header = self._build_header(report_type, pos_id, report_pos, initial_date, end_date, operator_id)
        body = self._build_body(discount_orders)

        report = header + body
        report_bytes = report.encode("utf-8")

        return report_bytes

    def _build_header(self, report_type, pos_id, report_pos, initial_date, end_date, operator_id):
        operator = "Todos"
        if operator_id is not None:
            operator = operator_id

        pos_list = "Todos" if report_pos is None else report_pos.zfill(2)
        if report_type == self.TYPE_REAL_DATE:
            report_type_text = "Data Fiscal"
        elif report_type == self.TYPE_BUSINESS_PERIOD:
            report_type_text = "Dia Negocio"
        else:
            report_type_text = "Surpresa"
            pos_list = pos_id

        header = DOUBLE_LINE
        header += u"  Relatorio Descontos - {0:>11}\n".format(report_type_text)
        header += u" Loja.........: " + self.store_id + "\n"
        header += u" Data/hora....: " + datetime.now().strftime("%d/%m/%Y %H:%M:%S") + "\n"
        formatted_initial_date = initial_date.strftime("%d/%m/%y")
        formatted_end_date = end_date.strftime("%d/%m/%y")
        header += u" {0:.<13}: {1} - {2}\n".format(report_type_text, formatted_initial_date, formatted_end_date)
        header += u" ID Operador..: " + u"{0} (POS # {1})\n".format(operator, str(pos_id).zfill(2))
        temp_pos_list = u" POS incluidos: " + u"{0}".format(pos_list)
        header += temp_pos_list[:38]
        if len(temp_pos_list) > 38:
            header += (u"\n                %s" % temp_pos_list[38:60])
        if len(temp_pos_list) > 66:
            header += (u"\n                %s" % temp_pos_list[60:82])
        header += u"\n"

        return header

    @staticmethod
    def _build_body(orders_with_discount):
        manual_orders_with_discount = [x for x in orders_with_discount if not x.delivery]
        delivery_orders_with_discount = [x for x in orders_with_discount if x.delivery]

        body = DOUBLE_LINE
        body += u"    Descricao       Qtd      Total\n"
        body += DiscountReport._add_discount_total_line("Total Descontos..:", orders_with_discount)
        body += DiscountReport._add_discount_total_line("      Delivery...:", delivery_orders_with_discount)
        body += DiscountReport._add_discount_total_line("      Operador...:", manual_orders_with_discount)
        body += DOUBLE_LINE
        body += u"PEDIDOS".center(38)
        body += DOUBLE_LINE

        discounts_by_date = DiscountReport._group_discounts_by_date(manual_orders_with_discount)

        for key in discounts_by_date.keys():
            body += "\n\n" + _format_date_only(key).center(38)
            total_discounts = len(discounts_by_date[key])
            discount_count = 1
            for order in discounts_by_date[key]:
                write_end_line = total_discounts != discount_count
                body = DiscountReport._add_discount(body, order, write_end_line)
                discount_count += 1

            body += DOUBLE_LINE

        return body

    @staticmethod
    def _add_discount_total_line(text, orders):
        discount_qty = len(orders)
        total_discount_value = sum([order.discount_amount for order in orders])

        return u"{0} [{1:>4}] R${2:>10}\n".format(text, discount_qty, _float_convert(total_discount_value))

    @staticmethod
    def _add_discount(body, order, write_end_line):
        order_id = str(order.order_id)
        applied_date = _format_time(order.applied_date)
        total_gross = _float_convert(order.total_gross)
        discount_amount = _float_convert(order.discount_amount)
        operator = remove_accents(order.operator[:20])
        authorizer = remove_accents(order.authorizer[:20])

        body += "\nPed./Hora..: {0:>12} /   {1:>8}\n".format(order_id, applied_date)
        body += "Total/Desc.:   R${0:>8} / R${1:>8}\n".format(total_gross, discount_amount)
        body += "Operator...: {0:>25}\n".format(operator)
        body += "Autorizador: {0:>25}\n".format(authorizer)

        if write_end_line:
            body += u"--------------------------------------\n"

        return body

    @staticmethod
    def _group_discounts_by_date(discount_orders):
        discounts_by_date = {}
        for discount in discount_orders:
            date = datetime(discount.applied_date.year, discount.applied_date.month, discount.applied_date.day)
            if date in discounts_by_date:
                discounts_by_date[date].append(discount)
            else:
                discounts_by_date[date] = [discount]
        return collections.OrderedDict(sorted(discounts_by_date.items()))

    @staticmethod
    def _get_discount_values(discount_orders):
        value_discount = 0
        qty_discount = 0

        for order in discount_orders:
            if order.discount > 0:
                value_discount += order.discount
                qty_discount += 1

        return qty_discount, value_discount


def _format_date_only(date):
    return datetime.strftime(date, "%d/%m/%Y")


def _format_time(date):
    return datetime.strftime(date, "%H:%M:%S")


def _float_convert(value):
    return locale.format("%.2f", value, True)
