# encoding: utf-8

import json

from datetime import datetime
from typing import (
    Any,
    Dict,
    List,
    Tuple,
)

from old_helper import BaseRepository


class BenefitsOrderRepository(BaseRepository):

    def __init__(self, mb_context, pos_list):
        super(BenefitsOrderRepository, self).__init__(mb_context)

        self.pos_list = pos_list

    def get_benefits_orders_detailed(
            self,
            initial_date,   # type: str
            end_date,       # type: str
            operator_id,    # type: str
            benefits        # type: Dict
    ):
        # type: (...) -> List[Dict[str, Any]]

        query = self._build_query(initial_date, end_date, operator_id)

        def inner_func(conn):
            def get_product_code(
                actions,    # type: Dict
            ):
                # type: (...) -> str

                for item in actions:
                    if 'item' not in item and 'ByGroups' not in item and type(actions[item]) in [list, dict] :
                        return get_product_code(actions[item])
                    elif 'item' in item:
                        return item['item']
                    else:
                        return ""

            def get_product_benefit(
                product_code,   # type: str
                order_id,       # type: str
            ):
                # type: (...) -> (str, str)

                product_benefit_query = """
                    SELECT
                        op.DefaultUnitPrice,
                        p.ProductName,
                        (
                            SELECT
                                SUM(oi.OrderedQty)
                            FROM OrderItem oi
                            WHERE oi.OrderId='{1}' AND oi.PartCode='{0}'
                        ) as Qty
                    FROM productdb.Price OP
                    INNER JOIN productdb.Product p
                    WHERE
                        op.ProductCode = '{0}'
                        AND p.ProductCode = '{0}'
                    LIMIT 1;
                """
                if product_code:
                    for new_row in conn.select(product_benefit_query.format(product_code, order_id)):
                        product_name = new_row.get_entry('ProductName')
                        default_unit_price = new_row.get_entry('DefaultUnitPrice')
                        qty = new_row.get_entry('Qty')
                        return product_name, default_unit_price, qty
                return None, None, None

            benefits_orders = []
            chosen_benefits = [x.get("key") for x in benefits]
            for row in conn.select(query):
                benefit_list_col = json.loads(row.get_entry("BenefitList"))
                benefit_list = json.loads(benefit_list_col.get("benefitList", []))
                for benefit in benefit_list:
                    benefit_name = benefit.get("benefit").get("name")

                    if benefit.get("benefit_applied") and benefit_name in chosen_benefits:
                        benefit_product_code = get_product_code(actions=benefit['benefit']['actions'])
                        benefit_product_name, benefit_product_price, product_qty = get_product_benefit(
                            product_code=benefit_product_code,
                            order_id=row.get_entry("OrderId")
                        )
                        if not benefit_product_name:
                            benefit_product_name = row.get_entry("ProductName")
                            benefit_product_price = float(row.get_entry("ProductUnitPrice"))
                            product_qty = row.get_entry("OrderedQty")

                        session_id = row.get_entry("SessionId")
                        operator = int(session_id.split("user=")[1].split(",")[0]) if session_id else ""
                        discount_type = [self._label_discount_type(a) for a in benefit['benefit']['actions'].items()]
                        benefits_orders.append({
                            "business_period": row.get_entry("BusinessPeriod"),
                            "order_time": row.get_entry("OrderTime"),
                            "operator": operator,
                            "order_id": row.get_entry("OrderId"),
                            "ordered_qty": int(float(product_qty)),
                            "product_unit_price": float(benefit_product_price),
                            "product_name": benefit_product_name,
                            "benefit_total_value": benefit["benefit_total_value"],
                            "discount_type": discount_type[0] or "",
                            "total_gross": float(row.get_entry("TotalGross"))
                        })
            return benefits_orders

        return self.execute_parallel_in_all_databases_returning_flat_list(inner_func, self.pos_list)

    @staticmethod
    def _label_discount_type(
        action,     # type: Tuple
    ):
        # type: (...) -> str

        action_name = action[1].items()[0][0].lower()
        action_value = action[1].items()[0][1]
        action_value = action_value if isinstance(action_value, float) else float(action_value[0].get('percentage', 0))
        action_value = int(action_value) if int(action_value * 10 % 10) == 0 else action_value

        return "$VALUE" if 'amount' in action_name else "$PERCENTAGE ({}%)".format(action_value)

    def get_benefits_orders_consolidated(
            self,
            initial_date,   # type: str
            end_date,       # type: str
            operator_id     # type: str
    ):
        # type: (...) -> List[Dict[str, Dict]]

        query = self._build_query(initial_date, end_date, operator_id)

        def inner_func(conn):
            benefits_orders = []
            for row in conn.select(query):
                benefit_list_col = json.loads(row.get_entry("BenefitList"))
                benefit_list = json.loads(benefit_list_col.get("benefitList", []))

                data = {}
                for benefit in benefit_list:
                    if benefit.get("benefit_applied"):
                        benefit_id = benefit.get("benefit_id")
                        item_unit_price = benefit.get("discount_item_values", 0.0)
                        if item_unit_price:
                            item_unit_price = item_unit_price[str(benefit.get("used_discount_ids")[0])]
                            item_unit_price = item_unit_price[item_unit_price.keys()[0]]
                        data.setdefault(
                            benefit_id,
                            {
                                "quantity": 0,
                                "product_unit_price": item_unit_price,
                                "benefit_total_value": 0.0,
                                "benefit_user_description": (
                                    benefit.get("benefit", {}).get("userDescription")
                                    or benefit_id
                                )
                            }
                        )
                        data[benefit_id]["quantity"] += len(benefit.get("discount_item_values"))
                        data[benefit_id]["benefit_total_value"] += benefit.get("benefit_total_value", 0.0)

                benefits_orders.append(data)

            return benefits_orders

        return self.execute_parallel_in_all_databases_returning_flat_list(inner_func, self.pos_list)

    def _build_query(
            self,
            initial_date,  # type: str
            end_date,  # type: str
            operator_id  # type: str
    ):
        # type: (...) -> str

        initial_date = datetime.strptime(initial_date, "%Y%m%d").date()
        end_date = datetime.strptime(end_date, "%Y%m%d").date()

        conditional = self._QUERY_ORDERS_CONDITIONAL_WITHOUT_OPERATOR.format(
            initial_date=initial_date,
            end_date=end_date
        )

        if operator_id is not None:
            operator_id = int(operator_id)
            conditional += self._QUERY_ORDERS_CONDITIONAL_WITH_OPERATOR.format(
                initial_date=initial_date,
                end_date=end_date,
                operator_id=operator_id
            )

        parts = [
            self._QUERY_ORDERS_PREAMBLE,
            conditional,
            self._QUERY_ORDERS_EPILOGUE
        ]
        return "".join(parts)

    _QUERY_ORDERS_PREAMBLE = """
        SELECT
            o.OrderId,
            o.TotalGross,
            o.SessionId,
            o.BusinessPeriod,
            od.OrderTime,
            oi.OrderedQty,
            ocp.Value AS BenefitList,
            p.ProductName,
            COALESCE(
                oi.OverwrittenUnitPrice,
                op.DefaultUnitPrice, 
                0.0
            ) AS ProductUnitPrice
        FROM OrderItem oi
        INNER JOIN Orders o ON o.OrderId = oi.OrderId
        INNER JOIN OrderCustomProperties ocp ON ocp.OrderId = o.OrderId
        INNER JOIN OrderPrice op ON op.OrderId = oi.OrderId
        INNER JOIN productdb.Product p ON p.ProductCode = oi.PartCode
        INNER JOIN (SELECT OrderId, 
                           strftime('%H:%M:%S', max(Timestamp), 'localtime') OrderTime
				      FROM OrderStateHistory
					 WHERE StateId = 5
					 GROUP BY OrderId
					) od on o.OrderId = od.OrderId
    """

    _QUERY_ORDERS_CONDITIONAL_WITH_OPERATOR = """
            AND o.SessionId LIKE '%user={operator_id:d},%'
    """

    _QUERY_ORDERS_CONDITIONAL_WITHOUT_OPERATOR = """
        WHERE o.StateId = 5 -- PAID
            AND ocp.Key = 'BENEFIT_LIST'
            AND o.BusinessPeriod BETWEEN '{initial_date:%Y%m%d}' AND '{end_date:%Y%m%d}'
    """

    _QUERY_ORDERS_EPILOGUE = """
        GROUP BY oi.OrderId
        ORDER BY o.OrderId, o.BusinessPeriod, od.OrderTime, p.ProductName
    """
