"""
Processing of provided xml data into orm objects
"""
import datetime
from datetime import date
import logging
from itertools import chain

from src.data.model.schemas.picture_types import PictureTypes
from src.data.model.db.article import *
from src.data.synchronisation.utils import extract_categorical_data, change_image_path
from functools import partial, reduce

logger = logging.getLogger('import_logger')


def process_color(articles: list[dict]) -> set:
    """
    Extracts all unique colors of provided articles amd creates corresponding orm objects
    :param articles: article details
    :return: a set of colors as orm objects
    """
    logger.info("Processing colors")

    def map_to_orm(article: dict):
        categorical = extract_categorical_data('Imp_Color', article)
        if not categorical:
            return None
        return Color(**categorical)

    colors = set(color for color in map(map_to_orm, articles) if color)
    return colors


def process_image(article: dict, image_type: PictureTypes):
    """
    Creates an image orm object from article data
    :param article: dictionary representing article data
    :param image_type: type of image
    :return: Picture orm object
    """
    if 'Imp_ItemColorImages' not in article.keys():
        return None

    images = article['Imp_ItemColorImages']
    result = []
    for image in images:
        if type(image) is str:
            continue
        if image['itemImage'] and image_type == PictureTypes.COLOR_IMAGE:
            continue

        reference = image['Color']['code'] if image_type == PictureTypes.COLOR_IMAGE else article['number']

        result += [Picture(
            ref_id=reference,
            type=str(image_type),
            path=change_image_path(image['uri'], image_type),
        )]
    return result


def process_color_images(articles: list[dict]) -> set:
    """
    Extracts all unique color images from provided articles and creates corresponding orm objects
    :param articles: article details
    :return: a set of color images as orm objects
    """
    logger.info("Processing color images")
    map_to_orm = partial(process_image, image_type=PictureTypes.COLOR_IMAGE)
    pictures = set(reduce(lambda i, k: i if i else [] + k if k else [], map(map_to_orm, articles)))
    return pictures


def process_season(articles: list[dict]) -> set:
    """
    Extracts all unique seasons from provided articles and creates corresponding orm objects
    :param articles: article details
    :return: a set of seasons as orm objects
    """
    logger.info("Processing seasons")
    seasons = set(map(lambda item: Season(**extract_categorical_data('Imp_Collection', item)), articles))
    return seasons


def process_group(articles: list[dict]) -> set:
    """
    Extracts all unique article groups from provided articles and creates corresponding orm objects
    :param articles: article details
    :return: a set of article groups
    """
    logger.info("Processing article group")
    groups = set(map(lambda item: Group(**extract_categorical_data('Imp_ItemGroup', item)), articles))
    return groups


def process_theme(articles: list[dict]) -> set:
    """
    Extracts all unique article theme groups from provided articles and creates corresponding orm objects
    :param articles: article details
    :return: a set of article theme groups
    """
    logger.info("Processing article theme groups")
    theme_groups = set(map(lambda item: ThemeGroup(**extract_categorical_data('Imp_ItemThemeGroup', item)), articles))
    return theme_groups


def process_article(articles: list[dict]):
    """
    Process article data into orm objects
    :param articles:
    :return: a map object of article orm objects
    """

    logger.info("processing of article data")

    def get_brand(article_number):
        # two last number representing brand
        return str(article_number[:2:-1])

    def map_to_orm(article: dict):
        if 'Imp_ItemDeliveryDates' not in article.keys():
            return None

        if type(article['Imp_ItemDeliveryDates']) is list:
            delivery_dates = article['Imp_ItemDeliveryDates'][1]
        else:
            delivery_dates = article['Imp_ItemDeliveryDates']

        delivery_date = delivery_dates['deliveryDate']
        delivery_date_from = date.fromisoformat(delivery_date['dateFrom']['date'])
        delivery_date_to = date.fromisoformat(delivery_date['dateUntil']['date'])

        # todo: missing classification

        return Article(
            article_number=article['number'],
            composition=article['Imp_MaterialComposition1']['code'],
            brand=get_brand(article['number']),
            delivery_date_from=delivery_date_from,
            delivery_date_to=delivery_date_to,
            season_nr=article['Imp_Collection']['code'],
            group_nr=article['Imp_ItemGroup']['code'],
            theme_group_nr=article['Imp_ItemThemeGroup']['code']
        )

    articles = list(filter(None, map(map_to_orm, articles)))
    return articles


def process_article_images(articles: list[dict]) -> set:
    """
    Extracts article images and creates corresponding orm objects
    :param articles: articles
    :return: a set of Picture orm objects
    """
    logger.info("processing of article images")
    map_to_orm = partial(process_image, image_type=PictureTypes.ARTICLE_IMAGE)
    article_images = set(reduce(lambda l, r: l if l else [] + r if r else [], map(map_to_orm, articles)))
    return article_images


def process_article_detail(articles: list[dict]):
    """
    Process article detail data into orm objects
    :param articles: article details
    :return: a map object of article detail orm objects
    """
    logger.info("processing of article detail data")

    def map_to_orm(article: dict):
        has_no_ref_item = '@xsi:nil' in article['ReferenceItem'].keys()
        sales_stopped = article['imp_salesStop'] == "true"
        active_color = article['imp_activeColor'] == "true"

        if has_no_ref_item or sales_stopped or not active_color:
            return None

        # todo: missing classifications
        size_info = article['Imp_Size']
        unlimited_sell = (bool(article['SalesItems'][-1]['classification4'])
                          if type(article['SalesItems']) is list
                          else False)

        gender = (article['SalesItems'][0]['classification2']
                  if type(article['SalesItems']) is list
                  else article['SalesItems']['classification2'])

        return ArticleDetail(
            ean=article['Uoms']['EANs']['id'],
            size=size_info['code'],
            article_number=article['ReferenceItem']['number'],
            size_register=size_info['SizeRegister']['code'],
            color_code=article['Imp_Color']['code'],
            surcharge_code=size_info['surcharge'],
            gender=gender,
            unlimited_sell=unlimited_sell,
            quantity=0
        )

    article_details = list(map(map_to_orm, articles))
    return article_details


def process_pricelist(pricelists: list[dict]) -> list:
    """
    Process price lists data into orm objects
    :param pricelists: price lists
    :return: a map objet of price list orm objects
    """
    logger.info("processing price lists")

    if isinstance(pricelists, dict):
        pricelists = [pricelists]

    def map_to_orm(pricelist: dict):
        code = pricelist['code']
        price_lists = []
        for definition in pricelist['PriceDefinitions']:
            if '#text' not in definition['Item']['eans'][0].keys():
                continue
            price_lists.append(Pricelist(
                code=code,
                price=definition['priceValue'],
                article_detail_ean=definition['Item']['eans'][0]['#text']
            ))
        return price_lists

    pricelists = list(chain(*list(map(map_to_orm, pricelists))))
    return pricelists

