//#define TEST_EXPORT
#if DEBUG
#define COMMIT_CHANGES
#endif
using ecoDMS.Library;
using KarleyLibrary;
using Serilog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using WK5.Core;
using WK5.Core.Basis;
using WK5.Core.Basis.Erweiterungen;
using WK5.Core.Models;
using WK5.Core.Models.Lager;
using WK5.Core.Models.Starmoney;
using WK5.Core.PageModels.Stammdaten.Lieferanten;

namespace WK5_Blazor.Pages.Bestellungen
{
    public partial class BestellungBezahlen : IAsyncDisposable
    {
        private CancellationTokenSource _cts = new();
        private List<LieferantenBezahlenInput> _zugänge = new List<LieferantenBezahlenInput>();
        public decimal ZahllimitProTag { get; set; }
        private async Task LadeZugängeAsync()
        {
            _cts.Cancel();
            _cts.Dispose();
            _cts = new CancellationTokenSource();
            using FbController2 fbController = new FbController2();
            var token = _cts.Token;
            await foreach (var item in lagerService.GetZugängeZuZahlenAsync(fbController, token))
            {
                _zugänge.Add(new LieferantenBezahlenInput(item));
            }
        }
        private Zugang? _selectedZugang;
        private List<string> _ecoVorschauBilder = new List<string>();

        private bool _loadingEcoDokument;
        private bool _useSkonto = true;
        private bool _useSkonto2 = true;
        protected override async Task OnInitializedAsync()
        {
            await LadeZugängeAsync();
            ZahllimitProTag = GlobalConfig.GetZahllimitProTag();
        }

        public bool ExportMöglich => _zugänge.Where(x => x.Exportieren).FirstOrDefault() is not null
            && (!GetSummen().Where(x => x.summe > ZahllimitProTag).Any() || _zugänge.Where(x => x.Exportieren).Count() is 1);

        public IEnumerable<(string lieferantenName, int zahlungsbedingung, DateTime zahlungsziel, decimal skonto, decimal summe)> GetSummen()
        {
            foreach (var item in _zugänge.Where(x => x.Exportieren)
                .Select(x => x.Zugang)
                .GroupBy(x => (x.LIEF_A_NAME1, x.ZUGA_N_ZABD))
            )
            {
                Zahlungsbedingung? zahlungsbedingung = Program.AppDaten.Zahlungsbedingungen[item.Key.ZUGA_N_ZABD];
                if (zahlungsbedingung is null)
                {
                    throw new ArgumentNullException(nameof(zahlungsbedingung));
                }

                DateTime zahlungsziel = item.Min(x => x.ZUGA_D_RE_DATUM);
                int addTage = zahlungsbedingung.ZABD_N_NETTOTAGE;
                decimal skonto = 0;
                if (_useSkonto && zahlungsbedingung.ZABD_N_SKONTO1TAGE > 0 && zahlungsbedingung.ZABD_N_SKONTO1PROZ > 0)
                {
                    if (_useSkonto2 && zahlungsbedingung.ZABD_N_SKONTO2TAGE > 0 && zahlungsbedingung.ZABD_N_SKONTO2PROZ > 0)
                    {
                        addTage = zahlungsbedingung.ZABD_N_SKONTO2TAGE;
                        skonto = zahlungsbedingung.ZABD_N_SKONTO2PROZ;
                    }
                    else
                    {
                        addTage = zahlungsbedingung.ZABD_N_SKONTO1TAGE;
                        skonto = zahlungsbedingung.ZABD_N_SKONTO1PROZ;
                    }
                }

                zahlungsziel = zahlungsziel.AddDays(addTage).AddDays(-2);
                zahlungsziel = KarleyLibrary.Erweiterungen.DateTimeErweiterung.NächstesDatumWochentag(zahlungsziel, true);


                yield return (item.Key.LIEF_A_NAME1, item.Key.ZUGA_N_ZABD, zahlungsziel, skonto, item.Sum(x => x.Brutto * ((100 - skonto) / 100)));
            }
        }

        public async Task ExportierenAsync()
        {
            using FbController2 fbController = new FbController2();
            await fbController.StartTransactionAsync();
            List<SepaÜberweisung> überweisungen = new List<SepaÜberweisung>();
            var zugängeZuExportieren = _zugänge.Where(x => x.Exportieren).ToList();
            foreach (var export in zugängeZuExportieren)
            {
                bool success = false;
                try
                {
                    success = await SetEcoZugangStatus(export);
                }
                catch (Exception ex)
                {
                    Log.Logger.Error("[DATEV-EXPORT] ERROR {error}", ex);
                    await AddAlertAsync(new AlertBox
                    {
                        AlertType = AlertType.Danger,
                        Message = $"Der Status von der Rechnungsnummer {export.Zugang.ZUGA_A_RE_NR} konnte nicht in ecoDMS umgestellt werden. Bitte manuell umstellen.\r\nFehler: {ex.Message}"
                    });
                    success = false;
                }

                if (success)
                {

                    var sucheÜberweisung = überweisungen.Where(x => x.Begünstiger.Equals(export.Zugang.LIEF_A_NAME1) && x.Betrag < ZahllimitProTag).FirstOrDefault();
                    Zahlungsbedingung? zahlungsbedingung = Program.AppDaten.Zahlungsbedingungen[export.Zugang.ZUGA_N_ZABD];

                    if (zahlungsbedingung is null)
                    {
                        throw new ArgumentNullException(nameof(zahlungsbedingung));
                    }


                    DateTime zahlungsziel = export.Zugang.ZUGA_D_RE_DATUM;
                    int addTage = zahlungsbedingung.ZABD_N_NETTOTAGE;
                    decimal skonto = 0;
                    if (_useSkonto && zahlungsbedingung.ZABD_N_SKONTO1TAGE > 0 && zahlungsbedingung.ZABD_N_SKONTO1PROZ > 0)
                    {
                        if (_useSkonto2 && zahlungsbedingung.ZABD_N_SKONTO2TAGE > 0 && zahlungsbedingung.ZABD_N_SKONTO2PROZ > 0)
                        {
                            addTage = zahlungsbedingung.ZABD_N_SKONTO2TAGE;
                            skonto = zahlungsbedingung.ZABD_N_SKONTO2PROZ;
                        }
                        else
                        {
                            addTage = zahlungsbedingung.ZABD_N_SKONTO1TAGE;
                            skonto = zahlungsbedingung.ZABD_N_SKONTO1PROZ;
                        }
                    }

                    zahlungsziel = zahlungsziel.AddDays(addTage).AddDays(-2);

                    zahlungsziel = KarleyLibrary.Erweiterungen.DateTimeErweiterung.NächstesDatumWochentag(zahlungsziel, true);

                    if (sucheÜberweisung is null)
                    {
                        SepaTextschlüssel textschlüssel = SepaTextschlüssel.SEPA_Überweisung;
                        if (!export.Zugang.LIEF_A_LAND.Equals("DE") && Program.AppDaten.Länder.Any(x => x.WK5_LAND_L_ISTEULAND && x.LAND_A_ID == export.Zugang.LIEF_A_LAND))
                        {
                            textschlüssel = SepaTextschlüssel.EU_Überweisung;
                        }


                        decimal zahlbetrag = export.Zugang.Brutto;

                        while (zahlbetrag > 0)
                        {
                            decimal betragÜberweisung = zahlbetrag > ZahllimitProTag ? ZahllimitProTag : zahlbetrag;
                            SepaÜberweisung überweisung = new SepaÜberweisung
                            {
                                Art = SepaArt.SEPA_Terminüberweisung,
                                Begünstiger = export.Zugang.LIEF_A_NAME1,
#if TEST_EXPORT
                            BegünstigerZeile2 = "TEST IMPORT",
#endif
                                BicDesBegünstigten = export.Zugang.LIEF_A_BIC,
                                IbanDesBegünstigten = export.Zugang.LIEF_A_IBAN,
                                Textschlüssel = textschlüssel,
                                Adressbuchübernahme = true,
                                Turnus = SepaTurnus.Einmalig,
                                Datum = DateTime.Now,
                                Rechnungsbetrag = betragÜberweisung,
                                AuftragAusführenAm = zahlungsziel,
                                Skonto = skonto,
                                Währung = "EUR", // Währung ist immer Euro, da wir andere Währungen beim zubuchen in EUR umwandeln.
                                UnsereKundennummerBeimLieferanten = export.Zugang.LIEF_A_KUNDENNR
                            };

                            überweisung.Rechnungsnummern.Add(export.Zugang.ZUGA_A_RE_NR!);
                            überweisungen.Add(überweisung);



                            zahlbetrag -= betragÜberweisung;
                        }


                    }
                    else
                    {

                        if (sucheÜberweisung.AuftragAusführenAm > zahlungsziel)
                        {
                            sucheÜberweisung.AuftragAusführenAm = zahlungsziel;
                        }

                        sucheÜberweisung.Rechnungsbetrag += export.Zugang.Brutto;
                        sucheÜberweisung.Rechnungsnummern.Add(export.Zugang.ZUGA_A_RE_NR!);
                    }

                    //Auf bezahlt setzen
                    //await lagerService.SetBezahltAsync(fbController, export.Zugang.ZUGA_N_NR);
                    export.Zugang.ZUGA_D_ZAHLDATUM = zahlungsziel;
                    export.Zugang.ZUGA_N_ZAHLTBETR = export.Zugang.Brutto * ((100 - skonto) / 100);
                    export.Zugang.WK5_ZUGA_L_BEZAHLT = true;
                    export.Zugang.ZUGA_L_ERLEDIGT = true;
                    await lagerService.UpdateZugangAsync(fbController, export.Zugang);


                    foreach (var zugangsnummer in export.Zugang.WeitereZugangsnummern)
                    {
                        Zugang? zugang = await Zugang.GetZugangAsync(zugangsnummer);
                        if (zugang is not null)
                        {
                            zugang.ZUGA_D_ZAHLDATUM = zahlungsziel;
                            zugang.ZUGA_N_ZAHLTBETR = export.Zugang.Brutto * ((100 - skonto) / 100);
                            zugang.WK5_ZUGA_L_BEZAHLT = true;
                            zugang.ZUGA_L_ERLEDIGT = true;
                            await lagerService.UpdateZugangAsync(fbController, zugang);
                        }
                        //await lagerService.SetBezahltAsync(fbController, zugangsnummer);
                    }
                    _zugänge.Remove(export);
                }
            }



            StringBuilder sb = new StringBuilder();
            sb.Append("Offline verwalten;");
            sb.Append("Adressbuchübernahme;");
            sb.Append("Eilauftrag;");
            sb.Append("Datum;");
            sb.Append("Begünstigter;");
            sb.Append("Begünstigter Zeile 2;");
            sb.Append("Begünstigter Zeile 3;");
            sb.Append("Begünstigter Zeile 4;");
            sb.Append("Konto des Begünstigten;");
            sb.Append("BLZ des Begünstigten;");
            sb.Append("IBAN des Begünstigten;");
            sb.Append("BIC des Begünstigten;");
            sb.Append("Rechnungsbetrag;");
            sb.Append("Skonto;");
            sb.Append("Währung;");
            sb.Append("Betrag;");
            for (int i = 0; i < SepaÜberweisung.ANZAHL_VERWENDUNGSZWECKE; i++)
            {
                sb.Append($"Verwendungszweckzeile {i + 1};");
            }

            sb.Append("Name des Spenders;");
            sb.Append("PLZ und Straße des Spenders;");
            sb.Append("Kontoinhaber;");
            sb.Append("Rechnungsnummer;");
            sb.Append("Turnus;");
            sb.Append("Ausführungstag;");
            sb.Append("Erstmalig;");
            sb.Append("Letzmalig;");
            sb.Append("Auftrag ausführen am;");
            sb.Append("Mandatsstatus;");
            sb.Append("Gläubiger-Identifikation;");
            sb.Append("Mandatsreferenz;");
            sb.Append("Ausstellungsdatum;");
            sb.Append("Automatisch Letztmalig;");
            sb.Append("Textschlüssel;");
            sb.Append($"Art;{Environment.NewLine}");
            foreach (SepaÜberweisung überweisung in überweisungen)
            {
                überweisung.WriteLine(ref sb);
            }

            await SetAlerts();
            await fbController.CommitChangesAsync();


            // StarMoney braucht die Datei als ISO-8859-1, wir haben den Inhalt aber als UTf-8, also müssen wir Konvertieren
            var bytes = Encoding.UTF8.GetBytes(sb.ToString());
            var bytesConverted = Encoding.Convert(Encoding.UTF8, Encoding.GetEncoding("ISO-8859-1"), bytes);
            await downloadService.DownloadFile("StarMoney_SepaImport.csv", bytesConverted, "text/plain");
            await downloadService.ClearBuffers();
        }
        private async Task GetEcoDokumentAsync(Zugang zugang)
        {
            _loadingEcoDokument = true;
            _ecoVorschauBilder.Clear();
            _selectedZugang = zugang;
            EcoDmsController ecoDmsController = await EcoDmsController.CreateAsync(new Uri(GlobalConfig.ECODMS_BASIS_URL), GlobalConfig.ECODMS_DEFAULT_USER, Log.Logger);

            var sucheDokument = (await ecoDmsController.EcoSucheDoksAsync(new List<EcoSearchFilter>
            {
                new EcoSearchFilter
                {
                    ClassifyAttribut = ecoDmsController.ClassifyAttributes.GetKeyByValue("Belegnummer")!,
                    SearchOperator = SuchOperator.Gleich,
                    SearchValue = zugang.ZUGA_A_RE_NR
                },
                new EcoSearchFilter
                {
                    ClassifyAttribut = "docart",
                    SearchOperator = SuchOperator.Gleich,
                    SearchValue = "15"
                }
            })).FirstOrDefault();


            if (sucheDokument is not null)
            {
                try
                {
                    int zähler = 1;
                    while (true)
                    {
                        var data = await ecoDmsController.GetDokumentenVorschauAsync(sucheDokument.DocId, zähler, 820);
                        string ecoBaseImage = Convert.ToBase64String(data);
                        _ecoVorschauBilder.Add(ecoBaseImage);
                        zähler++;
                    }
                }
                catch (Exception)
                {


                }
            }

            _loadingEcoDokument = false;
        }

        private async Task<bool> SetEcoZugangStatus(LieferantenBezahlenInput input)
        {
            if (String.IsNullOrWhiteSpace(input.Zugang.ZUGA_A_RE_NR))
            {
                return false;
            }
            EcoDmsController ecoDmsController = await EcoDmsController.CreateAsync(new Uri(GlobalConfig.ECODMS_BASIS_URL), GlobalConfig.ECODMS_DEFAULT_USER, Log.Logger);
            EcoDMSdocInfoCL? sucheDokument = (await ecoDmsController.EcoSucheDoksAsync(new List<EcoSearchFilter>
            {
                new EcoSearchFilter
                {
                    ClassifyAttribut = ecoDmsController.ClassifyAttributes.GetKeyByValue("Belegnummer")!,
                    SearchOperator = SuchOperator.Gleich,
                    SearchValue = input.Zugang.ZUGA_A_RE_NR.Trim()
                },
                new EcoSearchFilter
                {
                    ClassifyAttribut = "docart",
                    SearchOperator = SuchOperator.Gleich,
                    SearchValue = "15"
                }
            })).FirstOrDefault();

            if (sucheDokument is not null)
            {
                string erledigtStatusText = "Erledigt";
                string bearbeitenStatusText = "Zu Bearbeiten";
                EcoDMSStatiCL? erledigtStatus = ecoDmsController.EcoDmsStatusListe?.Where(x => x.Name.Equals(erledigtStatusText, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
                EcoDMSStatiCL? bearbeitenStatus = ecoDmsController.EcoDmsStatusListe?.Where(x => x.Name.Equals(bearbeitenStatusText, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
                string nichtsToDo = String.Empty;
                string zahlenToDo = "Zu zahlen";
                string datevToDo = "Datev verbuchen";
                string datevZahlenToDo = "Datev & zu zahlen";

                if (input.Zugang.ZUGA_L_FIBU)
                {
                    sucheDokument.ClassifyAttributes[ecoDmsController.ClassifyAttributes.GetKeyByValue("ToDo")!] = nichtsToDo;
                    if (erledigtStatus is not null)
                    {
                        sucheDokument.ClassifyAttributes[ecoDmsController.ClassifyAttributes.GetKeyByValue("Status")!] = erledigtStatus.Id.ToString();
                    }
                    else
                    {
                        await AddAlertAsync(new AlertBox
                        {
                            AlertType = AlertType.Danger,
                            Message = $@"Der ecoDMS Status konnte für die Rechnung {input.Zugang.ZUGA_A_RE_NR} nicht auf '{erledigtStatusText}' gesetzt werden da der Status nicht gefunden wurde
Die Rechnung wurde bezahlt und in Datev verbucht"
                        });
                        return false;
                    }
                }
                else
                {
                    sucheDokument.ClassifyAttributes[ecoDmsController.ClassifyAttributes.GetKeyByValue("ToDo")!] = datevToDo;
                    if (bearbeitenStatus is not null)
                    {
                        sucheDokument.ClassifyAttributes[ecoDmsController.ClassifyAttributes.GetKeyByValue("Status")!] = bearbeitenStatus.Id.ToString();
                    }
                    else
                    {
                        await AddAlertAsync(new AlertBox
                        {
                            AlertType = AlertType.Danger,
                            Message = $@"Der ecoDMS Status konnte für die Rechnung {input.Zugang.ZUGA_A_RE_NR} nicht auf '{bearbeitenStatusText}' gesetzt werden da der Status nicht gefunden wurde
Die Rechnung wurde bezahlt aber nicht in Datev verbucht"
                        });
                        return false;
                    }
                }
                await ecoDmsController.ClassifyAsync(sucheDokument);
                return true;
            }
            else
            {
                await AddAlertAsync(new AlertBox
                {
                    AlertType = AlertType.Danger,
                    Message = $@"Das Dokument zur Rechnung {input.Zugang.ZUGA_A_RE_NR} konnte nicht in ecoDMS gefunden werden"
                });
                return false;
            }
        }
        public override ValueTask DisposeAsync()
        {
            _cts?.Cancel();
            _cts?.Dispose();
            return base.DisposeAsync();
        }

    }
}
