using KarleyLibrary.Erweiterungen;
using Microsoft.AspNetCore.Components;
using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Toolbelt.Blazor.HotKeys;
using WK5.Core;
using WK5.Core.Basis;
using WK5.Core.Basis.Erweiterungen;
using WK5.Core.Basis.Filter;
using WK5.Core.Drucken.Angebote;
using WK5.Core.Email;
using WK5.Core.Models;
using WK5.Core.PageModels.Verkauf.Angebote;

namespace WK5_Blazor.Pages.Verkauf.Angebote
{
    public partial class AngeboteForm : IPipeline<Angebot>
    {
        [Parameter] public int Angebotsnummer { get; set; }
        [Parameter] public int RmaNummer { get; set; }

        [Parameter] public int Wartungsnummer { get; set; }
        public Pipeline<Angebot> Pipeline { get; set; } = new Pipeline<Angebot>();
        public Pipeline<Angebot> ÜbernahmePipeline { get; set; } = new Pipeline<Angebot>();

        private bool _showKundenArtikelhistorieModal;
        private bool _showÜbernahmeModal;
        private bool _showRoherlösWarnungModal;
        private bool _showArtikelzubehör = false;
        private bool _showEventualPositionenModal;
        private bool _showTermineNichtVerfügbarModal;
        private Func<bool, Task>? _confirmAction;
        private bool _confirmIsMail = false;
        private List<ArtikelBild> _bilder = new List<ArtikelBild>();
        private Rma? _rma;
        private Wartung? _wartung;
        private List<(int posNr, string artikelnummer, decimal alterPreis, decimal neuerPreis)> _preisÄnderungen = new();
        /// <summary>
        /// Eine Liste der PosId der Positionen, die in den Auftrag übernommen werden sollen, im Angebot aber nicht berechnet werden
        /// </summary>
        private List<int> _posOhneBerechnungFürÜbernahme = new List<int>();
        private List<(Belegposition pos, int belegnummer, string belegtyp)> _termineNichtVerfügbar = new();
        public AngeboteForm()
        {
            Input = new Angebot()
            {
                MwstBerechnen = true
            };

            FilterPakete = new PaketFilter
            {
                Typ = PaketTyp.Angebot
            };

            FilterZeiterfassung.Option = ZeitenFilterOption.Angebot;

            StartCopy = new Angebot();
            ActiveTab = AngeboteTabs.Kundendaten;
            EditOnlyTabs.Add(AngeboteTabs.Dokumentenablage);
            EditOnlyTabs.Add(AngeboteTabs.Historie);                
        }

        public override string ActivePageName => Modus switch
        {
            EditMode.Anlage => "Neues Angebot",
            EditMode.Bearbeiten => $"AN-{Angebotsnummer}",
            _ => "Unbekannt"
        };
        public override string FinalBreadcrumbItemName => Modus switch
        {
            EditMode.Anlage => "Neu",
            EditMode.Bearbeiten => $"{Angebotsnummer}",
            _ => "Unbekannt"
        };



        protected override async Task OnParametersSetAsync()
        {
            _versandMehrwertsteuer = await Mehrwertsteuer.GetVersandMehrwertsteuerAsync();

            if (Angebotsnummer > 0)
            {
                await LadeEditModeAsync();
            }
            FilterPakete.Nummer = Angebotsnummer.ToString();
            FilterZeiterfassung.Suchbegriff = Angebotsnummer.ToString();
            FilterZeiterfassung.Kundennummer = Input.Kundennummer;
            StartCopy = Input.DeepCopy();

            if (Modus is EditMode.Anlage)
            {
                using FbController2 fbController = new FbController2();
                _rma = await rmaService.GetAsync(RmaNummer, fbController);

                if (_rma is not null)
                {
                    Input.RmaNummerVerknüpfung = RmaNummer;

                    Kunde? kunde = await Kunde.GetKundeAsync(_rma.Kundennummer);
                    if (kunde is not null)
                    {
                        await KundeSelectedAsync(kunde, true, true);
                    }
                }

                if (Wartungsnummer is > 0)
                {
                    _wartung = await wartungsService.GetWartungAsync(Wartungsnummer, fbController);

                    if (_wartung is not null)
                    {
                        Input.WartungsnummerVerknüpfung = Wartungsnummer;

                        Kunde? kunde = await Kunde.GetKundeAsync(_wartung.Kundennummer);
                        if (kunde is not null)
                        {
                            await KundeSelectedAsync(kunde, true, true);
                        }
                    }
                }
            }

            if (Kunde is null)
            {
                _showKundenSucheModal = true;
            }

            await SetupPipelineAsync();
            await SetupÜbernahmePipelineAsync();

        }
        public async override Task LadeEditModeAsync()
        {
            using FbController2 fbController = new FbController2();
            Angebot? angebot = await Angebot.GetAngebotAsync(Angebotsnummer, fbController);
            if (angebot is not null)
            {
                Kunde = await Kunde.GetKundeAsync(angebot.Kundennummer);
                if (Kunde is not null)
                {
                    await KundeSelectedAsync(Kunde);

                    Input = angebot;
                    _zähler = angebot.Positionen.Count;
                    Gesperrt = angebot.BELE_L_UBERNAHME || angebot.AuftragsnummerVerknüpfung > 0;
                    await LieferanschriftChangedAsync(Input.LieferanschriftId);
                    await RechnungsanschriftChangedAsync(Input.RechnungsanschriftId);
                    await PositionSelected(Input.Positionen.FirstOrDefault());
                    _subBelege = await belegService.GetSubBelege(BelegTyp.Angebot, Angebotsnummer, fbController);
                    Modus = EditMode.Bearbeiten;
                }
            }
            else
            {
                await AddAlertAsync(new AlertBox
                {
                    AlertType = AlertType.Danger,
                    Message = $"Angebot {Angebotsnummer} konnte nicht gefunden werden."
                });


                navigationManager.NavigateTo("/Angebote");
            }

            if (!_isDisposed)
            {
                await CheckActivePageAsync();
            }


            await SetAlerts();
        }
        public async Task SetupPipelineAsync()
        {
            Pipeline = new Pipeline<Angebot>();
            await Pipeline.HardReset();

            if (Modus is EditMode.Anlage)
            {
                Pipeline.Add((input) =>
                {
                    decimal roherlös = input.GetRoherlös(Program.AppDaten.Optionen);
                    decimal netto = input.GetNetto(Program.AppDaten.Optionen);
                    decimal roherlösprozent = roherlös / (netto > 0 ? netto : 1) * 100;

                    if (roherlös < 20 || roherlösprozent < 10)
                    {
                        _showRoherlösWarnungModal = true;
                        return false;
                    }

                    return true;
                });
            }

            Pipeline.Add(async (input) =>
            {
                await SaveAsync();
                return true;
            });
        }
        private async Task SetupÜbernahmePipelineAsync()
        {
            await ÜbernahmePipeline.HardReset();
            ÜbernahmePipeline.Add((input) =>
            {
                _posOhneBerechnungFürÜbernahme.Clear();
                foreach (var pos in input.Positionen.Where(x => !string.IsNullOrWhiteSpace(x.Option)))
                {
                    var option = Program.AppDaten.Optionen[pos.Option];
                    if(option is not null && !option.OPTI_L_BERECHNEN)
                    {
                        _showEventualPositionenModal = true;
                        return false;
                    }
                }

                return true;
            });

            ÜbernahmePipeline.Add(async (input) =>
            {
                using FbController2 fbController = new FbController2();
                foreach (var pos in input.Positionen.Where(x => x.ARTI_L_MIETE))
                {
                    var (istVerfügbar, belegnummer, belegtyp) = await belegService.IstTerminVerfügbarAsync(pos, fbController);

                    if(!istVerfügbar)
                    {
                        _termineNichtVerfügbar.Add((pos, belegnummer, belegtyp));
                    }
                }

                if(_termineNichtVerfügbar.Any())
                {
                    _showTermineNichtVerfügbarModal = true;
                    return false;
                }

                return true;
            });

            // Wir wollen das Angebot noch auf Preisänderungen prüfen und den Mitarbeiter auf ggf. geänderte Preise aufmerksam machen.
            ÜbernahmePipeline.Add(async (input) =>
            {
                if(input.GültigBis.Date < DateTime.Today.Date)
                {
                    StringBuilder sb = new StringBuilder();
                    using FbController2 fbController = new FbController2();
                    foreach (var pos in input.Positionen.Where(x => x.Artikelnummer != "TEXT"))
                    {
                        Artikel artikel = await artikelService.GetAsync(pos.Artikelnummer, fbController) ?? throw new NullReferenceException(nameof(artikel));
                        List<decimal> preise = new List<decimal>();
                        var (preis, rabatt, rabattbezeichnung) = artikel.GetPreis(pos.Menge, this.Kunde!.KUND_N_PREISLISTNR);
                        if (artikel.LetzePreisÄnderung > input.BELE_D_DATE)
                        {
                            preise.Add(preis);                  
                        }
                        else
                        {
                            preise.Add(pos.Preis);
                        }

                        decimal posPreis = preise.Min();
        
                        if (posPreis != pos.Preis)
                        {
                            _preisÄnderungen.Add((pos.PosNr, pos.Artikelnummer, pos.Preis, posPreis));
                        }
                    }

                    return !_preisÄnderungen.Any();
                }

                return true;
            });

            ÜbernahmePipeline.Add(async (input) =>
            {
                await ÜbernahmeAsync();
                return true;
            });
        }
        private async Task SubmitAsync()
        {
            if (!NurDurchAdminZuBearbeiten || (NurDurchAdminZuBearbeiten && Mitarbeiter.IsAdmin))
            {
                if (!GesperrtDurchZahlung || (GesperrtDurchZahlung && Mitarbeiter.IsAdmin))
                {
                    if (!SperreDurchAnderenUser && Input.HasBeenModified(StartCopy))
                    {
                        if (_editForm is null || _editForm.EditContext is null || Kunde is null)
                        {
                            return;
                        }

                        if (_editForm.EditContext.Validate())
                        {

                            Pipeline.Reset();
                            await Pipeline.RunUntilFailureAsync(Input);

                        }
                    }
                }
            }
        }
        private async Task SaveAsync()
        {
            if (Kunde is null)
            {
                return;
            }



            using FbController2 fbController = new FbController2(Mitarbeiter.PersonalNummer);
            await fbController.StartTransactionAsync();
            try
            {
                if (Modus is EditMode.Anlage)
                {
                    await belegService.CreateAsync(Input, Kunde, Program.AppDaten.Optionen, fbController);

                    if (_wartung is not null)
                    {
                        await wartungsService.SetAuftragVerknüpfungAsync(Wartungsnummer, Input.Belegnummer, fbController);
                    }

                    await AddAlertAsync(new AlertBox
                    {
                        AlertType = AlertType.Success,
                        Message = $"Angebot {Input.Belegnummer} erfolgreich angelegt",
                        DecayTime = 10
                    });
                }
                else
                {
                    if (Gesperrt)
                    {
                        await belegService.UpdateGesperrtenBelegAsync(Input, fbController);
                        await AddAlertAsync(new AlertBox
                        {
                            AlertType = AlertType.Success,
                            Message = $"Notiz wurde erfolgreich gespeichert",
                            DecayTime = 10
                        });
                    }
                    else
                    {
                        await belegService.UpdateAsync(Input, Kunde, Program.AppDaten.Optionen, fbController);
                        await AddAlertAsync(new AlertBox
                        {
                            AlertType = AlertType.Success,
                            Message = $"Angebot {Input.Belegnummer} erfolgreich gespeichert",
                            DecayTime = 10
                        });
                    }

                }
                await fbController.CommitChangesAsync();
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex);
                await fbController.RollbackChangesAsync();
                throw;
            }

            if (Modus is EditMode.Anlage)
            {
                navigationManager.NavigateTo($"/Angebote/{Input.Belegnummer}");
            }
            else
            {
                await OnParametersSetAsync();
            }
        }
        private async Task ÜbernahmeAsync()
        {

            using FbController2 fbController = new FbController2(Mitarbeiter.PersonalNummer);
            await fbController.StartTransactionAsync();
            try
            {
                Angebot? angebot = await Angebot.GetAngebotAsync(Angebotsnummer, fbController);

                if (angebot is not null && Kunde is not null)
                {
                    await CheckÜbernahmeKundeAsync(angebot, Kunde);
                    Auftrag neuerAuftrag = (Auftrag)await belegService.ÜbernahmeAsync(angebot, Kunde, Program.AppDaten.Optionen, Program.AppDaten.VersandMehrwertsteuer, fbController, Mitarbeiter.PersonalNummer, _posOhneBerechnungFürÜbernahme);
                    fbController.AddParameter("@KUKO_N_BELEGNR_AN", Angebotsnummer);
                    await fbController.QueryAsync("UPDATE KUNDENKONTAKTE SET KUKO_L_WIEDERV_ERL = 'Y' WHERE KUKO_N_BELEGNR_AN = @KUKO_N_BELEGNR_AN");
                    await AddAlertAsync(new AlertBox
                    {
                        AlertType = AlertType.Success,
                        Message = $"AN-{Angebotsnummer} wurde erfolgreich in AU-{neuerAuftrag.Belegnummer} übernommen",
                        DecayTime = 10
                    });

                    await fbController.CommitChangesAsync();
                    navigationManager.NavigateTo($"/Auftraege/{neuerAuftrag.Belegnummer}");


                }
            }
            catch (Exception)
            {
                await fbController.RollbackChangesAsync();
                throw;
            }
        }
        private async Task KopierenAsync()
        {
            if (Modus is EditMode.Bearbeiten)
            {
                using FbController2 fbController = new FbController2(Mitarbeiter.PersonalNummer);
                await fbController.StartTransactionAsync();
                try
                {
                    Angebot? angebot = await Angebot.GetAngebotAsync(Angebotsnummer, fbController);
                    if (angebot is null)
                    {
                        throw new ArgumentNullException(nameof(angebot));
                    }

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

                    var (NeuerBeleg, Messages) = await belegService.CopyAsync(angebot, Kunde, Program.AppDaten.Optionen, fbController, Mitarbeiter.PersonalNummer);
                    await AddAlertsAsync(Messages);
                    if (NeuerBeleg is not null)
                    {
                        await AddAlertAsync(new AlertBox
                        {
                            AlertType = AlertType.Success,
                            Message = "Angebot wurde erfolgreich kopiert.",
                            DecayTime = 10
                        });

                        await fbController.CommitChangesAsync();
                        navigationManager.NavigateTo($"/Angebote/{NeuerBeleg.Belegnummer}");
                    }
                }
                catch (Exception)
                {
                    await fbController.RollbackChangesAsync();
                    throw;
                }



            }
        }
        private async Task LadeArtikelbilderAsync(Belegposition pos)
        {
            using FbController2 fbController = new FbController2();

            List<ArtikelBild> bilder = new List<ArtikelBild>();

            ArtikelBild? hauptbild = await Artikel.GetArtikelHauptImageAsync(pos.Artikelnummer);
            await foreach (ArtikelBild bild in ArtikelBild.GetBilderAsync(pos.Artikelnummer))
            {

                bilder.Add(bild);
            }

            _bilder = bilder;

            if (!_bilder.Any())
            {
                await jsRuntime.ShowToast(ToastType.error, "Es konnten keine Artikelbilder gefunden werden.");
            }
        }

        private async Task AddStaffelpreisTextAsync(Belegposition pos)
        {
            using FbController2 fbController = new FbController2();
            var artikel = await artikelService.GetAsync(pos.Artikelnummer, fbController);

            if (artikel is not null)
            {
                var tmp = artikel.GetStaffelpreise();

                if (!tmp.Any())
                {
                    await jsRuntime.ShowToast(ToastType.error, $"Im Artikel {pos.Artikelnummer} sind keine Staffelpreise hinterlegt.");
                }
                else
                {
                    StringBuilder staffelBuilder = new StringBuilder();
                    staffelBuilder.AppendLine($"Mengenrabatte:<br />");
                    foreach (var staffelpreis in tmp)
                    {
                        staffelBuilder.AppendLine($"Ab {staffelpreis.Menge} {artikel.ARTI_A_EINHEIT}: {staffelpreis.Rabatt}% Vorteil<br />");
                    }

                    var newPos = new Belegposition
                    {
                        Artikelnummer = "TEXT",
                        PosNr = ++_zähler,
                        Langtext = staffelBuilder.ToString()
                    };

                    Input.Positionen.Add(newPos);
                    await PositionSelected(newPos);
                }
            }



        }



        public async override Task DownloadAsync(bool confirmed = false)
        {
            if (Kunde is null)
            {
                return;
            }

            if (!confirmed && Input.Frachtkosten is 0 && !Input.Positionen.Where(x => x.Artikelnummer.Equals("VERSAND")).Any())
            {
                _confirmAction = DownloadAsync;
                _confirmIsMail = false;
                return;
            }

            _isDownloading = true;
            StateHasChanged(); // Wird für den Shortcut benötigt
            using FbController2 fbController = new FbController2();
            var angebot = await Angebot.GetAngebotAsync(Angebotsnummer, fbController);

            if (angebot is not null)
            {

                PrintAngebotRegelsatz printRegeln = new PrintAngebotRegelsatz
                {
                    ShowFooter = true,
                    ShowHeader = true
                };

                if (Program.AppDaten.VersandMehrwertsteuer is null)
                {
                    throw new ArgumentNullException(nameof(Program.AppDaten.VersandMehrwertsteuer));
                }

                PrintAngebot angebotPrinter = await PrintAngebot.CreateAsync(angebot, printRegeln, fbController);
                string filenameAngebotPdf = angebotPrinter.Print(GlobalConfig.Configuration.OutputPfad);
                await belegService.SetDruckdatumAsync(BelegTyp.Angebot, Angebotsnummer, fbController);
                await downloadService.DownloadFile($"AN-{Angebotsnummer}.pdf", await File.ReadAllBytesAsync(filenameAngebotPdf), "application/pdf");
                await downloadService.ClearBuffers();
            }

            _isDownloading = false;
        }
        public async override Task ÖffneMailAsync(bool confirmed = false)
        {
            if (Kunde is null)
            {
                return;
            }

            if (!confirmed && Input.Frachtkosten is 0 && !Input.Positionen.Where(x => x.Artikelnummer.Equals("VERSAND")).Any())
            {
                _confirmAction = ÖffneMailAsync;
                _confirmIsMail = true;
                return;
            }

            _mailIsLoading = true;
            StateHasChanged(); // Wird für den Shortcut benötigt
            using FbController2 fbController = new FbController2();
            var angebot = await Angebot.GetAngebotAsync(Angebotsnummer, fbController);

            if (angebot is not null)
            {
                Ansprechpartner? ansprechpartner = await Ansprechpartner.GetAnsprechpartnerByNameAsync(angebot.Kundennummer, angebot.Ansprechpartner);
                string? empfängerEmail = Kunde.KUND_A_EMAIL;
                if (ansprechpartner is not null && !String.IsNullOrWhiteSpace(ansprechpartner.PART_A_EMAIL))
                {
                    empfängerEmail = ansprechpartner.PART_A_EMAIL;
                }

                if (string.IsNullOrWhiteSpace(empfängerEmail))
                {
                    await jsRuntime.ShowToast(ToastType.error, "Angebot konnte nicht in David geöffnet werden, da beim Kunden keine Mailadresse hinterlegt ist");
                    return;
                }


                KarleyBrowserInterfaceEmail email = new KarleyBrowserInterfaceEmail
                {
                    Absender = GlobalConfig.EmailInfoEU,
                    Empfänger = empfängerEmail,
                    Content = String.Empty,
                    Betreff = $"Angebot {Angebotsnummer}"
                };

                PrintAngebotRegelsatz printRegeln = new PrintAngebotRegelsatz
                {
                    ShowFooter = true,
                    ShowHeader = true
                };

                if (Program.AppDaten.VersandMehrwertsteuer is null)
                {
                    throw new ArgumentNullException(nameof(Program.AppDaten.VersandMehrwertsteuer));
                }

                PrintAngebot angebotPrinter = await PrintAngebot.CreateAsync(angebot, printRegeln, fbController);
                string filenameAngebotPdf = angebotPrinter.Print(GlobalConfig.Configuration.OutputPfad);
                await belegService.SetDruckdatumAsync(BelegTyp.Angebot, Angebotsnummer, fbController);
                email.Anhänge.Add(filenameAngebotPdf);

                string agbPfad = await GlobalConfig.GetAgbPathAsync();
                email.Anhänge.Add(agbPfad);

                bool istErstkunde = await Kunde.IstErstkundeAsync(angebot.Kundennummer);
                if (istErstkunde)
                {
                    email.Anhänge.Add(@"\\srv01\daten\Werbung\Karley-Flyer\Mailing_Systemhaus.pdf");
                }


                List<KeyValuePair<string, string>> warengruppenDokumente = new List<KeyValuePair<string, string>>()
                {
                    new KeyValuePair<string, string>( "2172", @"\\srv01\daten\Preislisten\_Karley\KARLEY-PREISLISTE-ETIKETTEN.pdf" ), //EtikettenMaterial                    
                };

                List<KeyValuePair<string, string>> unterwarengruppenDokumente = new List<KeyValuePair<string, string>>()
                {
                    new KeyValuePair<string, string>( "2543", @"\\srv01\daten\Preislisten\_Karley\KARLEY-PREISLISTE-BONROLLEN.pdf" ), //Bonrollen                    
                };



                foreach (Belegposition pos in angebot.GetEndPositionen())
                {
                    IEnumerable<string> wgDoks = warengruppenDokumente.Where(x => x.Key == pos.ARTI_A_WARENGRUPPE).Select(x => x.Value);
                    IEnumerable<string> uwgDoks = unterwarengruppenDokumente.Where(x => x.Key == pos.ARTI_A_UNTERWARENG).Select(x => x.Value);

                    foreach (string dok in wgDoks)
                    {
                        if (!email.Anhänge.Contains(dok))
                        {
                            email.Anhänge.Add(dok);
                        }
                    }

                    foreach (string dok in uwgDoks)
                    {
                        if (!email.Anhänge.Contains(dok))
                        {
                            email.Anhänge.Add(dok);
                        }
                    }
                }

                var (success, data, _) = KarleyLibrary.Serialization.XMLWriter.Serialize(email);
                if (success)
                {
                    string xmlFilename = $"{email.GetHashCode()}.xml";
                    await File.WriteAllTextAsync(Path.Combine(GlobalConfig.Configuration.OutputPfad, xmlFilename), data);
                    await jsRuntime.OpenNewTab($"karley:davidMail;{xmlFilename}");
                }


            }
            _mailIsLoading = false;

        }

        public override async Task CheckActivePageAsync()
        {
            var (success, page) = ActivePages.AddActivePage(new ActivePage(PageType.Angebot, Angebotsnummer.ToString(), Mitarbeiter.PersonalNummer));

            if (success)
            {
                Page = page;
            }
            else
            {
                await AddAlertAsync(new AlertBox
                {
                    AlertType = AlertType.Danger,
                    Message = $"Das Angebot wird zurzeit durch {Program.AppDaten.GetMitarbeiterName(page.UserId)} bearbeitet."
                });

                SperreDurchAnderenUser = true;
            }
        }

        public override void AddHotKeys(HotKeysContext context)
        {
            context.Add(ModKeys.Alt, Keys.S, SubmitAsync, "Speichert das Angebot", Exclude.Default);
            base.AddHotKeys(context);
        }
        public async Task OnTabChange(AngeboteTabs newActiveTab)
        {
            switch (newActiveTab)
            {
                case AngeboteTabs.Positionen:
                    if (Input.Positionen.Count is 0)
                    {
                        _showArtikelSucheModal = true;
                    }
                    break;
                case AngeboteTabs.Historie:
                    await LadeHistorieAsync();
                    break;
                default:
                    break;
            }
        }

    }
}
