#if DEBUG
#define skipDirectStore
#define skipOnlineBestand
#define skipPdfKorrektur
//#define skipZubehoer
#define skipFilter
//#define TestBesonderenArtikel
#endif
using Serilog;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WK5.Core;
using WK5.Core.Email;
using WK5.Core.Models;
using WK5.Core.OpenCart;
using WK5.Core.OpenCart.Karley;
using WK5.Core.Services;

namespace ArtikelUpdateOpencart
{
    internal class OnlineBestand
    {
        private EmailController _emailController;
        private readonly ILogger _logger;
        private List<string> _angeboteGelöscht = new();

        public OnlineBestand(ILogger logger)
        {
            _emailController = new EmailController();
            _logger = logger;
        }

        public async Task RunAsync()
        {
            using FbController2 fbController = new FbController2();
            using MySqlController2 mySqlController = new MySqlController2();

            _logger.Information("OnlineBestand synchronisation gestartet...");
            //OnlineArtikel für den Direct Store Aktualisieren, müssen in W4 als Internetartikel gekennzeichnet sein
#if !skipDirectStore
            try
            {
                await RunDirectShopUpdateAsync(fbController, mySqlController);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
                await EmailController.FehlerMailSendenAsync("Fehler beim Update des Directshops", ex.ToString());
            }
#endif

#if !skipOnlineBestand
            await OnlineBestandUpdateAsync(fbController, mySqlController);
#endif
#if !skipPdfKorrektur
            await PdfKorrekturAsync(mySqlController);
#endif
#if !skipZubehoer
            await ZubehörUpdateAsync(mySqlController, fbController);
#endif
#if !skipFilter
            await FilterUpdateAsync(mySqlController, fbController);
#endif
        }

        private async Task RunDirectShopUpdateAsync(FbController2 fbController, MySqlController2 mysqlController)
        {
            _logger.Information("Update des Directshops gestartet...");
            int bearbeitet = 0;
#if !skipDirectProducts
            #region Directshop Artikel
            //Model und Product_ID aus dem Shop holen , für spätere zuordnung. 
            DataTable OnlineArtikel = await mysqlController.SelectDataAsync("select product_id, model from product");

            //Alle Internetartikel aus W4 holen
            DataTable W4Artikel = await fbController.SelectDataAsync("SELECT ARTI_A_NR, ARTI_N_VK5 FROM ARTIKEL WHERE ARTI_L_INTERNET = 'Y' AND ARTI_N_VK5 > 0");

            //Erst alle Produkte aus Shop 10(direct) löschen - Um Liste Aktuell zu halten -
            await mysqlController.QueryAsync("DELETE FROM product_to_store WHERE store_id = 10 AND product_id NOT IN (SELECT p.product_id FROM `product` p WHERE p.`status` = 0)");


            //Dann Artikel neu hinzufügen
            foreach (DataRow artikel in W4Artikel.Rows)
            {
                bearbeitet++;
                Console.Write("\r[{0}/{1}]", bearbeitet, W4Artikel.Rows.Count);
#if TestBesonderenArtikel
                if (artikel["ARTI_A_NR"].ToString().ToUpper() != "33873-10")
                    continue;
#endif

                if (Convert.ToDouble(artikel["ARTI_N_VK5"].ToString()) > 0)
                {
                    int id = (from DataRow dr in OnlineArtikel.Rows
                              where (string)dr["model"].ToString().ToUpper() == artikel["ARTI_A_NR"].ToString().ToUpper()
                              select (int)dr["product_id"]).FirstOrDefault();

                    if (id != 0)
                    {
                        //Artikel dem Store zuweisen
                        mysqlController.AddParameter("@product_id", id);
                        await mysqlController.QueryAsync("INSERT IGNORE INTO product_to_store values(@product_id, 10)");
                    }
                }
            }



            Console.WriteLine();
            _logger.Information("Artikel des Directshops erfolgreich upgedatet!");
            #endregion
#endif
            #region Direct-Shop Kategorien aktualisieren
            _logger.Information("Kategorien des Directshops updaten...");
            // Klein: 19.04.2018 - Die Kategorien für den Direct-Shop aktualisieren, sodass im Direct Shop nur Kategorien mit aktiven Artikeln 
            // Schritt 1: Alle Kategorien aus dem Direct-Shop entfernen
            await mysqlController.QueryAsync("DELETE FROM `category_to_store` WHERE `store_id` = 10");


            // Schritt 2: Alle Kategorien mit Produkte im Direct-Shop holen
            DataTable directKategorien = new DataTable();

            try
            {
                directKategorien = await mysqlController.SelectDataAsync(@"SELECT ptc.`category_id` AS CATEGORY_ID, COUNT(pts.`product_id`) AS PRODUKTANZAHL FROM `product_to_store` pts
                    LEFT JOIN `product_to_category` ptc ON(ptc.`product_id` = pts.`product_id`)
                    WHERE pts.`store_id` = 10
                    GROUP BY ptc.`category_id`");
            }
            catch (Exception ex)
            {
                await EmailController.FehlerMailSendenAsync("Direct -Shop Kategorien aktualisieren Fehler", $"Fehler beim Direct-Shop Kategorien aktualisieren, programm wurde geschlossen. {ex}");
                Environment.Exit(0);
            }


            //Hier fehlten natürlich die Hauptkategorien, damit die auch angezeigt werden, da diese t.w. keine Artikel haben!

            DataTable eltern = await mysqlController.SelectDataAsync(@"select DISTINCT path_id AS CATEGORY_ID from category_path 
WHERE category_id in (SELECT ptc.`category_id` FROM `product_to_category` ptc LEFT JOIN `product_to_store` pts ON(ptc.`product_id` = pts.`product_id`) WHERE pts.`store_id` = 10 GROUP BY ptc.`category_id`) 
order by category_id ASC");


            //Ley 29.04.2018 - Überprüfen ob wir die REihen schon drin haben, spart!
            foreach (DataRow reihe in eltern.Rows)
            {

                bool enthalten = false;
                foreach (DataRow directReihe in directKategorien.Rows)
                {
                    if (directReihe["CATEGORY_ID"].ToString() == reihe["CATEGORY_ID"].ToString())
                    {
                        enthalten = true;
                    }
                }

                if (!enthalten)
                {
                    directKategorien.Rows.Add(reihe["CATEGORY_ID"].ToString(), 0); // 0 ist die Menge, da die Kat keine Produkte sondern nur Unterkats hat
                }
            }

            // Schritt 3: Kategorien für den Direct-Shop online einfügen
            List<string> aktivierteKategorien = new List<string>();
            bearbeitet = 0;
            foreach (DataRow row in directKategorien.Rows)
            {
                int category_id = row.Field<int>("CATEGORY_ID");
                bearbeitet++;
                Console.Write("\r[{0}/{1}]", bearbeitet, directKategorien.Rows.Count);
                aktivierteKategorien.Add(category_id.ToString());

                try
                {
                    mysqlController.AddParameter("@CATEGORY_ID", category_id);
                    await mysqlController.QueryAsync("INSERT INTO `category_to_store` VALUES (@CATEGORY_ID, 10)");
                }
                catch (Exception ex)
                {
                    await EmailController.FehlerMailSendenAsync("Kategorien für den Direct-Shop online einfügen Fehler", $"Fehler beim Kategorien für den Direct-Shop online einfügen, programm wurde geschlossen.{ex}");
                    Environment.Exit(0);
                }



            }

            Console.WriteLine();
            await mysqlController.QueryAsync("delete from category_to_store where store_id = 10 AND category_id not in (select distinct category_id from product_to_category where product_id in (select distinct product_id from product_to_store where store_id = 10))");


            _logger.Information("Kategorien des Directshops erfolgreich upgedatet!");
            #endregion
            #region  Direct-Shop Manufacturer aktualisieren
            _logger.Information("Hersteller des Directshop updaten...");
            //Erst alle löschen
            try
            {
                await mysqlController.QueryAsync("delete from manufacturer_to_store where store_id = 10");
            }
            catch (Exception ex)
            {
                _logger.Error("#ONLINE2 Fehler beim Direct-Shop deleteCmd: {error}", ex);
            }

            //Dann die richtigen hinzufügen
            try
            {
                await mysqlController.QueryAsync("insert into manufacturer_to_store SELECT DISTINCT `manufacturer_id` , '10' FROM `product` p left join product_to_store pts on p.product_id = pts.product_id AND pts.store_id = 10");
            }
            catch (Exception ex)
            {
                _logger.Error("#ONLINE3 Fehler beim Direct-Shop addCmd: {error}", ex);
            }

            _logger.Information("Hersteller für Directshop erfolgreich upgedatet!");
            #endregion

            _logger.Information("Update des Directshops abgeschlossen!");
        }

        public async Task<int> OnlineLagerUpdateAsync(Artikel artikel, DataTable onlineProdukte, DataTable onlineReservierteArtikel, MySqlController2 mysqlController, FbController2 fbController)
        {
            //Für Händler und Staffelpreise benötigen wir die product_id -> Wenn es die sowiso nicht gibt, können wir hier raus.
            ArtikelService artikelService = new ArtikelService();
            int product_id = 0;
            int Quantity = 0;
            int online_stock_status_id = 0;
            //Lager1 wird nicht ausgelesen, weil wir ja in der W4Bestand schon Lager1 haben

            int Reserviert = 0;///ArtikelOnlineReserviert hat model und SUMME als Spalten
            DataRow[] Ergebnis = onlineReservierteArtikel.Select($"model = '{artikel.Artikelnummer}'");
            foreach (DataRow Reihe in Ergebnis)
            {
                _ = int.TryParse(Reihe[1].ToString(), out Reserviert);
            }

            try
            {
                DataRow[] onlinerow = onlineProdukte.Select("model = '" + artikel.Artikelnummer + "'");

                if (onlinerow.Length > 0)
                {
                    product_id = (int)onlinerow[0]["product_id"];
                    Quantity = (int)onlinerow[0]["quantity"];
                    online_stock_status_id = (int)onlinerow[0]["stock_status_id"];
                }

            }
            catch (Exception ex)
            {
                _logger.Error("Zuordnung onlineToLocal - {error}", ex);
            }

            //Produkt ist Online, kann also geupdated werden
            if (product_id > 0)
            {
                int avaibility_id = 0;

                bool istAbverkauf = false;

                int w4Bestand = (int)artikel.Bestand;
                int lagerExtern = (int)(await artikelService.GetExternenBestand(artikel, fbController));
                //Virtueller Lagerbestand kommt dann zu tragen, wenn eigentliches W4 Lager = 0. So täuschen wir Lager vor für Artikel, die nicht da sind.
                if (w4Bestand == 0 && artikel.WK5_ARTI_N_VIRT_LAGER > 0)
                {
                    w4Bestand = (int)artikel.WK5_ARTI_N_VIRT_LAGER;
                }

                //Jetzt können wir den Lagerbestand insgesamt updaten - wir kennen ja nun die Mengen!
                Quantity = w4Bestand + lagerExtern - Reserviert;

                if (Quantity < 0)
                {
                    Quantity = 0;
                }


                if (Quantity > 0)
                {
                    avaibility_id = 1;
                }
                else
                {
                    avaibility_id = 5;
                }

                // Abverkauf so lange Vorrat reicht
                if (Quantity > 0 && artikel.ARTI_L_INAKT_ABVER)
                {
                    avaibility_id = 10;
                    istAbverkauf = true;
                }

                if (Quantity == 0 && artikel.ARTI_L_SCHWVERFUEG)
                {
                    avaibility_id = 7; // längere Lieferzeiten einkalkulieren
                }

                // 02.03.2022 MK: Wenn online angegeben ist, dass es ein Download Artikel ist, dann darf der Status nicht verändert werden
                if (online_stock_status_id is 2)
                {
                    avaibility_id = 2;
                }

                bool istInaktiv = false;
                if (artikel.ARTI_L_INAKTIV || (Quantity == 0 && artikel.ARTI_L_INAKT_ABVER))
                {
                    istInaktiv = true;
                }
                else
                {
                    istInaktiv = false;
                }

                if (artikel.ARTI_L_SCHWVERFUEG)
                {
                    avaibility_id = 7;  //längere Lieferzeiten einkalkulie
                }

                List<string> LangeLieferzeitBeiLagerGleichNull = await FileIO.LeseListe(Path.Combine(GlobalConfig.W4TmpFolderSrv01, "LangeLieferzeitBeiLagerNull.txt"));

                if (LangeLieferzeitBeiLagerGleichNull.Contains(artikel.Hersteller.ToUpper()) && Quantity == 0)
                {
                    avaibility_id = 7;
                }

                List<string> DirektverfuegBareHersteller = await FileIO.LeseListe(Path.Combine(GlobalConfig.W4TmpFolderSrv01, "DirektverfuegBareHersteller.txt"));

                if (DirektverfuegBareHersteller.Contains(artikel.Hersteller.ToUpper()) && Quantity == 0)
                {
                    avaibility_id = 1;
                }


                //Angebote löschen, wenn beide Qty = 0 werden oder wenn Lager1 von bestand zu 0 wechselt. E-Mail senden.
                bool angebotLoeschen = false;
                if (Quantity == 0)
                {
                    angebotLoeschen = true;
                }
                else if (w4Bestand is 0)
                {
                    //"alte" online qty holen um zu Prüfen ob Intern von Bestand zu 0 wechselt

                    int quantity_intern = 0;

                    try
                    {
                        mysqlController.AddParameter("@model", artikel.Artikelnummer);
                        quantity_intern = Convert.ToInt32(await mysqlController.FetchObjectAsync("Select quantity_intern FROM product where UPPER(model) = @model"));

                        //Abverkauf
                        if (quantity_intern > 0)
                        {
                            angebotLoeschen = true;
                        }
                    }
                    catch (Exception ex)
                    {
                        _logger.Error("#OnlineLagerUpdate1 {error}", ex);
                    }
                    finally
                    {
                        mysqlController.ClearParameters();
                    }
                }

                if (angebotLoeschen)
                {

                    int menge = 0;
                    try
                    {
                        mysqlController.AddParameter("@model", artikel.Artikelnummer);
                        menge = Convert.ToInt32(await mysqlController.FetchObjectAsync("SELECT count(*) as Menge FROM product_special where product_id = (SELECT product_id FROM product where UPPER(model) = @model)"));

                        if (menge <= 0)
                        {
                            angebotLoeschen = false;
                        }
                    }
                    catch (Exception ex)
                    {
                        _logger.Error("#OnlineLagerUpdate2 - {error}", ex);
                    }
                    finally
                    {
                        mysqlController.ClearParameters();
                    }
                }

                if (angebotLoeschen)
                {
                    try
                    {
                        mysqlController.AddParameter("@model", artikel.Artikelnummer);
                        await mysqlController.QueryAsync("DELETE FROM product_special where product_id = (SELECT product_id FROM product where UPPER(model) = @model)");
                    }
                    catch (Exception ex)
                    {
                        _logger.Error("#OnlineLagerUpdate3 - {error}", ex);
                    }
                    finally
                    {
                        mysqlController.ClearParameters();
                    }


                    _angeboteGelöscht.Add(artikel.Artikelnummer);
                }

                int anLager = 0;

                if (artikel.IstBundle)
                {
                    anLager = (int)artikel.Bestand;
                }
                else
                {
                    anLager = (int)artikel.Bestand + lagerExtern;
                }

                //Muss mind. 1 sein, sonst kann man die Artikel im Shop nicht kaufen
                if (artikel.ARTI_N_COLLI < 1)
                {
                    artikel.ARTI_N_COLLI = 1;
                }

                // Wenn wir Teile bei uns selbst an Lager haben, können wir auch abweichende Chargenmengen verkaufen. 
                // Das macht aber nur Sinn, wenn die Lagermenge nicht ein Vielfaches der VPE ist
                if (artikel.Bestand > 0 && (artikel.Bestand % artikel.ARTI_N_COLLI != 0))
                {
                    artikel.ARTI_N_COLLI = 1;
                }



                StringBuilder sqlBuilder = new StringBuilder();
                sqlBuilder.AppendLine(@"UPDATE `product` SET
quantity = @GESAMT_MENGE, 
quantity_intern = @LAGER1, 
quantity_extern = @LAGER2, 
date_modified = NOW(), 
sonderangebotArt = @SONDERART, 
minimum = @MINIMUM,
sku = @ARTIKELNUMMER,
status = @STATUS,
abverkauf = @ABVERKAUF");

                //if (artikel.ARTI_L_FIXPREIS)
                //{
                //  // Bei Fixpreisen wollen wir die Availability nicht ändern
                //  sqlBuilder.AppendLine(", stock_status_id = @STOCK_STATUS_ID"); //  avaibility_id
                //}

                // 10.12.2021 - MK: Ley sagt, immer updaten. Besondere Fälle sind durch Listen abgedeckt. 
                sqlBuilder.AppendLine(", stock_status_id = @STOCK_STATUS_ID"); //  avaibility_id



                if (artikel.Herstellernummer.Length > 0)
                {
                    sqlBuilder.AppendLine(", mpn = @HERSTELLERNUMMER");
                }

                if (artikel.ARTI_A_EAN is not null && artikel.ARTI_A_EAN.Length > 10)
                {
                    sqlBuilder.AppendLine(", ean = @EAN");
                }

                if (artikel.ARTI_N_GEWICHT > 0)
                {
                    sqlBuilder.AppendLine(", weight = @GEWICHT");
                    sqlBuilder.AppendLine(", weight_class_id = '1'");
                }


                //Wenn Kategorie RFID und Bestand > 0, dann Mindestmenge = 1 ... ansonsten Mindestmenge = W4.Mindestmenge
                if (artikel.Warengruppe == "2141" && artikel.Bestand > 0)
                {
                    artikel.ARTI_N_COLLI = 1;
                }

                if (artikel.PreisoptionOnline is <= 1 or > 3)
                {
                    sqlBuilder.AppendLine(", preis_auf_anfrage = 0, preis_nach_login = 0");
                }
                else if (artikel.PreisoptionOnline is 2)
                {
                    sqlBuilder.AppendLine(", preis_auf_anfrage = 1, preis_nach_login = 0");
                }
                else if (artikel.PreisoptionOnline is 3)
                {
                    sqlBuilder.AppendLine(", preis_auf_anfrage = 0, preis_nach_login = 1");
                }

                if (artikel.ARTI_N_VK1 > 0)
                {
                    sqlBuilder.AppendLine(", price = @PREIS");
                }

                if (artikel.ARTI_N_VK5 > 0)
                {
                    sqlBuilder.AppendLine(", directprice = @DIRECTPRICE");
                }
                sqlBuilder.AppendLine(" WHERE UPPER(model) = @ARTIKELNUMMER");

                mysqlController.AddParameter("@ARTIKELNUMMER", artikel.Artikelnummer);
                mysqlController.AddParameter("@HERSTELLERNUMMER", artikel.Herstellernummer);
                mysqlController.AddParameter("@EAN", artikel.ARTI_A_EAN);
                mysqlController.AddParameter("@MINIMUM", artikel.ARTI_N_COLLI);
                mysqlController.AddParameter("@STATUS", !istInaktiv);
                mysqlController.AddParameter("@STOCK_STATUS_ID", avaibility_id);
                mysqlController.AddParameter("@ABVERKAUF", istAbverkauf);
                mysqlController.AddParameter("@GEWICHT", artikel.ARTI_N_GEWICHT);
                mysqlController.AddParameter("@PREIS", artikel.ARTI_N_VK1);
                mysqlController.AddParameter("@DIRECTPRICE", artikel.ARTI_N_VK5);
                mysqlController.AddParameter("@GESAMT_MENGE", Quantity);
                mysqlController.AddParameter("@LAGER1", w4Bestand);
                mysqlController.AddParameter("@LAGER2", lagerExtern);
                mysqlController.AddParameter("@SONDERART", artikel.ARTI_L_ABVERKAUF || artikel.ARTI_L_INAKT_ABVER ? "Abverkauf" : "Sonderangebot");

                try
                {
                    await mysqlController.QueryAsync(sqlBuilder.ToString());
                }
                catch (Exception ex)
                {
                    _logger.Error("#OnlineLagerUpdate4 {error}", ex);

                }
                finally
                {
                    mysqlController.ClearParameters();
                }


                // Staffelpreise sind in product_discount ... sowie händlerpreise und auch Sonderpreise!
                // product_discount_id, Product_id, custmer_group_id, quantity, priority, price, date_start, date_end

                bool gibtHändlerPreis = false;

                //1) Händlerpreis = Customer_group_id = 3 einfügen:
                if (artikel.ARTI_N_VK2 > 0 && artikel.ARTI_N_VK2 > artikel.ARTI_N_EK)
                {
                    try
                    {
                        await MysqlInsertProductDiscountAsync(product_id, 3, 1, artikel.ARTI_N_VK2, mysqlController);
                    }
                    catch (Exception ex)
                    {
                        _logger.Error("#MysqlInsertProductDiscount1 - {error}", ex);
                    }
                    finally
                    {
                        mysqlController.ClearParameters();
                    }

                    gibtHändlerPreis = true;
                }

                string deleteDiscounts = $"DELETE FROM product_discount where product_id = {product_id}";

                // Staffelpreise für alle Kundengruppen gleich: - spart Zeit, statt einzelne Updates, Überladung ohne KundengruppenID
                // WICHTIG: N_COLLI muss * 2 genommen werden, da dieser Wert als Minimum für eine Bestellung im Shop hinterlegt ist
                // Staffelpreise für ein Minimum ergeben keinen sinn!
                if (artikel.ARTI_N_STAFFELP1 > 0 && artikel.ARTI_N_COLLI * 2 <= artikel.ARTI_N_STAFFELM1)
                {
                    try
                    {
                        await MysqlInsertProductDiscountAsync(product_id, artikel.ARTI_N_STAFFELM1, artikel.ARTI_N_STAFFELP1, mysqlController);
                    }
                    catch (Exception ex)
                    {
                        _logger.Error("#MysqlInsertProductDiscount2 - {error}", ex);
                    }

                    deleteDiscounts += " AND quantity != " + artikel.ARTI_N_STAFFELM1;
                }
                if (artikel.ARTI_N_STAFFELP2 > 0 && artikel.ARTI_N_COLLI * 2 <= artikel.ARTI_N_STAFFELM2)
                {
                    try
                    {
                        await MysqlInsertProductDiscountAsync(product_id, artikel.ARTI_N_STAFFELM2, artikel.ARTI_N_STAFFELP2, mysqlController);
                    }
                    catch (Exception ex)
                    {
                        _logger.Error("#MysqlInsertProductDiscount3 - {error}", ex);
                    }
                    deleteDiscounts += " AND quantity != " + artikel.ARTI_N_STAFFELM2;
                }
                if (artikel.ARTI_N_STAFFELP3 > 0 && artikel.ARTI_N_COLLI * 2 <= artikel.ARTI_N_STAFFELM3)
                {
                    try
                    {

                        await MysqlInsertProductDiscountAsync(product_id, artikel.ARTI_N_STAFFELM3, artikel.ARTI_N_STAFFELP3, mysqlController);
                    }
                    catch (Exception ex)
                    {
                        _logger.Error("#MysqlInsertProductDiscount4 - {error}", ex);
                    }
                    deleteDiscounts += " AND quantity != " + artikel.ARTI_N_STAFFELM3;
                }
                if (artikel.ARTI_N_STAFFELP4 > 0 && artikel.ARTI_N_COLLI * 2 <= artikel.ARTI_N_STAFFELM4)
                {
                    try
                    {

                        await MysqlInsertProductDiscountAsync(product_id, artikel.ARTI_N_STAFFELM4, artikel.ARTI_N_STAFFELP4, mysqlController);
                    }
                    catch (Exception ex)
                    {
                        _logger.Error("#MysqlInsertProductDiscount5 - {error}", ex);
                    }
                    deleteDiscounts += " AND quantity != " + artikel.ARTI_N_STAFFELM4;
                }
                if (artikel.ARTI_N_STAFFELP5 > 0 && artikel.ARTI_N_COLLI * 2 <= artikel.ARTI_N_STAFFELM5)
                {
                    try
                    {

                        await MysqlInsertProductDiscountAsync(product_id, artikel.ARTI_N_STAFFELM5, artikel.ARTI_N_STAFFELP5, mysqlController);
                    }
                    catch (Exception ex)
                    {
                        _logger.Error("#MysqlInsertProductDiscount6 - {error}", ex);
                    }
                    deleteDiscounts += " AND quantity != " + artikel.ARTI_N_STAFFELM5;
                }

                if (gibtHändlerPreis)
                {
                    deleteDiscounts += " AND customer_group_id != 3";
                }

                //DeleteDiscounts += " AND date_start = '0000-00-00' AND date_end = '0000-00-00'";

                // Wir löschen nun die Werte, die nicht upgedated sind und Datum = 0000-00-00
                // Das sind alte Werte, die ggf. nicht mehr benötigit werden.

                await mysqlController.QueryAsync(deleteDiscounts);

                // Sonderangebote berücksichtigen
                try
                {
                    mysqlController.AddParameter("@product_id", product_id);
                    await mysqlController.QueryAsync("DELETE FROM `product_special` WHERE product_id = @product_id");
                }
                catch (Exception ex)
                {
                    _logger.Error("#DeleteProductSpecials1 - {error}", ex);
                }
                finally
                {
                    mysqlController.ClearParameters();
                }

                foreach (var sonderangebot in artikel.Sonderangebote)
                {
                    if (sonderangebot.ARAN_N_OPENCART_GRUPPE is 0)
                    {
                        continue;
                    }

                    if (sonderangebot.SolangeDerVorratReicht && artikel.Bestand is 0)
                    {
                        continue;
                    }

                    try
                    {
                        mysqlController.AddParameter("@product_id", product_id);
                        mysqlController.AddParameter("@customer_group_id", sonderangebot.ARAN_N_OPENCART_GRUPPE);
                        mysqlController.AddParameter("@priority", 0);
                        mysqlController.AddParameter("@price", sonderangebot.ARAN_N_PREIS);
                        mysqlController.AddParameter("@date_start", sonderangebot.ARAN_D_START == default ? "0000-00-00" : sonderangebot.ARAN_D_START);
                        mysqlController.AddParameter("@date_end", sonderangebot.ARAN_D_ENDE == default ? "0000-00-00" : sonderangebot.ARAN_D_ENDE);
                        await mysqlController.QueryAsync(@"INSERT INTO product_special
(
product_id,
customer_group_id,
priority,
price,
date_start,
date_end
)
VALUES
(
@product_id,
@customer_group_id,
@priority,
@price,
@date_start,
@date_end
)");
                    }
                    catch (Exception ex)
                    {
                        _logger.Error("#MysqlInsertProductDiscountAsync7 - {error}", ex);
                    }
                    finally
                    {
                        mysqlController.ClearParameters();
                    }
                }


            }
            return product_id;
        }

        private async Task OnlineBestandUpdateAsync(FbController2 fbController, MySqlController2 mySqlController)
        {
            DateTime anfangszeit = DateTime.Now;
            _logger.Information("OnlineBestand Anfangszeit: {start}", anfangszeit);

            DataTable artikelnummern = await fbController.SelectDataAsync("SELECT ARTI_A_NR FROM ARTIKEL WHERE ARTI_L_INAKTIV = 'N'");



            // Nochmal das ganze mit den Online Artikeln
            DataTable onlineArtikel = await mySqlController.SelectDataAsync("SELECT product_id, UPPER(model) AS MODEL, quantity, quantity_intern, quantity_extern, stock_status_id FROM product");
            DataTable product_option_value_table = await mySqlController.SelectDataAsync("SELECT * FROM product_option_value");
            DataTable product_option_table = await mySqlController.SelectDataAsync("SELECT * FROM product_option");

            // ArtikelOnlineReserviert hat model und SUMME als Spalten
            DataTable onlineReservierteArtikel = await mySqlController.SelectDataAsync(@"SELECT 
op.model, 
sum(op.quantity) AS SUMME 
FROM `order_product` op 
LEFT JOIN `order` od on(op.order_id = od.order_id) 
where
(od.order_status_id = 1 OR od.order_status_id = 2 OR od.order_status_id = 17) 
GROUP BY op.model 
ORDER BY model ASC");

            ArtikelService artikelService = new ArtikelService();

            int zähler = 0;
            foreach (DataRow row in artikelnummern.Rows)
            {

                zähler++;
                string artikelnummer = row.Field<string>("ARTI_A_NR")!;
#if TestBesonderenArtikel
                if (!artikelnummer.ToUpper().Equals("L36M102076HIS", StringComparison.OrdinalIgnoreCase))
                    continue;
#endif

                Artikel? artikel = await artikelService.GetAsync(artikelnummer, fbController);

                if (artikel is null)
                {
                    _logger.Warning("Artikel {artikelnummer} wurde nicht gefunden", artikelnummer);
                    continue;
                }


                // Klein 2021: Staffelpreise gibt es nicht mehr, nur noch Rabatte
                artikel.ARTI_N_STAFFELP1 = 0;
                artikel.ARTI_N_STAFFELP2 = 0;
                artikel.ARTI_N_STAFFELP3 = 0;
                artikel.ARTI_N_STAFFELP4 = 0;
                artikel.ARTI_N_STAFFELP5 = 0;

                // 30.03.2021 Der Staffelpreis wird immer auf Basis des VK Preises Abzüglich Staffelrabatt berechnet. In der Datenbank gibt es in dem Feld Preis keine Werte mehr
                if (artikel.ARTI_N_STAFFELR1 > 0)
                {
                    artikel.ARTI_N_STAFFELP1 = artikel.ARTI_N_VK1 - (artikel.ARTI_N_VK1 / 100 * artikel.ARTI_N_STAFFELR1);
                }
                if (artikel.ARTI_N_STAFFELR2 > 0)
                {
                    artikel.ARTI_N_STAFFELP2 = artikel.ARTI_N_VK1 - (artikel.ARTI_N_VK1 / 100 * artikel.ARTI_N_STAFFELR2);
                }
                if (artikel.ARTI_N_STAFFELR3 > 0)
                {
                    artikel.ARTI_N_STAFFELP3 = artikel.ARTI_N_VK1 - (artikel.ARTI_N_VK1 / 100 * artikel.ARTI_N_STAFFELR3);
                }
                if (artikel.ARTI_N_STAFFELR4 > 0)
                {
                    artikel.ARTI_N_STAFFELP4 = artikel.ARTI_N_VK1 - (artikel.ARTI_N_VK1 / 100 * artikel.ARTI_N_STAFFELR4);
                }
                if (artikel.ARTI_N_STAFFELR5 > 0)
                {
                    artikel.ARTI_N_STAFFELP5 = artikel.ARTI_N_VK1 - (artikel.ARTI_N_VK1 / 100 * artikel.ARTI_N_STAFFELR5);
                }

                //LieferantenStaffelPreise. prüfen - Wenn LieferantenStaffelPreise angegeben, dann preis < LfStaffel prüfen
                int lieferantenId = await fbController.SelectRowAsync<int>(@"SELECT FIRST 1 ARLI_N_LIEFNR 
                                FROM LIEFERANTENARTIKEL
                                WHERE ARLI_N_PREIS > 0
                                AND ARLI_A_ARTIKELNR = @ARLI_A_ARTIKELNR
                                AND ARLI_N_LIEFNR != @ARLI_N_LIEFNR
                                ORDER BY ARLI_N_PREIS ASC", new { ARLI_A_ARTIKELNR = artikel.Artikelnummer, ARLI_N_LIEFNR = 70246 });

                //LieferantenStaffelpreise holen
                Dictionary<decimal, decimal> lieferantenStaffelPreise = new Dictionary<decimal, decimal>();
                if (lieferantenId > 0)
                {
                    Lieferantenartikel lieferantenartikel = await Lieferantenartikel.GetLieferantenartikelAsync(artikel.Artikelnummer, lieferantenId, fbController) ?? throw new NullReferenceException(nameof(lieferantenartikel));

                    void CheckLieferantenStaffelpreis(decimal lieferantenPreis, decimal menge, decimal rabatt)
                    {
                        if (menge > 0 && rabatt > 0)
                        {
                            decimal preis = lieferantenPreis * (1 - (rabatt / 100));
                            if (lieferantenStaffelPreise.ContainsKey(menge))
                            {
                                if (lieferantenStaffelPreise[menge] > preis)
                                {
                                    lieferantenStaffelPreise[menge] = preis;
                                }
                            }
                            else
                            {
                                lieferantenStaffelPreise.Add(menge, preis);
                            }
                        }
                    }

                    CheckLieferantenStaffelpreis(lieferantenartikel.ARLI_N_PREIS, lieferantenartikel.ARLI_N_MENGE1, lieferantenartikel.ARLI_N_RABATTMENGE1);
                    CheckLieferantenStaffelpreis(lieferantenartikel.ARLI_N_PREIS, lieferantenartikel.ARLI_N_MENGE2, lieferantenartikel.ARLI_N_RABATTMENGE2);
                    CheckLieferantenStaffelpreis(lieferantenartikel.ARLI_N_PREIS, lieferantenartikel.ARLI_N_MENGE3, lieferantenartikel.ARLI_N_RABATTMENGE3);
                    CheckLieferantenStaffelpreis(lieferantenartikel.ARLI_N_PREIS, lieferantenartikel.ARLI_N_MENGE4, lieferantenartikel.ARLI_N_RABATTMENGE4);
                }

                if (artikel.ARTI_N_STAFFELP1 <= artikel.ARTI_N_EK)
                {
                    if (!lieferantenStaffelPreise.ContainsKey(artikel.ARTI_N_STAFFELM1)
                        || artikel.ARTI_N_STAFFELP1 <= lieferantenStaffelPreise[artikel.ARTI_N_STAFFELM1]
                        || lieferantenStaffelPreise[artikel.ARTI_N_STAFFELM1] == 0
                        )
                    {
                        artikel.ARTI_N_STAFFELP1 = 0;
                        artikel.ARTI_N_STAFFELM1 = 0;
                    }
                }

                if (artikel.ARTI_N_STAFFELP2 <= artikel.ARTI_N_EK)
                {
                    if (!lieferantenStaffelPreise.ContainsKey(artikel.ARTI_N_STAFFELM2)
                        || artikel.ARTI_N_STAFFELP2 <= lieferantenStaffelPreise[artikel.ARTI_N_STAFFELM2]
                        || lieferantenStaffelPreise[artikel.ARTI_N_STAFFELM2] == 0
                        )
                    {
                        artikel.ARTI_N_STAFFELP2 = 0;
                        artikel.ARTI_N_STAFFELM2 = 0;
                    }
                }

                if (artikel.ARTI_N_STAFFELP3 <= artikel.ARTI_N_EK)
                {
                    if (!lieferantenStaffelPreise.ContainsKey(artikel.ARTI_N_STAFFELM3)
                        || artikel.ARTI_N_STAFFELP3 <= lieferantenStaffelPreise[artikel.ARTI_N_STAFFELM3]
                        || lieferantenStaffelPreise[artikel.ARTI_N_STAFFELM3] == 0
                        )
                    {
                        artikel.ARTI_N_STAFFELP3 = 0;
                        artikel.ARTI_N_STAFFELM3 = 0;
                    }
                }

                if (artikel.ARTI_N_STAFFELP4 <= artikel.ARTI_N_EK)
                {
                    if (!lieferantenStaffelPreise.ContainsKey(artikel.ARTI_N_STAFFELM4)
                        || artikel.ARTI_N_STAFFELP4 <= lieferantenStaffelPreise[artikel.ARTI_N_STAFFELM4]
                        || lieferantenStaffelPreise[artikel.ARTI_N_STAFFELM4] == 0
                        )
                    {
                        artikel.ARTI_N_STAFFELP4 = 0;
                        artikel.ARTI_N_STAFFELM4 = 0;
                    }
                }

                if (artikel.ARTI_N_STAFFELP5 <= artikel.ARTI_N_EK)
                {
                    artikel.ARTI_N_STAFFELP5 = 0;
                    artikel.ARTI_N_STAFFELM5 = 0;
                }





                try
                {
                    int product_id = await OnlineLagerUpdateAsync(artikel, onlineArtikel, onlineReservierteArtikel, mySqlController, fbController);

                }
                catch (Exception ex)
                {
                    _logger.Error("Artikelnummer: {artikelnummer}; Error: {error}", artikel.Artikelnummer, ex);
                    throw new Exception($"{artikel.Artikelnummer}", ex);
                }


                Console.Write($"[{zähler}/{artikelnummern.Rows.Count}]\r"); //W4LagerArtikel.Rows Zaehler
            } //Ende ForEach 
            Console.WriteLine();

            if (_angeboteGelöscht.Any())
            {
                StringBuilder sb = new StringBuilder();
                sb.AppendLine("Online wurden Angebote für folgende Artikel gelöscht. Siehe: <a href='http://wikis.local/wiki/doku.php/wk5:stammdaten:artikel:artikelimport:meldungen:au0002'>http://wikis.local/wiki/doku.php/wk5:stammdaten:artikel:artikelimport:meldungen:au0002</a><br />");
                foreach (string artikelnummer in _angeboteGelöscht)
                {
                    sb.AppendLine($"<a href='http://wk5.local/Artikel/{artikelnummer}'>{artikelnummer}</a><br />");
                }

                await _emailController.SendenAsync(GlobalConfig.EmailShopverwalter, $"Angebot wurde gelöscht", sb.ToString());
            }




            DateTime endzeit = DateTime.Now;
            _logger.Information("Onlinebestand Endzeit: {ende}", endzeit);
            _logger.Information("Onlinebestand synchronisation beendet.");
            TimeSpan differenz = endzeit.Subtract(anfangszeit);

            await _emailController.SendenAsync(GlobalConfig.EmailLogsEU, "W4 -> Onlinebestand + Preis Update", $"Abgleich des Online Bestandes und der Preise und Zubehör mittels ArtikelUpdate.exe und Daten aus der W4 beendet\n Anfangszeit: {anfangszeit}\nEnde: {endzeit}\nDauer: {differenz}");
            _logger.Information("Benötigte Zeit: {zeit}", differenz);
        }

        private static Task MysqlInsertProductDiscountAsync(int product_id, int customer_group_id, int menge, decimal preis, MySqlController2 mysqlController)
        {
            //Hier geht es nur um den Händlerpreis - daher Priorität 1

            if (menge > 0 && preis > 0)
            {
                mysqlController.AddParameter("@product_id", product_id);
                mysqlController.AddParameter("@preis", preis);
                mysqlController.AddParameter("@customer_group_id", customer_group_id);
                mysqlController.AddParameter("@menge", menge);
                return mysqlController.QueryAsync(@"INSERT INTO product_discount 
(
product_id, 
customer_group_id, 
quantity, 
priority, 
price, 
date_start, 
date_end
) 
VALUES
(
@product_id, 
@customer_group_id, 
@menge, 
1, 
@preis, 
'0000-00-00',
'0000-00-00')
ON DUPLICATE KEY UPDATE price = @preis;");
            }

            return Task.CompletedTask;
        }
        private static async Task MysqlInsertProductDiscountAsync(int product_id, decimal menge, decimal preis, MySqlController2 mySqlController)
        {
            if (menge > 0 && preis > 0)  //0 EUR wollen wir nicht in Staffelpreisen oder Händlerpreisen
            {
                string insertSql = "";
                // Nur diese Customer gruppen können einen Discount haben!
                List<int> customer_group_ids = new List<int>()
                {
                    1, 2, 3
                };

                foreach (int customer_group_id in customer_group_ids)
                {
                    insertSql = @$"INSERT INTO product_discount 
(
product_id, 
customer_group_id, 
quantity, 
priority, 
price, 
date_start, 
date_end
) 
VALUES
(
@product_id, 
{customer_group_id}, 
@menge, 
2, 
@preis, 
'0000-00-00',
'0000-00-00')
ON DUPLICATE KEY UPDATE price = @preis;";
                    mySqlController.AddParameter("@product_id", product_id);
                    mySqlController.AddParameter("@preis", preis);
                    mySqlController.AddParameter("@menge", menge);

                    await mySqlController.QueryAsync(insertSql);
                }



            }


        }
        private async Task PdfKorrekturAsync(MySqlController2 mySqlController)
        {
            _logger.Information("PdfKorrektur gestartet...");

            DataTable DownloadTabelle = await mySqlController.SelectDataAsync("SELECT d.filename, d.language_id, pd.product_id from download d left join product_to_download pd on d.download_id = pd.download_id WHERE pd.product_id is not null ORDER BY `d`.`language_id` DESC");

            int zaehler = 0;
            foreach (DataRow Reihe in DownloadTabelle.Rows)
            {
                zaehler++;
                mySqlController.AddParameter("@PDF", Reihe["filename"].ToString());
                mySqlController.AddParameter("@PID", (int)Reihe["product_id"]);
                mySqlController.AddParameter("@LID", (int)Reihe["language_id"]);

                try
                {
                    await mySqlController.QueryAsync("UPDATE product_description SET pdf = @PDF WHERE product_id = @PID AND language_id = @LID");
                }
                catch (Exception ex)
                {
                    _logger.Error("#pdfkorrektur1 {error}", ex);
                }

                Console.Write("\r{0}", $"[{zaehler}/{DownloadTabelle.Rows.Count}]");
            }

            Console.WriteLine();


            _logger.Information("PdfKorrektur beendet!");
        }
        private async Task ZubehörUpdateAsync(MySqlController2 mySqlController, FbController2 fbController)
        {

            _logger.Information("ZubehörUpdate gestartet...");
            DataTable wk5Artikel = await fbController.SelectDataAsync("SELECT ARZU_A_AUSGANGSART, ARZU_A_ZUBEHOERART FROM ARTIKELZUBEHOER");

            //jetzt laden wir eine neue Tabelle um Produkte und Zubehör zu haben und nicht einzeln abfragen zu müssen
            DataTable onlineArtikel = await mySqlController.SelectDataAsync("Select distinct UPPER(model) AS P1, product_id from product");

            await mySqlController.QueryAsync("TRUNCATE TABLE product_related");


            int zähler = 0;
            foreach (DataRow Reihe in wk5Artikel.Rows)
            {

                zähler++;
                string AusgangsModel = Reihe.Field<string>("ARZU_A_AUSGANGSART") ?? string.Empty;
                string RelatedModel = Reihe.Field<string>("ARZU_A_ZUBEHOERART") ?? string.Empty;

                DataRow[] result1 = onlineArtikel.Select("P1 = '" + AusgangsModel + "'");
                DataRow[] result2 = onlineArtikel.Select("P1 = '" + RelatedModel + "'");

                if (result1.Length > 0 && result2.Length > 0)
                {

                    mySqlController.AddParameter("@product_id", result1[0][1]);
                    mySqlController.AddParameter("@related_id", result2[0][1]);
                    try
                    {
                        await mySqlController.QueryAsync("INSERT IGNORE INTO product_related (product_id, related_id) VALUES (@product_id, @related_id)");
                    }
                    catch (Exception ex)
                    {
                        _logger.Error("Fehler beim Zubehör hinzufügen - {error}", ex);
                    }
                    finally
                    {
                        mySqlController.ClearParameters();
                    }
                }

                Console.Write($"[{zähler}/{wk5Artikel.Rows.Count}]\r");
            }
            Console.WriteLine();

            _logger.Information("ZubehörUpdate beendet", 1);

            await _emailController.SendenAsync(GlobalConfig.EmailLogsEU, "Zubehör Tabellen Update aus W4", "Die Zubehör Artikel wurden online eingestellt aus der W4 in der Funktion onlinebestand des W4 Artikelupdate.exe Programs");
        }
        private async Task FilterUpdateAsync(MySqlController2 mySqlController, FbController2 fbController)
        {


            string datei = Path.Combine(GlobalConfig.W4TmpFolderSrv01, $"FilterUpdate.txt");
            Dictionary<int, List<string>> zuordnungen = FilterUpdateFileParser.ParseFile(datei);


            ArtikelService artikelService = new ArtikelService();

            foreach (KeyValuePair<int, List<string>> kvp in zuordnungen)
            {
                List<ProductFilter> filter = await ProductFilter.FromFilterAsync(kvp.Key, GlobalConfig.KarleyMySql).ToListAsync();

                foreach (string artikelnummer in kvp.Value)
                {
                    _logger.Information($"Checking {artikelnummer}");
                    await foreach (ArtikelZubehör zub in ArtikelZubehör.GetArtikelZubehörAsync(artikelnummer, fbController))
                    {
                        Artikel? artikel = await artikelService.GetAsync(zub.ARZU_A_ZUBEHOERART, fbController);
                        KarleyProduct? product = await KarleyProduct.GetProductAsync(zub.ARZU_A_ZUBEHOERART);

                        if (product is not null && artikel is not null && artikel.Warengruppe.Equals("2172", StringComparison.OrdinalIgnoreCase))
                        {
                            if (!filter.Where(x => x.ProductId == product.ProductId).Any())
                            {
                                _logger.Information($"Adding {artikel.Artikelnummer} ({product.ProductId}) - {artikel.Bezeichnung1} to {artikelnummer} ({kvp.Key})");
                                try
                                {
                                    ProductFilter produktFilter = new ProductFilter()
                                    {
                                        FilterId = kvp.Key,
                                        ProductId = product.ProductId
                                    };

                                    filter.Add(produktFilter);

                                    await produktFilter.AddFilterAsync(GlobalConfig.KarleyMySql);

                                }
                                catch (Exception ex)
                                {
                                    _logger.Error($"Fehler beim hinzufügen des Artikel {artikel.Artikelnummer} zu {artikelnummer}. Fehler: {{ex}}", ex);
                                    throw;
                                }
                            }
                        }

                    }
                }
            }
        }
    }

}
