using KarleyLibrary.Serialization;
using Serilog;
using Serilog.Core;
using Serilog.Sinks.Firebird;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using WK5.Core;
using WK5.Core.Email;
using WK5.Core.Models;
using WK5.Core.Services;
//#define wdhRechnungenAnKarley // Gibt an, das neu erstellte wiederkehrende Rechnungen an info@ geschickt werden, anstatt zum Kunden
namespace KarleyUpdate
{
    public class Auftragsfreigabe
    {
        public Logger log { get; set; } = new LoggerConfiguration()
                .MinimumLevel.Information()
                .WriteTo.Console()
                .WriteTo.File(GlobalConfig.AuftragsFreigabeLogFile, rollingInterval: RollingInterval.Day)
#if DEBUG
                .WriteTo.Firebird(GlobalConfig.W4LocalDebugConnectionString, prefix: () => "KarleyUpdate Auftragsfreigabe ")
#else
                .WriteTo.Firebird(GlobalConfig.W4ConnectionString, prefix: () => "KarleyUpdate Auftragsfreigabe ")
#endif
                .CreateLogger();
        public EmailController emailController { get; set; } = new EmailController();
        public Auftragsfreigabe()
        {

        }

        private async Task<bool> SendLogMailAsync(string identifier, string BELE_A_TYP, int BELE_N_NR, string mailBody, string mailSubject, string mailRecipient, List<EmailAnhang>? anhänge = null)
        {            
            var readresult = XMLWriter.ReadList<EmailLog>(GlobalConfig.AuftragsFreigabeEmailLogFile);
            if (readresult.success)
            {
                List<EmailLog> logs = readresult.elements;
                bool alreadySent = false;
                foreach (EmailLog eLog in logs)
                {
                    if (eLog.BELE_A_TYP == BELE_A_TYP && eLog.BELE_N_NR == BELE_N_NR && eLog.Typ == identifier)
                    {
                        if (eLog.Timestamp.AddDays(7) > DateTime.Now)
                        {
                            alreadySent = true;
                        }
                    }
                }

                if (!alreadySent)
                {
                    EmailResponse emailResponse = await emailController.SendenAsync(mailRecipient, mailSubject, mailBody, anhänge);
                    if (emailResponse.Success)
                    {
                        log.Information($"{BELE_A_TYP}-{BELE_N_NR} Email {identifier} wurde erfolgreich versendet");
                    }
                    else
                    {
                        log.Information($"{BELE_A_TYP}-{BELE_N_NR} Fehler beim versenden der {identifier} Email: {emailResponse.Message}");
                    }

                    EmailLog newLog = new EmailLog(identifier, BELE_A_TYP, BELE_N_NR, mailRecipient, DateTime.Now);
                    var (success, message) = XMLWriter.ListAppend<EmailLog>(newLog, GlobalConfig.AuftragsFreigabeEmailLogFile);
                    if (!success)
                    {
                        log.Error($"{BELE_A_TYP}-{BELE_N_NR} Fehler beim schreiben der XML Emails für {identifier}. Fehler: {message}", 0);
                        return false;
                    }
                    else
                    {
                        return true;
                    }
                }
                else
                {
                    log.Information($"{BELE_A_TYP}-{BELE_N_NR} {identifier} Email wurde nicht erneut gesendet. Wurde bereits vor weniger als 7 Tagen gesendet");
                    return true;
                }
            }
            else
            {
                log.Error($"{BELE_A_TYP}-{BELE_N_NR} Email {identifier}: Konnte Email Log (\"{GlobalConfig.AuftragsFreigabeEmailLogFile}\") nicht lesen: {readresult.message}");
                return false;
            }
        }
        public async Task RunAsync()
        {
            ArtikelService artikelService = new ArtikelService();
            Dictionary<string, decimal> bereitsFreigegeben = new Dictionary<string, decimal>();            

            log.Information("Starte Auftragsfreigabe");

            AuftragsfreigabeService service = new AuftragsfreigabeService();
            BelegService belegService = new BelegService();
            using FbController2 fbController = new FbController2();
            ZahlungsbedingungCollection zahlungsbedingungen = await Zahlungsbedingung.GetZahlungsbedingungenAsync(fbController);
            await foreach (AuftragFreigegeben auftrag in service.GetFreigegebeneAufträgeAsync())
            {
                foreach (Belegposition bpos in auftrag.GetEndPositionen())
                {
                    if (bereitsFreigegeben.ContainsKey(bpos.Artikelnummer))
                    {
                        bereitsFreigegeben[bpos.Artikelnummer] += bpos.Menge - bpos.BPOS_N_MENGEGELIEF;
                    }
                    else
                    {
                        bereitsFreigegeben[bpos.Artikelnummer] = bpos.Menge - bpos.BPOS_N_MENGEGELIEF;
                    }
                }
            }

            await foreach (AuftragFreigegeben auftrag in service.GetOffeneAufträgeAsync(null, CancellationToken.None))
            {

                if (auftrag.WK5_BELE_L_LIMITPRUEFUNG)
                {
                    log.Information($"Auftrag {auftrag.Belegnummer} wurde nicht freigegeben, da er noch nicht durch die Kreditlimitprüfung freigegeben wurde");
                }



                if (auftrag.WK5_BELE_L_PAUSIERT)
                {
                    log.Information($"Auftrag {auftrag.Belegnummer} wurde nicht freigegeben, da er pausiert ist.");
                    continue;
                }

                //Shopbestellungen können wir direkt freigeben (Wollte Marvin: 04.09.2020)
                if (String.IsNullOrWhiteSpace(auftrag.Bestellnummer) || !auftrag.Bestellnummer.StartsWith("KL-"))
                {
                    if (auftrag.BELE_D_ANLAGEDATUM.AddHours(1) > DateTime.Now)
                    {
#if !DEBUG
                        continue;
#endif
                    }
                }

                Kunde kunde = await Kunde.GetKundeAsync(auftrag.Kundennummer) ?? throw new ArgumentNullException(nameof(kunde));

                if (kunde.KUND_L_SPERRE)
                {
                    log.Information($"Auftrag {auftrag.Belegnummer} wurde nicht freigegeben! Der Kunde ist gesperrt!");
                    continue;
                }

                if (kunde.KUND_L_SPERRE_OP && kunde.OffenePosten > 0)
                {
                    log.Information($"Auftrag {auftrag.Belegnummer} wurde nicht freigegeben! Der Kunde ist gesperrt, da er noch offene Posten hat!");
                    continue;
                }


                bool skipLockedPos = false;
                foreach (Belegposition bpos in auftrag.GetEndPositionen().Where(x => x.BPOS_N_MENGEGELIEF < x.Menge))
                {
                    if (bpos.Artikelnummer.Equals("ETIPROD", StringComparison.OrdinalIgnoreCase))
                    {
                        log.Information($"Auftrag {auftrag.Belegnummer} wurde nicht freigegeben! Der Auftrag enthält gesperrte Positionen");
                        skipLockedPos = true;
                    }
                }

                if (skipLockedPos)
                {
                    continue;
                }

                if (!auftrag.ZahlungErhalten(zahlungsbedingungen))
                {
                    log.Information($"Auftrag {auftrag.Belegnummer} wurde nicht freigegeben! Wir haben für den Auftrag noch keine Zahlung erhalten");
                    continue;
                }

                if (await auftrag.WirdKomplettDurchBestellungGeliefert())
                {
                    log.Information($"Auftrag {auftrag.Belegnummer} wurde nicht freigegeben! Der Auftrag wird komplett vom Lieferanten geliefert.");
                    continue;
                }

                if (auftrag.BELE_L_ABRUF)
                {
                    log.Information($"Auftrag {auftrag.Belegnummer} wurde nicht freigegeben! Der Auftrag ist ein Abrufauftrag!");
                    continue;
                }

                if (auftrag.WK5_BELE_L_LIMITPRUEFUNG)
                {
                    decimal verbleibendesKreditlimit = await belegService.GetUnverbrauchtenKreditlimitWert(auftrag.Kundennummer, fbController);
                    OptionCollection optionCollection = await Option.GetOptionenAsync(fbController);
                    decimal nettoBetrag = auftrag.GetNettoBetrag(optionCollection);

                    verbleibendesKreditlimit += nettoBetrag;

                    if (verbleibendesKreditlimit >= nettoBetrag)
                    {
                        fbController.AddParameter("@WK5_BELE_L_LIMITPRUEFUNG", false);
                        fbController.AddParameter("@BELE_N_NR", auftrag.Belegnummer);
                        fbController.AddParameter("@BELE_A_TYP", auftrag.Belegtyp);
                        await fbController.QueryAsync("UPDATE BELEGE SET WK5_BELE_L_LIMITPRUEFUNG = @WK5_BELE_L_LIMITPRUEFUNG WHERE BELE_N_NR = @BELE_N_NR AND BELE_A_TYP = @BELE_A_TYP");
                        await BelegChange.InsertBelegChangeAsync(fbController, new BelegChange
                        {
                            BCNG_A_BELEGTYP = auftrag.Belegtyp,
                            BCNG_N_BELEGNR = auftrag.Belegnummer.ToString(),
                            BCNG_A_WERTALT = "Kreditlimitprüfung wurde automatisch durch die Auftragsfreigabe geändert",
                            BCNG_A_WERTNEU = "",
                            BCNG_N_ART = 911,
                            BCNG_N_POSNR = 0,
                            BCNG_N_USER = 0,
                            BCNG_TIMESTAMP = DateTime.Now,
                            BCNG_A_FELD = null

                        });
                    }
                    else
                    {
                        log.Information($"Auftrag {auftrag.Belegnummer} wurde nicht freigegeben! Kunde hat das Kreditlimit überzogen");

                        StringBuilder sbEmail = new StringBuilder();
                        sbEmail.AppendLine($"<p>Der Beleg AU-{auftrag.Belegnummer} liegt über dem Kreditilimit des Kunden und erfordert daher eine manuelle Prüfung.</p>");
                        sbEmail.AppendLine($"<p>Bitte prüft den Auftrag nach und entscheidet ob der Freigegeben werden soll</p>");
                        sbEmail.AppendLine($"<br/><p>Zu diesem Auftrag kriegt ihr über das Kreditlimit 7 Tage keine Email mehr!</p>");
                        sbEmail.AppendLine($"<br/><p><a href=\"http://wk5.local/Auftraege/Limitprüfung\">http://wk5.local/Auftraege/Limitprüfung</a></p>");
                        sbEmail.AppendLine($"<br/><br/><p>Artikelnummern zu dem Auftrag</p>");
                        sbEmail.AppendLine(String.Join("<br/>", auftrag.Positionen.Select(x => x.Artikelnummer)));
                        await SendLogMailAsync("KREDIT", auftrag.Belegtyp, auftrag.Belegnummer, "", "", "");
                        continue;
                    }
                }


                // 28.09.2021 - MK: Mit Ley besprochen, es werden immer alle Lagerführenden Artikel bestellt, unabhängig davon, ob ein Auftrag Service enthält oder nicht.

                if (auftrag.Liefertermin != default)
                {
                    if (auftrag.Liefertermin > DateTime.Now)
                    {
                        log.Information($"Auftrag {auftrag.Belegnummer} wurde nicht freigegeben, da der Liefertermin noch nicht erreicht wurde.");
                        continue;
                    }
                }

                if (auftrag.WK5_BELE_L_DIREKTLIEFERUNG)
                {
                    log.Information($"Auftrag {auftrag.Belegnummer} wurde nicht freigegeben, da es sich hierbei um eine Direktlieferung handelt.");
                    continue;
                }

                bool skipNotEnough = false;
                bool skipFertigung = false;
                foreach (Belegposition bpos in auftrag.GetEndPositionen().Where(x => x.BPOS_N_MENGEGELIEF < x.Menge))
                {
                    if (!bpos.ARTI_L_LAGERFUEHR)
                    {
                        continue;
                    }

                    decimal remainingMenge = bpos.Bestand;
                    if (bereitsFreigegeben.ContainsKey(bpos.Artikelnummer))
                    {
                        remainingMenge -= bereitsFreigegeben[bpos.Artikelnummer];
                    }

                    decimal toLiefern = bpos.GetZuLieferndeMenge();
                    if (toLiefern > 0 && bpos.BPOS_L_FERTIGUNG)
                    {
                        log.Information($"Auftrag {auftrag.Belegnummer} wurde nicht freigegeben! Der Auftrag enthält Positionen die gefertigt werden müssen!");
                        skipFertigung = true;
                        break;
                    }

                    if (remainingMenge >= toLiefern)
                    {
                        if (bereitsFreigegeben.ContainsKey(bpos.Artikelnummer))
                        {
                            bereitsFreigegeben[bpos.Artikelnummer] += toLiefern;
                        }
                        else
                        {
                            bereitsFreigegeben.Add(bpos.Artikelnummer, toLiefern);
                        }
                    }
                    else
                    {
                        log.Information($"Auftrag {auftrag.Belegnummer} wurde nicht freigegeben! Der Auftrag ist nicht lieferbar!");
                        skipNotEnough = true;
                    }
                }

                if (skipNotEnough)
                {
                    continue;
                }

                if (skipFertigung)
                {
                    await SendLogMailAsync("AUFTRAGSFREIGABE_FERTIGUNG", "AU", auftrag.Belegnummer, $"Der Auftrag {auftrag.Belegnummer} hätte freigegeben werden können, enthält aber eine Position die gefertigt werden muss. Kümmert euch bitte darum!", "Auftragsfreigabe - Aufträge mit Fertigung", "support@karley.eu");
                    continue;
                }

                if (auftrag.HatServiceleistung)
                {
                    log.Information($"Auftrag {auftrag.Belegnummer} wurde nicht freigegeben! Der Auftrag hat eine Service Leistung");
                    var (success, elements, message) = XMLWriter.ReadList<EmailLog>(GlobalConfig.AuftragsFreigabeEmailLogFile);
                    if (success)
                    {
                        List<EmailLog> logs = elements;
                        bool alreadySent = false;
                        foreach (EmailLog eLog in logs)
                        {
                            if (eLog.BELE_A_TYP == "AU" && eLog.BELE_N_NR == auftrag.Belegnummer && eLog.Typ == "SERVICE")
                            {
                                if (eLog.Timestamp.AddDays(7) > DateTime.Now)
                                {
                                    alreadySent = true;
                                }
                            }
                        }

                        if (!alreadySent)
                        {
                            bool hatTechnikerLeistung = auftrag.GetEndPositionen().Any(x => x.Artikelnummer.Equals("SERVTS", StringComparison.OrdinalIgnoreCase) || x.Artikelnummer.StartsWith("KVA-") || x.Artikelnummer.StartsWith("MIETE_"));

                            hatTechnikerLeistung = hatTechnikerLeistung || auftrag.IstTechnikbeleg;

                            string empfänger = GlobalConfig.EmailInfoEU;

                            if (hatTechnikerLeistung)
                            {
                                empfänger = "support@karley.eu;sockalin@karley.eu";
                            }

                            StringBuilder sbEmail = new StringBuilder();
                            sbEmail.AppendLine($"<p>Der Beleg AU-{auftrag.Belegnummer} enthält Service Artikel.</p>");
                            sbEmail.AppendLine($"<p>Kunde: {auftrag.Kundenname}</p>");
                            sbEmail.AppendLine($"<p>Bitte prüft den Auftrag nach und besprecht mit der Technik ob der Auftrag freigegeben werden kann.</p>");
                            sbEmail.AppendLine($"<br/><p>Zu diesem Auftrag kriegt ihr über Service Artikel 7 Tage keine Email mehr!</p>");
                            EmailResponse emailResponse = await emailController.SendenAsync(empfänger, "Auftragsfreigabe - Serviceauftrag", sbEmail.ToString());
                            if (emailResponse.Success)
                            {
                                log.Information($"Auftrag {auftrag.Belegnummer} ServiceAuftrag Email wurde erfolgreich versendet");
                            }
                            else
                            {
                                log.Information($"Auftrag {auftrag.Belegnummer} Fehler beim versenden der ServiceAuftrag Email: {emailResponse.Message}");
                            }

                            EmailLog newLog = new EmailLog("SERVICE", "AU", auftrag.Belegnummer, empfänger, DateTime.Now);
                            var appendResult = XMLWriter.ListAppend<EmailLog>(newLog, GlobalConfig.AuftragsFreigabeEmailLogFile);
                            if (!appendResult.success)
                            {
                                log.Error($"Fehler beim schreiben der XML Emails. Fehler: {appendResult.message}", 0);
                            }
                        }
                    }
                    continue;
                }

                await service.AuftragFreigebenAysnc(auftrag.Belegnummer, DateTime.Now, fbController);
                log.Information($"Der Auftrag {auftrag.Belegnummer} wurde freigegeben!");

            }
        }
    }
}