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

import locale

from datetime import datetime
from typing import List, Union

from cashreport import CashReport
from cashreport.repository import (
    AccountRepository,
    FiscalRepository,
    OrderRepository,
    PosCtrlRepository,
    ProductRepository,
    TableRepository,
    TenderRepository,
)
from msgbus import MBEasyContext

from utils import str_float, get_pos_list

DOUBLE_LINE_SEPARATOR = u"======================================\n"


class SalesSummary(object):
    TYPE_BUSINESS_PERIOD = "BusinessPeriod"

    def __init__(self,
                 mb_context,    # type: MBEasyContext
                 store_id,      # type: Union[str, None]
                 ):
        # type: (...) -> None

        self.mb_context = mb_context
        self.store_id = store_id

        self.order_repository = OrderRepository(mb_context, get_pos_list(mb_context), '')
        self.account_repository = AccountRepository(mb_context)
        self.tender_repository = TenderRepository(mb_context)
        self.fiscal_repository = FiscalRepository(mb_context)
        self.pos_ctrl_repository = PosCtrlRepository(mb_context)
        self.table_repository = TableRepository(mb_context)
        self.product_repository = ProductRepository(mb_context)
        self.cash_report = CashReport(
            self.order_repository,
            self.account_repository,
            self.tender_repository,
            self.fiscal_repository,
            self.pos_ctrl_repository,
            self.table_repository,
            self.product_repository,
            None,
        )

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

    def generate_summary_report(
        self,
        pos_id,         # type: int
        initial_date,   # type: datetime
        end_date,       # type: datetime
    ):
        # type: (...) -> str

        paid_orders = self.order_repository.get_paid_orders_by_business_period(
            initial_date=initial_date,
            end_date=end_date,
            operator_id=None,
            report_pos=None,
        )

        opened_orders = self.order_repository.get_opened_orders(
            report_type=self.TYPE_BUSINESS_PERIOD,
            initial_date=initial_date,
            end_date=end_date,
            operator_id=None
        )

        header = self._build_header(initial_date, end_date)

        paid_orders = filter(lambda x: x != "error", paid_orders)
        tables_by_sector = self.table_repository.get_tables_by_sector(paid_orders)
        paid_orders = self.cash_report.fill_orders_sector(tables_by_sector, paid_orders)
        grouped_paid_orders_by_sector = self.cash_report.get_grouped_paid_orders_by_sector(paid_orders)
        body, additional_info = self._build_body(pos_id, paid_orders, opened_orders, grouped_paid_orders_by_sector)

        average_info = self.table_repository.get_sales_average_info(initial_date, end_date, "")
        table_info = self.cash_report.build_table_info(average_info, grouped_paid_orders_by_sector, additional_info)
        body += table_info

        report = header + body

        return report.encode("utf-8")

    def _build_header(
        self,
        initial_date,     # type: datetime
        end_date          # type: datetime
    ):
        # type: (...) -> unicode

        pos_list = "Todos"
        report_name = "Resumo de Vendas"
        operator_name = "Todos"

        header = DOUBLE_LINE_SEPARATOR
        header += u"{0}".format(report_name).center(38)
        header += u"\nLoja.........: " + self.store_id.upper()
        header += u"\nData/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("Periodo", formatted_initial_date, formatted_end_date)
        header += u"Operador.....: " + u"{0}\n".format(operator_name[:20])
        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"
        header += DOUBLE_LINE_SEPARATOR

        return header

    def _build_body(
        self,
        pos_id,                              # type: str
        paid_orders,                         # type: List[object]
        opened_orders,                       # type: List[str]
        grouped_paid_orders_by_sector=None,  # type: dict
    ):
        # type: (...) -> (unicode, List[dict])

        (
            qty_paid_orders,
            qty_discount,
            qty_donation,
            qty_tip,
            value_paid_orders,
            value_discount,
            value_donation,
            value_tip
        ) = self.cash_report.get_paid_order_values(paid_orders)

        qty_opened_orders, value_opened_orders = self.cash_report.get_opened_orders_values(pos_id, opened_orders)

        delivery_paid_orders = filter(lambda x: x.sale_type == 3 and x.delivery_originator, paid_orders)
        qty_delivery_orders, value_delivery_orders = self.cash_report.get_delivery_orders_values(delivery_paid_orders)

        manual_delivery_paid_orders = filter(lambda x: x.sale_type == 3 and not x.delivery_originator, paid_orders)
        (
            qty_manual_delivery_orders,
            value_manual_delivery_orders
        ) = self.cash_report.get_delivery_orders_values(manual_delivery_paid_orders)

        total_quantity_delivery = qty_delivery_orders + qty_manual_delivery_orders
        total_value_delivery = value_delivery_orders + value_manual_delivery_orders

        body = u"Descricao          Qtd    Total\n"
        body += DOUBLE_LINE_SEPARATOR
        sales_without_tip = str_float(value_paid_orders + value_opened_orders)
        body += u"Venda s/ Gorjetas: [{0:>4}] R${1:>10}\n".format(qty_paid_orders, sales_without_tip)

        total_quantity_table = 0
        total_value_table = 0.0
        lines = []
        for sector in sorted(grouped_paid_orders_by_sector, key=lambda x: grouped_paid_orders_by_sector[x]['value']):
            quantity = grouped_paid_orders_by_sector[sector]['quantity']
            value = str_float(grouped_paid_orders_by_sector[sector]['value'])
            total_quantity_table += quantity
            total_value_table += grouped_paid_orders_by_sector[sector]['value']
            lines.append(u"    {0:.<13}: [{1:>4}] R${2:>10}\n".format(sector, quantity, value))

        total_bill_paid = value_paid_orders + value_tip
        total_quantity_counter = qty_paid_orders - total_quantity_table - total_quantity_delivery
        total_value_counter = total_bill_paid - total_value_table - total_value_delivery

        additional_info = [
            {
                "name": "Balcao",
                "value": total_value_counter / total_quantity_counter if total_quantity_counter > 0 else 0,
                "quantity": total_quantity_counter
            },
            {
                "name": "Delivery",
                "value": total_value_delivery / total_quantity_delivery if total_quantity_delivery > 0 else 0,
                "quantity": total_quantity_delivery
            }
        ]

        return body, additional_info
