import re
from sqlite3 import Connection

from dbapitemplate import DbApiTemplate
from production.model import Course
from typing import (
    Dict,
    List,
    Optional,
)

from production.repository import ProductRepository as ProductionProductRepository


class ProductRepository(ProductionProductRepository):

    def __init__(
        self,           # type: ProductRepository
        connection,     # type: Connection
    ):
        # type: (...) -> None

        self.connection = DbApiTemplate(connection)
        self.all_part_code_jit_lines = None

    def get_part_codes_of_jit_lines(
        self,       # type: ProductRepository
        jit_lines,  # type: List[str]
    ):
        # type: (...) -> List[int]

        if len(jit_lines) == 0:
            return []

        part_codes = []
        all_products_jit_lines = self.get_all_part_code_jit_lines()
        for key, value in all_products_jit_lines.items():
            current_jit_lines = re.findall('(\\w+)', value)
            if any(x in jit_lines for x in current_jit_lines):
                part_codes.append(key)

        return part_codes

    def get_all_part_code_jit_lines(
        self,   # type: ProductRepository
    ):
        # type: (...) -> Dict[int, str]

        products_jit_lines = {}

        def mapper(row):
            products_jit_lines[int(row["ProductCode"])] = row["JITLines"].encode("utf-8")

        if self.all_part_code_jit_lines is None:
            self.connection.execute(
                sql=get_part_codes_of_jit_lines_query,
                mapper=mapper,
            )
            self.all_part_code_jit_lines = products_jit_lines

        return self.all_part_code_jit_lines

    def get_items_to_show_on_jit(
        self,               # type: ProductRepository
        allowed_jit_lines,  # type: List
    ):
        # type: (...) -> List[int]

        if not allowed_jit_lines:
            return []

        part_codes = []

        def mapper(row):
            part_codes.append(int(row["ProductCode"]))

        formatted_jit_lines = ["JITLines LIKE '%{}%'".format(s) for s in allowed_jit_lines]
        placeholders = " OR ".join(formatted_jit_lines)
        query = get_show_on_kitchen_by_jit.format(placeholders=placeholders)

        self.connection.execute(
            sql=query,
            mapper=mapper,
        )
        return part_codes

    def get_prep_times(
        self,    # type: ProductRepository
    ):
        # type: (...) -> Optional[Dict[int, int]]

        prep_time_dict = {}

        def mapper(row):
            part_code = int(row["ProductCode"])
            prep_time = int(row["CustomParamValue"])
            prep_time_dict[part_code] = prep_time

        self.connection.execute(
            sql=get_prep_time_query,
            mapper=mapper,
        )
        return prep_time_dict

    def get_courses_products(
        self,   # type: ProductRepository
    ):
        # type: (...) -> Dict[int, List[int]]

        course_dict = {}

        def mapper(row):
            part_code = int(row["ProductCode"])
            course = row["CustomParamValue"].encode("utf-8")
            if course not in course_dict:
                course_dict[course] = []

            course_dict[course].append(part_code)

        self.connection.execute(
            sql=course_query,
            mapper=mapper,
        )
        return course_dict

    def get_not_show_on_kitchen(
        self,   # type: ProductRepository
    ):
        # type: (...) -> List[int]

        part_codes = []

        def mapper(row):
            part_codes.append(int(row["ProductCode"]))

        self.connection.execute(
            sql=not_show_on_kitchen_query,
            mapper=mapper,
        )
        return part_codes

    def get_no_jit_part_codes(
        self,   # type: ProductRepository
    ):
        # type: (...) -> List[int]

        part_codes = []

        def mapper(row):
            part_codes.append(int(row["ProductCode"]))

        self.connection.execute(
            sql=no_jit_query,
            mapper=mapper,
        )
        return part_codes

    def get_cfh_items(
        self,   # type: ProductRepository
    ):
        # type: (...) -> List[int]

        part_codes = []

        def mapper(row):
            part_codes.append(int(row["ProductCode"]))

        self.connection.execute(
            sql=cfh_items_query,
            mapper=mapper,
        )
        return part_codes

    def get_all_courses(
        self,   # type: ProductRepository
    ):
        # type: (...) -> List[Course]

        courses = []

        def mapper(row):
            course = Course(
                course_id=int(row["Id"]),
                name=row["Name"].encode("utf-8"),
                description=row["Description"].encode("utf-8"),
                abbreviation=row["Abbreviation"].encode("utf-8"),
                delay=int(row["Delay"]),
            )
            courses.append(course)

        self.connection.execute(
            sql=get_courses_query,
            mapper=mapper,
        )
        return courses

    def get_products_with_custom_params(
        self,               # type: ProductRepository
        custom_params,      # type: List[str]
    ):
        # type: (...) -> Dict[str, Dict[int, str]]

        product_custom_params = {}

        def mapper(row):
            product_code = int(row["ProductCode"])
            custom_param_id = row["CustomParamId"]
            if custom_param_id not in product_custom_params:
                product_custom_params[custom_param_id] = {}

            product_custom_params[custom_param_id][product_code] = (row["CustomParamValue"] or "").encode("utf-8")

        custom_params = ', '.join('"{0}"'.format(w) for w in custom_params)
        self.connection.execute(
            sql=get_products_with_custom_params.format(customParams=custom_params),
            mapper=mapper,
        )
        return product_custom_params


get_part_codes_of_jit_lines_query = """
SELECT ProductCode, JITLines
FROM
Production
WHERE (Expiration IS NULL OR Expiration > DATETIME('now')) AND JITLInes IS NOT NULL
"""

get_show_on_kitchen_by_jit = """
SELECT *
FROM Production
WHERE ({placeholders})
AND ShowOnKitchen = 1
"""

get_prep_time_query = """
SELECT ProductCode, CustomParamValue
FROM ProductCustomParams
WHERE CustomParamId = 'PrepTime' AND CustomParamValue IS NOT NULL
"""


course_query = """
select ProductCode, CustomParamValue from ProductCustomParams where CustomParamId = 'Course'
"""

not_show_on_kitchen_query = """
SELECT p.ProductCode
FROM Production p
JOIN ProductKernelParams pkp ON pkp.ProductCode = p.ProductCode
JOIN ProductType pt ON pt.TypeId = pkp.ProductType
WHERE ShowOnKitchen == 0 AND pt.TypeDescr <> 'TYPE_OPTION'
"""

no_jit_query = """
select ProductCode from Production where JITLines is NULL and ShowOnKitchen = 1
"""

cfh_items_query = """
 SELECT ProductCode FROM ProductTags
WHERE LOWER(Tag) = 'cfh=true'
UNION
SELECT ProductCode FROM ProductCustomParams
WHERE LOWER(CustomParamId) = 'cfh' AND LOWER(CustomParamValue) = 'true'
"""


get_courses_query = """
SELECT Id, Name, Description, Abbreviation, Delay FROM Courses
"""

get_products_with_custom_params = """
    SELECT
        ProductCode,
        CustomParamId,
        CustomParamValue
    FROM ProductCustomParams
    WHERE CustomParamId IN ({customParams})
"""
