import json

from mbcontextmessagehandler import MbContextMessageBus
from mwhelper import BaseRepository
from typing import Dict

from persistence import Connection

from model.services import ProductInfoServiceModel


class ProductInfoService(ProductInfoServiceModel, BaseRepository):

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

        super(ProductInfoService, self).__init__(message_bus.mbcontext)
        self.message_bus = message_bus

    def get_product_info(
        self,
        language    # type: str
    ):
        # type: (...) -> str

        def inner_func(
            conn    # type: Connection
        ):
            # type: (...) -> str

            product_description = self._get_all_products(conn=conn)
            self._fill_description(
                descriptions=product_description,
                language=language,
                conn=conn
            )
            custom_param_dict = self._get_custom_params(conn=conn)
            self._fill_description_custom_params(
                descriptions=product_description,
                custom_params=custom_param_dict
            )
            return product_description

        product_description_json = self.execute_with_connection(
            method=inner_func,
            service_name="Persistence"
        )

        return product_description_json

    @staticmethod
    def _get_all_products(
        conn    # type: Connection
    ):
        # type: (...) -> Dict

        query = """SELECT ProductCode, ProductName FROM Product"""
        cursor = conn.select(sql=query)

        products = {}
        for row in cursor:
            product_id = str(row.get_entry('ProductCode'))
            name = row.get_entry('ProductName')
            products[product_id] = {
                "nutritionalInfo": "",
                "allergens": "",
                "description": "",
                "briefDescription": "",
                "displayName": name
            }

        return products

    @staticmethod
    def _fill_description(
        descriptions,   # type: Dict
        language,       # type: str
        conn            # type: Connection
    ):
        # type: (...) -> None

        query = """
                    SELECT
                        PD.ProductCode AS ProductCode,
                        PD.ProductDescription AS ProductDescription,
                        DES.Description AS Description
                    FROM ProductDescriptions PD
                    JOIN Descriptions DES ON DES.DescrId=PD.DescrId
                    WHERE DES.Language = "{}"
                """.format(language)

        cursor = conn.select(sql=query)

        for row in cursor:
            product_id = str(row.get_entry('ProductCode'))
            description_name = row.get_entry('Description')
            description_content = row.get_entry('ProductDescription')
            if description_name == "nutritionalInfo":
                description_content = json.loads(description_content)

            descriptions[product_id].update({description_name: description_content})

    @staticmethod
    def _get_custom_params(
        conn    # type: Connection
    ):
        # type: (...) -> Dict

        query = """
            SELECT 
                ProductCode, 
                CustomParamValue AS ProductDescription,
                CustomParamId AS Description
            FROM ProductCustomParams 
            WHERE CustomParamId in ('PublicName', 'ProductBriefDescription')
        """

        cursor = conn.select(query)

        custom_param_dict = {}
        for row in cursor:
            product_id = row.get_entry('ProductCode')
            description_type = row.get_entry('Description')
            description_value = row.get_entry('ProductDescription')
            if product_id not in custom_param_dict.keys():
                custom_param_dict[product_id] = {
                    "PublicName": "",
                    "ProductBriefDescription": ""
                }

            custom_param_dict[product_id].update({description_type: description_value})

        return custom_param_dict

    @staticmethod
    def _fill_description_custom_params(
        descriptions,   # type: Dict
        custom_params   # type: Dict
    ):
        # type: (...) -> None

        for product_id in custom_params:
            if product_id not in descriptions.keys():
                continue

            brief_description = custom_params[product_id]["ProductBriefDescription"]
            descriptions[product_id]["briefDescription"] = brief_description

            display_name = custom_params[product_id]["PublicName"]
            if display_name:
                descriptions[product_id]["displayName"] = display_name
