import logging
from itertools import chain

from src.data.model.db.partner_agent import *

logger = logging.getLogger('import_logger')


def extract_customer_data(partner: dict):
    """
    Extracts customer data from partner
    :param partner:
    :return: allow order flag of partner,
            shipping terms as tupel of (code, name),
            surcharge code,
            payment data as tupel of (code, name),
            price listing,
    """
    # means partner has no customer data associated with hin
    if 'Customer' not in partner.keys() or (type(partner['Customer']) is not list or len(partner['Customer']) < 2):
        return None

    customer = partner['Customer'][1]
    # allow order flag
    allow_order = customer['allowOrders']

    # shipping terms
    shipping_term = customer['deliveryDataInfo']['ShippingTerms']
    shipping_code, shipping_name = 0, ""
    if "code" in shipping_term.keys() and "description" in shipping_term.keys():
        shipping_code, shipping_name = shipping_term['code'], shipping_term['description']

    # surcharge
    surcharge_listing = customer['Imp_SizeSurchargeListing']
    surcharge_code = 0
    if "code" in surcharge_listing.keys():
        surcharge_code = surcharge_listing['code']

    # payment data
    payment_code, payment_description = None, None
    if 'PartnerAccountingData' in partner.keys():
        accounting_data = partner['PartnerAccountingData']
        if type(accounting_data) is list:
            accounting_data = accounting_data[0]
        sales_terms = accounting_data['SalesPaymentTerms']
        payment_code, payment_description = 0, ""
        if "code" in sales_terms.keys() and "description" in sales_terms.keys():
            payment_code, payment_description = sales_terms['code'], sales_terms['description']

    # todo: missing pricelisting and classification 1/2 of partner

    return (allow_order,
            (shipping_code, shipping_name),
            surcharge_code,
            (payment_code, payment_description))


def process_shipping_and_payment_terms(partner_data: dict):
    """
    Extracts unique shipping and payment terms from partner data
    and creates corresponding orm objects of it
    :param partner_data:
    :return: tupel(shipping_terms, payment_terms) as mapping object containing
    """
    logger.info("extracting used shipping and payment terms from partner data")
    shipping_terms, payment_terms = set(), set()

    # extract data from each of partner
    for partner in partner_data:
        customer_data = extract_customer_data(partner)
        if customer_data:
            _, shipping_term, _, payment_term = customer_data
            shipping_terms.add(shipping_term)
            payment_terms.add(payment_term)

    shipping_terms = list(
        map(lambda term: Shipping(code=term[0], designation=term[1]) if term[1] else None, shipping_terms))
    payment_terms = list(
        map(lambda term: Payment(code=term[0], designation=term[1]) if term[1] else None, payment_terms))

    payment_terms = list(filter(None, payment_terms))
    shipping_terms = list(filter(None, shipping_terms))

    return shipping_terms, payment_terms


def process_partner_relation(partner_relation: dict):
    """
    Maps xml data of partner agent relations given by a dict to the list of orm objects of partner agent relations
    :param partner_relation: xml parsed to dict
    :return: a list of orm object representing partner agent relations
    """
    logger.info("processing of partner agent relations")

    def map_to_orm(agent: dict):
        """
        Reads partner numbers of partners which are associated with selected partner
        :param agent: dict containing
        :return: numbers of to agent related partners
        """
        customers = agent['Customer']
        partner_num = agent['number']
        relations_result = []
        for customer in customers:
            if type(customer) is str:
                continue
            relations = customer['PartnerRelations']
            for relation in relations:
                if 'name' not in relation['Type'].keys() or relation['Type']['name'] != 'Salesrepresentative':
                    continue
                relations_result += [
                    AgentPartner(
                        partner_num=partner_num,
                        agent_num=relation['Target']['number']
                    )
                ]
        return relations_result

    partner_agent_relations = list(chain(*list(map(map_to_orm, partner_relation))))
    return partner_agent_relations


def process_partner(partner_dict: dict):
    """
    Maps xml data of partners given by a dict to the list of orm objects of partner
    :param partner_dict: xml parsed to dict
    :return: a list of orm objects representing partner
    """
    logger.info("processing of partner data")

    def map_to_orm(partner: dict):
        telephone = email = ""
        for communication in partner['CommunicationData']:
            if type(communication) is str:
                continue
            if communication['Method']['description'] == "Email":
                email = communication['normalizedCommData']
            elif communication['Method']['description'] == "Telefone":
                telephone = communication['normalizedCommData']
            else:
                continue

        customer_data = extract_customer_data(partner)
        if not customer_data:
            return None

        allow_order, shipping_term, surcharge_code, payment_term = customer_data
        shipping_code, _ = shipping_term
        payment_code, _ = payment_term

        street = partner['addressData']['street']
        if not street:
            street = ""

        if payment_code == 0 or not payment_code:
            payment_code = 13

        partner = Partner(
            partner_num=partner['number'],
            name=partner['name'],
            username=partner['number'],
            street=street,
            # postalcode=partner['addressData']['postalCode'],
            city=partner['addressData']['city'],
            land=partner['addressData']['Country']['description'],
            iso_code=partner['addressData']['Country']['isoCode'],
            email=email,
            password_hash="ghghghghg", # todo: password???
            telefon=telephone,
            shipping_code=shipping_code,
            payment_code=payment_code,
            surcharge_code=surcharge_code,
            allow_orders=allow_order == "true")

        return partner

    partners = list(filter(lambda f: f is not None, map(map_to_orm, partner_dict)))
    return partners


def process_sales_order_type_partner(partner_sales_order_type: dict):
    """
    Maps xml data of partners sales order type given by a dict to the list of rom object of partner sales order type
    :param partner_sales_order_type: xml parsed to dict
    :return: a list of orm objects representing partner sales order type relation
    """
    logger.info("processing of partners sales order type to price list relation")

    def map_to_orm(partner: dict):
        partner_num = partner['number']
        sales_order_type_partner = []
        for customer in partner['Customer']:
            if type(customer) is str:
                continue
            if 'Imp_CustomerPossibleOrderTypes' not in customer.keys():
                continue
            for order_pricelist in customer['Imp_CustomerPossibleOrderTypes']:
                if type(order_pricelist) is str:
                    continue
                sales_order_type_partner += [
                    SalesOrderTypePartner(
                        code=order_pricelist['SalesOrderType']['code'],
                        partner_num=partner_num,
                        pricelist_code=order_pricelist['PriceListing']['code']
                    )
                ]
        return sales_order_type_partner

    partner_sales_order_types = list(chain(*list(map(map_to_orm, partner_sales_order_type))))
    return partner_sales_order_types
