import csv

from sqlalchemy.orm import Session
from loguru import logger
from src.config import settings
from src.data.imports.schema.partner import parse as parse_partner, PartnerTypeSub as Import_Partner
from src.data.imports.schema.partner_relation import parse as parse_relation, PartnerTypeSub as Import_PartnerRelation
from src.data.imports.schema.partner_sales_order_type import parse as parse_sales_order, \
    PartnerTypeSub as Import_PartnerSalesOrder
from src.data.model.db.article import Pricelist
from src.data.model.db.connection import get_sync_engine
from src.data.model.db.partner_agent import Partner, Shipping, Payment, SalesOrderTypePartner, AgentPartner, Agent, \
    SalesOrderType
import hashlib

from src.data.synchronisation.utils import extract_categorical_data, merge_with_current_db_state, parse
from src.utils.jwt import hash_password

""""
SELECT 
salesordertype.pricelisting,
listing_listung.listing,
listing_listung.vk_list
from salesordertype
join
listing_listung
on salesordertype.pricelisting = listing_listung.listung
where salesordertype.partner_id = '".$_SESSION['kunde']."'
and salesordertype.salesordertype = '".$_POST['ordertype']."'
and listing_listung.listing <= '400'
"""


def process_sales_order(
        partners: set[int],
        price_lists: list[int],
        sales_orders: set[int],
        partner_sales_order: list[Import_PartnerSalesOrder]
):
    logger.info("importing sales order for partner")
    mapping = price_listing_price_list_map()
    sales_order_types = []
    for partner in partner_sales_order:
        for customer in partner.Customer:
            for sales_order in customer.Imp_CustomerPossibleOrderTypes:

                if partner.number not in partners:
                    continue

                sales_order_nr = int(sales_order.SalesOrderType.code)
                if sales_order_nr not in sales_orders:
                    continue

                price_listing = int(sales_order.PriceListing.code)
                if price_listing not in mapping.keys():
                    continue

                price_list = mapping[price_listing]
                if price_list not in price_lists:
                    continue

                sales_order_types.append(
                    SalesOrderTypePartner(
                        code=sales_order.SalesOrderType.code,
                        partner_num=partner.number,
                        pricelist_code=price_list
                    )
                )

    return sales_order_types


def process_partner_relation(
        partners_numbers: set[int],
        agents_numbers: set[int],
        partner_relation_import: list[Import_PartnerRelation]
):
    logger.info("Importing partners relations")

    return [
        AgentPartner(
            partner_num=partner.number,
            agent_num=relation.Target.number
        )
        for partner in partner_relation_import
        for customer in partner.Customer
        for relation in customer.PartnerRelations
        if relation.Type.name == "Salesrepresentative"
           and int(relation.Target.number) in agents_numbers
           and partner.number in partners_numbers
    ]


def process_partner(partners_import: list[Import_Partner], passwords: dict):
    partners: list[Partner] = []
    logger.info("Importing partners")

    for partner in partners_import:
        if len(partner.Customer) < 2:
            continue

        telephone = email = ""
        for communication in partner.CommunicationData:
            if communication.Method.description == "Email":
                email = communication.normalizedCommData
            elif communication.Method.description == "Telefone":
                telephone = communication.normalizedCommData
            else:
                continue

        customer_data = partner.Customer[1]
        classification1, classification2 = customer_data.classification1, customer_data.classification2
        payment = None
        if len(partner.PartnerAccountingData) > 1:
            payment = Payment(**extract_categorical_data(partner.PartnerAccountingData[0].SalesPaymentTerms))
        if not payment or not payment.designation:
            payment = Payment(code="-1", designation="unknown")

        surcharge_code = customer_data.Imp_SizeSurchargeListing.code
        if not surcharge_code:
            surcharge_code = 0

        if customer_data.deliveryDataInfo.ShippingTerms.description:
            shipping = Shipping(**extract_categorical_data(customer_data.deliveryDataInfo.ShippingTerms))
        else:
            shipping = Shipping(code="-1", designation="unknown")

        #  take password hash or generate a new password
        if partner.number in passwords.keys():
            password = passwords[partner.number]
        else:
            password = hash_password(partner.number)

        partners.append(
            Partner(
                partner_num=partner.number,
                name=partner.name,
                username=partner.number,
                street=partner.addressData.street,
                city=partner.addressData.city,
                land=partner.addressData.Country.description,
                iso_code=partner.addressData.Country.isoCode,
                email=email,
                classification1=classification1,
                classification2=classification2,
                password_hash=password,
                telefon=telephone,
                shipping=shipping,
                payment=payment,
                surcharge_code=surcharge_code,
                allow_orders=customer_data.allowOrders
            )
        )
    return partners


def import_partner_data(session: Session):
    partner_data = parse(settings.import_files_xml_partner, parse_partner)
    partner_relation_imported = parse(settings.import_files_xml_partner_relation, parse_relation)
    partner_sales_order_imported = parse(settings.import_files_xml_partner_sales_order_types, parse_sales_order)

    passwords = {
        partner.partner_num: partner.password_hash
        for partner in session.query(Partner).all()
    }

    partners = process_partner(partner_data.Partner, passwords)
    price_lists = set(map(lambda p: p.code, session.query(Pricelist).all()))
    agents = set(map(lambda a: a.agent_num, session.query(Agent).all()))
    partner_numbers = set(map(lambda p: p.partner_num, partners))
    sales_orders = set(map(lambda s: int(s.code), session.query(SalesOrderType).all()))

    partner_sales_order = process_sales_order(partner_numbers, list(price_lists), sales_orders,
                                              partner_sales_order_imported.Partner)
    partner_relations = process_partner_relation(partner_numbers, agents, partner_relation_imported.Partner)

    print(sales_orders)
    merge_with_current_db_state(
        session,
        partners,
        partner_relations,
        partner_sales_order
    )

    session.commit()
    session.close()


def price_listing_price_list_map():
    filename = "vertreter_listing_listung.csv"

    def read_csv():
        with open(filename, 'r') as file:
            for row in csv.DictReader(file, ['', 'Listing', 'Listung', '']):
                yield row

    return {
        int(row['Listung']): listing
        for row in read_csv() if (listing := int(row['Listing'])) <= 400
    }
