from logging import Logger

from model.services import CouponServiceModel
from mbcontextmessagehandler import MbContextMessageBus
from mwhelper import BaseRepository
from persistence import Connection
from sysactions import get_podtype, get_model
from typing import List, Any


class CouponService(CouponServiceModel, BaseRepository):

    def __init__(self, message_bus, logger):
        # type: (MbContextMessageBus, Logger) -> None

        super(CouponService, self).__init__(message_bus.mbcontext)
        self.message_bus = message_bus
        self.logger = logger
        self.electronic_payments_id = None

    def get_prices(self, posid):
        # type: (int) -> Any

        part_data = self._get_part_data()
        tag_data = self._get_tag_data()
        price_data = self._get_price_data(get_podtype(get_model(posid)) == "DL")

        prices = {}
        for product in price_data:
            if '.' not in product:
                prices[product] = price_data[product]

        def try_to_get_price(part_code, part_context):
            return price_data.get("{}.{}".format(part_context, part_code), price_data.get(part_code)) or 0

        def get_price_by_product_type_recursively(part_enters, produc):
            price = try_to_get_price(produc, '1')
            for part_params in part_enters:
                product_type = part_params["product_type"]
                part_code = part_params["part_code"]

                full_context = "{}.{}".format(produc, part_code)
                if product_type == 0:  # product
                    price += try_to_get_price(part_code, produc)
                elif product_type == 1:  # option
                    default_part = tag_data.get(produc.split('.')[-1], {}).get(part_code)
                    if default_part:
                        price += try_to_get_price(default_part, full_context)
                    else:
                        price += try_to_get_price(part_params["product_codes"][0], full_context)
                elif product_type == 2 and part_code in part_data:
                    price += get_price_by_product_type_recursively(part_data[part_code], full_context)
            return price

        for product, part_entries in part_data.iteritems():
            prices[product] = get_price_by_product_type_recursively(part_entries, product)

        return prices

    def _get_part_data(self):
        def inner_func(conn):
            # type: (Connection) -> List[int]

            part_data = {}
            cursor = conn.select("""
                                    SELECT
                                        part.productCode,
                                        part.partcode,
                                        part.defaultQty,
                                        params.ProductType,
                                        class.ProductCodes
                                    FROM ProductPart part
                                    JOIN ProductKernelParams params
                                        ON params.ProductCode = part.PartCode
                                    LEFT JOIN (
                                        SELECT
                                            classcode,
                                            group_concat(ProductCode) as ProductCodes
                                        FROM ProductClassification
                                        GROUP BY ClassCode
                                    ) class
                                         ON class.ClassCode = part.PartCode
                                    WHERE part.MinQty > 0
                                    ORDER BY part.ProductCode;
                                 """)
            for row in cursor:
                part_data.setdefault(row.get_entry(0), []).append({
                    "part_code": row.get_entry(1),
                    "default_qty": row.get_entry(2),
                    "product_type": int(row.get_entry(3)),
                    "product_codes": (row.get_entry(4) or "").split(',')
                })
            return part_data

        result_part_data = self.execute_with_connection(inner_func, service_name="Persistence")
        return result_part_data

    def _get_tag_data(self):
        def inner_func(conn):
            # type: (Connection) -> List[int]

            tag_data = {}
            cursor = conn.select("""
                        SELECT substr(Tag, 12), ProductCode
                        FROM ProductTags
                        WHERE Tag LIKE 'HasOptions=%'
                    """)
            for row in cursor:
                entry_data = {}
                for option_data in row.get_entry(0).split('|'):
                    option_code, default_option = option_data.split('>')
                    if not default_option:
                        continue
                    entry_data[option_code] = default_option
                tag_data[row.get_entry(1)] = entry_data
            return tag_data

        result_tag_data = self.execute_with_connection(inner_func, service_name="Persistence")
        return result_tag_data

    def _get_price_data(self, is_delivery):
        def inner_func(conn):
            # type: (Connection) -> List[int]

            price_data = {}
            cursor = conn.select("""
                    SELECT ProductCode, Context, COALESCE(CAST(DefaultUnitPrice AS FLOAT), 0)
                    FROM Price
                    WHERE PriceListId = '{}' AND DATETIME('now') BETWEEN ValidFrom AND ValidThru;
                    """.format("DL" if is_delivery else "EI"))
            for row in cursor:
                product_code, context, price = row
                if context and context != '1':
                    item_id = "{}.{}".format(context, product_code)
                else:
                    item_id = product_code
                price_data[item_id] = float(price)
            return price_data

        result_price_data = self.execute_with_connection(inner_func, service_name="Persistence")
        return result_price_data
