using Elwig.Helpers;
using Elwig.Models.Dtos;
using Elwig.Models.Entities;
using Microsoft.EntityFrameworkCore;
using System;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using Elwig.Helpers.Billing;
using Elwig.Helpers.Export;
using Microsoft.Win32;
using System.Text.Json;
using Elwig.Documents;

namespace Elwig.Windows {
    public partial class PaymentVariantsWindow : ContextWindow {

        public readonly int Year;
        public readonly bool SeasonLocked;
        private bool DataValid, DataChanged, NameChanged, CommentChanged, DateValid, DateChanged, TransferDateValid, TransferDateChanged;
        private BillingData? BillingData;
        private bool WeightModifierChanged = false;

        private static readonly JsonSerializerOptions JsonOpt = new() { WriteIndented = true };

        private readonly RoutedCommand CtrlL = new("CtrlL", typeof(MemberAdminWindow), [new KeyGesture(Key.L, ModifierKeys.Control)]);
        private readonly RoutedCommand CtrlP = new("CtrlP", typeof(MemberAdminWindow), [new KeyGesture(Key.P, ModifierKeys.Control)]);
        private readonly RoutedCommand CtrlÜ = new("CtrlÜ", typeof(MemberAdminWindow), [new KeyGesture(Key.Oem1, ModifierKeys.Control)]);
        private readonly RoutedCommand CtrlShiftP = new("CtrlShiftP", typeof(MemberAdminWindow), [new KeyGesture(Key.P, ModifierKeys.Control | ModifierKeys.Shift)]);

        public PaymentVariantsWindow(int year) {
            InitializeComponent();
            CommandBindings.Add(new CommandBinding(CtrlL, Menu_ExportSave_Click));
            CommandBindings.Add(new CommandBinding(CtrlP, Menu_SummaryShow_Click));
            CommandBindings.Add(new CommandBinding(CtrlÜ, Menu_EbicsSave_Click));
            CommandBindings.Add(new CommandBinding(CtrlShiftP, Menu_SummaryPrint_Click));
            Year = year;
            using (var ctx = new AppDbContext()) {
                SeasonLocked = ctx.Seasons.Find(Year + 1) != null;
            }
            Title = $"Auszahlungsvarianten - Lese {Year} - Elwig";
            if (!App.Config.Debug) {
                DataInput.Visibility = Visibility.Hidden;
            }
            if (App.Client.IsMatzen) {
                ConsiderCustomInput.Content = ConsiderCustomInput.Content.ToString()?.Replace("Benutzerdefinierte", "Bentz.def.").Replace("Mitglied", "Mg.") + " (inkl. Treuebonus)";
            } else {
                MatzenNote.Visibility = Visibility.Hidden;
            }
        }

        protected override async Task OnRenewContext(AppDbContext ctx) {
            ControlUtils.RenewItemsSource(PaymentVariantList, await ctx.PaymentVariants
                .Where(v => v.Year == Year)
                .OrderBy(v => v.AvNr)
                .Include(v => v.Season.Currency)
                .ToListAsync());
            await Update();
        }

        private async Task Update() {
            if (PaymentVariantList.SelectedItem is PaymentVar v) {
                var locked = !v.TestVariant;
                DeleteButton.IsEnabled = !locked;
                CalculateButton.IsEnabled = !locked;
                CommitButton.IsEnabled = !locked && !SeasonLocked;
                CommitButton.Visibility = !locked ? Visibility.Visible : Visibility.Hidden;
                RevertButton.IsEnabled = locked && !SeasonLocked;
                RevertButton.Visibility = locked ? Visibility.Visible : Visibility.Hidden;
                Arrow3.Content = locked ? "\xF0B0" : "\xF0AF";
                Arrow4.Content = locked ? "\xF0B0" : "\xF0AF";
                CopyButton.IsEnabled = true;
                EditButton.Content = locked ? "Ansehen" : "Bearbeiten";
                EditButton.IsEnabled = true;
                SaveButton.IsEnabled = !locked;
                MailButton.IsEnabled = true;
                Menu_ExportSave.IsEnabled = locked;
                Menu_EbicsSave.IsEnabled = locked;
                Menu_SummaryExport.IsEnabled = true;
                Menu_SummaryShow.IsEnabled = true;
                Menu_SummarySave.IsEnabled = true;
                Menu_SummaryPrint.IsEnabled = true;

                NameInput.Text = v.Name;
                NameInput.IsReadOnly = false;
                CommentInput.Text = v.Comment;
                CommentInput.IsReadOnly = false;
                DateInput.Text = $"{v.Date:dd.MM.yyyy}";
                DateInput.IsReadOnly = false;
                TransferDateInput.Text = $"{v.TransferDate:dd.MM.yyyy}";
                TransferDateInput.IsReadOnly = false;
                try {
                    BillingData = BillingData.FromJson(v.Data);
                    ConsiderModifiersInput.IsChecked = BillingData.ConsiderDelieryModifiers;
                    ConsiderPenaltiesInput.IsChecked = BillingData.ConsiderContractPenalties;
                    ConsiderPenaltyInput.IsChecked = BillingData.ConsiderTotalPenalty;
                    ConsiderAutoInput.IsChecked = BillingData.ConsiderAutoBusinessShares;
                    ConsiderCustomInput.IsChecked = BillingData.ConsiderCustomModifiers;
                    if (BillingData.NetWeightModifier != 0) {
                        WeightModifierInput.Text = $"{Math.Round(BillingData.NetWeightModifier * 100.0, 8)}";
                    } else if (BillingData.GrossWeightModifier != 0) {
                        WeightModifierInput.Text = $"{Math.Round(BillingData.GrossWeightModifier * 100.0, 8)}";
                    } else {
                        WeightModifierInput.Text = "";
                    }
                    DataInput.Text = JsonSerializer.Serialize(BillingData.Data, JsonOpt);
                } catch {
                    BillingData = null;
                    ConsiderModifiersInput.IsChecked = false;
                    ConsiderPenaltiesInput.IsChecked = false;
                    ConsiderPenaltyInput.IsChecked = false;
                    ConsiderAutoInput.IsChecked = false;
                    ConsiderCustomInput.IsChecked = false;
                    WeightModifierInput.Text = "";
                    DataInput.Text = v.Data;
                }
                WeightModifierInput.IsReadOnly = false;
                ConsiderModifiersInput.IsEnabled = !locked;
                ConsiderPenaltiesInput.IsEnabled = !locked;
                ConsiderPenaltyInput.IsEnabled = !locked;
                ConsiderAutoInput.IsEnabled = !locked;
                ConsiderCustomInput.IsEnabled = !locked;
                DataInput.IsReadOnly = locked;
            } else {
                EditButton.Content = "Bearbeiten";
                EditButton.IsEnabled = false;
                SaveButton.IsEnabled = false;
                CopyButton.IsEnabled = false;
                CalculateButton.IsEnabled = false;
                CommitButton.IsEnabled = false;
                CommitButton.Visibility = Visibility.Visible;
                RevertButton.IsEnabled = false;
                RevertButton.Visibility = Visibility.Hidden;
                Arrow3.Content = "\xF0AF";
                Arrow4.Content = "\xF0AF";
                DeleteButton.IsEnabled = false;
                MailButton.IsEnabled = false;
                Menu_ExportSave.IsEnabled = false;
                Menu_EbicsSave.IsEnabled = false;
                Menu_SummaryExport.IsEnabled = false;
                Menu_SummaryShow.IsEnabled = false;
                Menu_SummarySave.IsEnabled = false;
                Menu_SummaryPrint.IsEnabled = false;

                BillingData = null;
                NameInput.Text = "";
                NameInput.IsReadOnly = true;
                CommentInput.Text = "";
                CommentInput.IsReadOnly = true;
                DateInput.Text = "";
                DateInput.IsReadOnly = true;
                TransferDateInput.Text = "";
                TransferDateInput.IsReadOnly = true;
                WeightModifierInput.Text = "";
                WeightModifierInput.IsReadOnly = true;
                ConsiderModifiersInput.IsChecked = false;
                ConsiderModifiersInput.IsEnabled = false;
                ConsiderPenaltiesInput.IsChecked = false;
                ConsiderPenaltiesInput.IsEnabled = false;
                ConsiderPenaltyInput.IsChecked = false;
                ConsiderPenaltyInput.IsEnabled = false;
                ConsiderAutoInput.IsChecked = false;
                ConsiderAutoInput.IsEnabled = false;
                ConsiderCustomInput.IsChecked = false;
                ConsiderCustomInput.IsEnabled = false;
                DataInput.Text = "";
                DataInput.IsReadOnly = true;
            }
            await UpdateSums();
            UpdateSaveButton();
        }

        private void UpdateSaveButton() {
            SaveButton.IsEnabled = PaymentVariantList.SelectedItem != null &&
                ((DataChanged && DataValid) || NameChanged || CommentChanged ||
                (DateChanged && DateValid) ||
                (TransferDateChanged && TransferDateValid) ||
                (ConsiderModifiersInput.IsChecked != BillingData?.ConsiderDelieryModifiers) ||
                (ConsiderPenaltiesInput.IsChecked != BillingData?.ConsiderContractPenalties) ||
                (ConsiderPenaltyInput.IsChecked != BillingData?.ConsiderTotalPenalty) ||
                (ConsiderAutoInput.IsChecked != BillingData?.ConsiderAutoBusinessShares) ||
                (ConsiderCustomInput.IsChecked != BillingData?.ConsiderCustomModifiers) ||
                WeightModifierChanged);
            CalculateButton.IsEnabled = !SaveButton.IsEnabled && PaymentVariantList.SelectedItem is PaymentVar { TestVariant: true };
            CommitButton.IsEnabled = CalculateButton.IsEnabled;
        }

        private async Task UpdateSums() {
            if (PaymentVariantList.SelectedItem is PaymentVar v) {
                using var ctx = new AppDbContext();
                var sym = v.Season.Currency.Symbol ?? v.Season.Currency.Code;
                var modSum = await ctx.PaymentDeliveryParts
                    .Where(p => p.Year == v.Year && p.AvNr == v.AvNr)
                    .SumAsync(p => p.AmountValue - p.NetAmountValue);
                ModifierSum.Text = $"{v.Season.DecFromDb(modSum):N2} {sym}";
                var totalSum = await ctx.MemberPayments
                    .Where(p => p.Year == v.Year && p.AvNr == v.AvNr)
                    .SumAsync(p => p.AmountValue);
                TotalSum.Text = $"{v.Season.DecFromDb(totalSum):N2} {sym}";
                var credits = ctx.Credits.Where(c => c.Year == v.Year && c.AvNr == v.AvNr);
                if (!credits.Any()) {
                    VatSum.Text = $"- {sym}";
                    DeductionSum.Text = $"- {sym}";
                    PaymentSum.Text = $"- {sym}";
                } else {
                    // all values in the credit table are stored with precision 2!
                    TotalSum.Text = $"{Utils.DecFromDb(await credits.SumAsync(c => c.NetAmountValue), 2):N2} {sym}";
                    VatSum.Text = $"{Utils.DecFromDb(await credits.SumAsync(c => c.VatAmountValue), 2):N2} {sym}";
                    DeductionSum.Text = $"{-Utils.DecFromDb(await credits.SumAsync(c => c.ModifiersValue ?? 0), 2):N2} {sym}";
                    PaymentSum.Text = $"{Utils.DecFromDb(await credits.SumAsync(c => c.AmountValue), 2):N2} {sym}";
                }
            } else {
                ModifierSum.Text = "-";
                TotalSum.Text = "-";
                VatSum.Text = "-";
                DeductionSum.Text = "-";
                PaymentSum.Text = "-";
            }
        }

        private async void PaymentVariantList_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
            await Update();
        }

        private async void AddButton_Click(object sender, RoutedEventArgs evt) {
            try {
                PaymentVar? v;
                using (var ctx = new AppDbContext()) {
                    v = new PaymentVar {
                        Year = Year,
                        AvNr = await ctx.NextAvNr(Year),
                        Name = "Neue Auszahlungsvariante",
                        TestVariant = true,
                        DateString = $"{DateTime.Today:yyyy-MM-dd}",
                        Data = "{\"mode\": \"elwig\", \"version\": 1, \"payment\": {}, \"curves\": []}",
                    };

                    ctx.Add(v);
                    await ctx.SaveChangesAsync();
                }

                App.HintContextChange();

                ControlUtils.SelectItem(PaymentVariantList, v);
            } catch (Exception exc) {
                var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
                if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
                MessageBox.Show(str, "Auszahlungsvariante erstellen", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        private async void CopyButton_Click(object sender, RoutedEventArgs evt) {
            if (PaymentVariantList.SelectedItem is not PaymentVar orig) return;
            try {
                PaymentVar? n;
                using (var ctx = new AppDbContext()) {
                    n = new PaymentVar {
                        Year = orig.Year,
                        AvNr = await ctx.NextAvNr(Year),
                        Name = $"{orig.Name} (Kopie)",
                        TestVariant = true,
                        DateString = $"{DateTime.Today:yyyy-MM-dd}",
                        Data = orig.Data,
                    };

                    ctx.Add(n);
                    await ctx.SaveChangesAsync();
                }

                App.HintContextChange();

                ControlUtils.SelectItem(PaymentVariantList, n);
            } catch (Exception exc) {
                var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
                if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
                MessageBox.Show(str, "Auszahlungsvariante kopieren", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        private async void DeleteButton_Click(object sender, RoutedEventArgs evt) {
            if (PaymentVariantList.SelectedItem is not PaymentVar v || !v.TestVariant) return;
            try {
                using (var ctx = new AppDbContext()) {
                    ctx.Remove(v);
                    await ctx.SaveChangesAsync();
                }
                App.HintContextChange();
            } catch (Exception exc) {
                var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
                if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
                MessageBox.Show(str, "Auszahlungsvariante löschen", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        private async void CalculateButton_Click(object sender, RoutedEventArgs evt) {
            if (PaymentVariantList.SelectedItem is not PaymentVar v)
                return;
            CalculateButton.IsEnabled = false;
            Mouse.OverrideCursor = Cursors.AppStarting;
            try {
                var b = new BillingVariant(v.Year, v.AvNr);
                await b.Calculate();
            } catch (Exception exc) {
                MessageBox.Show(exc.Message, "Berechnungsfehler", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            App.HintContextChange();
            Mouse.OverrideCursor = null;
            CalculateButton.IsEnabled = true;
        }

        private void EditButton_Click(object sender, RoutedEventArgs evt) {
            if (PaymentVariantList.SelectedItem is not PaymentVar v)
                return;
            App.FocusChartWindow(v.Year, v.AvNr);
        }

        private void PaymentAdjustmentButton_Click(object sender, RoutedEventArgs evt) {
            App.FocusPaymentAdjustment(Year);
        }

        private async void MailButton_Click(object sender, RoutedEventArgs evt) {
            if (PaymentVariantList.SelectedItem is not PaymentVar pv)
                return;
            using var ctx = new AppDbContext();
            var vars = await ctx.PaymentVariants
                .Where(v => pv.Year == v.Year)
                .OrderBy(v => v.AvNr)
                .Select(v => v.AvNr)
                .ToArrayAsync();
            var w = App.FocusMailWindow(pv.Year);
            w.AddCreditNote(Array.IndexOf(vars, pv.AvNr));
        }

        private async void Menu_SummaryExport_Click(object sender, RoutedEventArgs evt) {
            if (PaymentVariantList.SelectedItem is not PaymentVar v)
                return;
            var d = new SaveFileDialog() {
                FileName = $"Variantendaten-{v.Name.Trim().Replace(' ', '-')}.ods",
                DefaultExt = "ods",
                Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods",
                Title = $"Variantendaten {v.Name} speichern unter - Elwig"
            };
            if (d.ShowDialog() == false)
                return;
            Mouse.OverrideCursor = Cursors.AppStarting;
            try {
                using var ctx = new AppDbContext();
                var data = await PaymentVariantSummaryData.ForPaymentVariant(v, ctx.PaymentVariantSummaryRows);
                using var ods = new OdsFile(d.FileName);
                await ods.AddTable(data);
            } catch (Exception exc) {
                MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            Mouse.OverrideCursor = null;
        }

        private async void Menu_SummaryShow_Click(object sender, RoutedEventArgs evt) {
            if (PaymentVariantList.SelectedItem is not PaymentVar v)
                return;
            await GenerateSummary(v, ExportMode.Show);
        }

        private async void Menu_SummarySave_Click(object sender, RoutedEventArgs evt) {
            if (PaymentVariantList.SelectedItem is not PaymentVar v)
                return;
            await GenerateSummary(v, ExportMode.SavePdf);
        }

        private async void Menu_SummaryPrint_Click(object sender, RoutedEventArgs evt) {
            if (PaymentVariantList.SelectedItem is not PaymentVar v)
                return;
            await GenerateSummary(v, ExportMode.Print);
        }

        private static async Task GenerateSummary(PaymentVar v, ExportMode mode) {
            Mouse.OverrideCursor = Cursors.AppStarting;
            try {
                using var ctx = new AppDbContext();
                var data = await PaymentVariantSummaryData.ForPaymentVariant(v, ctx.PaymentVariantSummaryRows);
                using var doc = new PaymentVariantSummary((await ctx.PaymentVariants.FindAsync(v.Year, v.AvNr))!, data);
                await Utils.ExportDocument(doc, mode);
            } catch (Exception exc) {
                MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            Mouse.OverrideCursor = null;
        }

        private async void CommitButton_Click(object sender, RoutedEventArgs evt) {
            if (PaymentVariantList.SelectedItem is not PaymentVar v)
                return;
            CommitButton.IsEnabled = false;
            Mouse.OverrideCursor = Cursors.AppStarting;
            try {
                var b = new BillingVariant(v.Year, v.AvNr);
                await b.Commit();
            } catch (Exception exc) {
                MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            Mouse.OverrideCursor = null;
            RevertButton.IsEnabled = true;
            App.HintContextChange();
        }

        private async void RevertButton_Click(object sender, RoutedEventArgs evt) {
            if (PaymentVariantList.SelectedItem is not PaymentVar v)
                return;
            var res = MessageBox.Show(
                "Sollen wirklich alle festgesetzten Traubengutschriften der ausgewählten Auszahlungsvariante unwiderruflich gelöscht werden?\n\n" +
                "Dies ist im Allgemeinen nie empfohlen. Handelt es sich um die aktuellste Auszahlungsvariante könnte das eine Ausnahme sein.",
                "Traubengutschriften löschen", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
            if (res != MessageBoxResult.Yes)
                return;
            RevertButton.IsEnabled = false;
            Mouse.OverrideCursor = Cursors.AppStarting;
            var b = new BillingVariant(v.Year, v.AvNr);
            await b.Revert();
            App.HintContextChange();
            Mouse.OverrideCursor = null;
            CommitButton.IsEnabled = true;
        }

        private async void Menu_EbicsSave_Click(object sender, RoutedEventArgs evt) {
            if (PaymentVariantList.SelectedItem is not PaymentVar v) {
                return;
            } else if (v.TransferDate == null) {
                MessageBox.Show("Überweisungsdatum muss gesetzt sein!", "Exportieren nicht möglich", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            using var ctx = new AppDbContext();
            v = (await ctx.PaymentVariants.FindAsync(v.Year, v.AvNr))!;

            var withoutIban = v.Credits.Count(c => c.Member.Iban == null);
            if (withoutIban > 0) {
                var r = MessageBox.Show($"Achtung: Für {withoutIban:N0} Mitglieder ist kein IBAN hinterlegt.\n\nDiese werden NICHT exportiert.",
                    "Mitglieder ohne IBAN", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
                if (r != MessageBoxResult.OK) return;
            }
            var withNegAmount = v.Credits.Count(c => c.Amount <= 0);
            if (withNegAmount > 0) {
                var r = MessageBox.Show($"Achtung: Es gibt {withNegAmount:N0} Traubengutschriften mit negativem Betrag.\n\nDiese werden NICHT exportiert.",
                    "Traubengutschriften mit negativem Betrag", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.OK);
                if (r != MessageBoxResult.OK) return;
            }

            var d = new SaveFileDialog() {
                FileName = $"{App.Client.NameToken}-Überweisungsdaten-{v.Year}-{v.Name.Trim().Replace(' ', '-')}.{Ebics.FileExtension}",
                DefaultExt = Ebics.FileExtension,
                Filter = "EBICS-Datei (*.xml)|*.xml",
                Title = $"Überweisungsdaten speichern unter - Elwig",
            };
            if (d.ShowDialog() == true) {
                Menu_EbicsSave.IsEnabled = false;
                Mouse.OverrideCursor = Cursors.AppStarting;
                try {
                    using var e = new Ebics(v, d.FileName, App.Client.ExportEbicsVersion, (Ebics.AddressMode)App.Client.ExportEbicsAddress);
                    await e.ExportAsync(Transaction.FromPaymentVariant(v));
                } catch (Exception exc) {
                    MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
                }
                Mouse.OverrideCursor = null;
                Menu_EbicsSave.IsEnabled = true;
            }
        }

        private async void Menu_ExportSave_Click(object sender, RoutedEventArgs evt) {
            if (PaymentVariantList.SelectedItem is not PaymentVar v) {
                return;
            }
            var d = new SaveFileDialog() {
                FileName = $"{App.Client.NameToken}-Buchungsliste-{v.Year}-{v.Name.Trim().Replace(' ', '-')}.ods",
                DefaultExt = "ods",
                Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods",
                Title = $"Buchungsliste speichern unter - Elwig"
            };
            if (d.ShowDialog() == true) {
                Menu_ExportSave.IsEnabled = false;
                Mouse.OverrideCursor = Cursors.AppStarting;
                try {
                    using var ctx = new AppDbContext();
                    var tbl = await CreditNoteData.ForPaymentVariant(ctx, v.Year, v.AvNr);
                    using var ods = new OdsFile(d.FileName);
                    await ods.AddTable(tbl);
                } catch (Exception exc) {
                    MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
                }
                Mouse.OverrideCursor = null;
                Menu_ExportSave.IsEnabled = true;
            }
        }

        private async void SaveButton_Click(object sender, RoutedEventArgs evt) {
            if (PaymentVariantList.SelectedItem is not PaymentVar v || BillingData == null) return;
            try {
                v.Name = NameInput.Text;
                v.Comment = (CommentInput.Text != "") ? CommentInput.Text : null;
                v.DateString = string.Join("-", DateInput.Text.Split(".").Reverse());
                v.TransferDateString = (TransferDateInput.Text != "") ? string.Join("-", TransferDateInput.Text.Split(".").Reverse()) : null;
                var d = App.Config.Debug ? BillingData.FromJson(DataInput.Text) : BillingData;
                d.ConsiderDelieryModifiers = ConsiderModifiersInput.IsChecked ?? false;
                d.ConsiderContractPenalties = ConsiderPenaltiesInput.IsChecked ?? false;
                d.ConsiderTotalPenalty = ConsiderPenaltyInput.IsChecked ?? false;
                d.ConsiderAutoBusinessShares = ConsiderAutoInput.IsChecked ?? false;
                d.ConsiderCustomModifiers = ConsiderCustomInput.IsChecked ?? false;
                var modVal = WeightModifierInput.Text.Length > 0 ? double.Parse(WeightModifierInput.Text) : 0;
                d.NetWeightModifier = modVal > 0 ? modVal / 100.0 : 0;
                d.GrossWeightModifier = modVal < 0 ? modVal / 100.0 : 0;
                WeightModifierChanged = false;
                v.Data = JsonSerializer.Serialize(d.Data);

                using (var ctx = new AppDbContext()) {
                    ctx.Update(v);
                    await ctx.SaveChangesAsync();
                }

                App.HintContextChange();
                CommentInput_TextChanged(null, null);
                ConsiderModifiersInput_Changed(null, null);
                ConsiderPenaltiesInput_Changed(null, null);
                ConsiderPenaltyInput_Changed(null, null);
                ConsiderAutoInput_Changed(null, null);
                ConsiderCustomInput_Changed(null, null);
                WeightModifierInput_TextChanged(null, null);
            } catch (Exception exc) {
                await HintContextChange();
                var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
                if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
                MessageBox.Show(str, "Auszahlungsvariante aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        private void ModifierButton_Click(object sender, RoutedEventArgs evt) {
            App.FocusBaseDataSeason(Year);
        }

        private void NameInput_TextChanged(object sender, TextChangedEventArgs evt) {
            if (PaymentVariantList.SelectedItem is not PaymentVar v) {
                ControlUtils.ClearInputState(NameInput);
                return;
            }
            if (NameInput.Text != v.Name) {
                ControlUtils.SetInputChanged(NameInput);
                NameChanged = true;
            } else {
                ControlUtils.ClearInputState(NameInput);
                NameChanged = false;
            }
            UpdateSaveButton();
        }

        private void CommentInput_TextChanged(object? sender, TextChangedEventArgs? evt) {
            if (PaymentVariantList.SelectedItem is not PaymentVar v) {
                ControlUtils.ClearInputState(CommentInput);
                return;
            }
            if (CommentInput.Text != (v.Comment ?? "")) {
                ControlUtils.SetInputChanged(CommentInput);
                CommentChanged = true;
            } else {
                ControlUtils.ClearInputState(CommentInput);
                CommentChanged = false;
            }
            UpdateSaveButton();
        }

        private void DateInput_TextChanged(object sender, TextChangedEventArgs evt) {
            if (PaymentVariantList.SelectedItem is not PaymentVar v) {
                ControlUtils.ClearInputState(DateInput);
                return;
            }
            var res = Validator.CheckDate(DateInput, true);
            if (!res.IsValid) {
                ControlUtils.SetInputInvalid(DateInput);
                DateValid = false;
            } else if (DateInput.Text != $"{v.Date:dd.MM.yyyy}") {
                ControlUtils.SetInputChanged(DateInput);
                DateValid = true;
                DateChanged = true;
            } else {
                ControlUtils.ClearInputState(DateInput);
                DateValid = true;
                DateChanged = false;
            }
            UpdateSaveButton();
        }

        private void TransferDateInput_TextChanged(object sender, TextChangedEventArgs evt) {
            if (PaymentVariantList.SelectedItem is not PaymentVar v) {
                ControlUtils.ClearInputState(TransferDateInput);
                return;
            }
            var res = Validator.CheckDate(TransferDateInput, false);
            if (!res.IsValid) {
                ControlUtils.SetInputInvalid(TransferDateInput);
                TransferDateValid = false;
            } else if (TransferDateInput.Text != $"{v.TransferDate:dd.MM.yyyy}") {
                ControlUtils.SetInputChanged(TransferDateInput);
                TransferDateValid = true;
                TransferDateChanged = true;
            } else {
                ControlUtils.ClearInputState(TransferDateInput);
                TransferDateValid = true;
                TransferDateChanged = false;
            }
            UpdateSaveButton();
        }

        private void DataInput_TextChanged(object sender, TextChangedEventArgs evt) {
            if (PaymentVariantList.SelectedItem is not PaymentVar v) {
                ControlUtils.ClearInputState(DataInput);
                return;
            }
            try {
                var data = BillingData.FromJson(DataInput.Text);
                var origJson = v.Data;
                try {
                    origJson = JsonSerializer.Serialize(BillingData.FromJson(v.Data).Data);
                } catch { }
                DataValid = true;
                if (JsonSerializer.Serialize(data.Data) != origJson) {
                    ControlUtils.SetInputChanged(DataInput);
                    DataChanged = true;
                } else {
                    ControlUtils.ClearInputState(DataInput);
                    DataChanged = false;
                }
            } catch {
                ControlUtils.SetInputInvalid(DataInput);
                DataValid = false;
            }
            UpdateSaveButton();
        }

        private void ConsiderModifiersInput_Changed(object? sender, RoutedEventArgs? evt) {
            if (BillingData == null) {
                ControlUtils.ClearInputState(ConsiderModifiersInput);
                return;
            }
            if (BillingData.ConsiderDelieryModifiers != ConsiderModifiersInput.IsChecked) {
                ControlUtils.SetInputChanged(ConsiderModifiersInput);
            } else {
                ControlUtils.ClearInputState(ConsiderModifiersInput);
            }
            UpdateSaveButton();
        }

        private void ConsiderPenaltiesInput_Changed(object? sender, RoutedEventArgs? evt) {
            if (BillingData == null) {
                ControlUtils.ClearInputState(ConsiderPenaltiesInput);
                return;
            }
            if (BillingData.ConsiderContractPenalties != ConsiderPenaltiesInput.IsChecked) {
                ControlUtils.SetInputChanged(ConsiderPenaltiesInput);
            } else {
                ControlUtils.ClearInputState(ConsiderPenaltiesInput);
            }
            UpdateSaveButton();
        }

        private void ConsiderPenaltyInput_Changed(object? sender, RoutedEventArgs? evt) {
            if (BillingData == null) {
                ControlUtils.ClearInputState(ConsiderPenaltyInput);
                return;
            }
            if (BillingData.ConsiderTotalPenalty != ConsiderPenaltyInput.IsChecked) {
                ControlUtils.SetInputChanged(ConsiderPenaltyInput);
            } else {
                ControlUtils.ClearInputState(ConsiderPenaltyInput);
            }
            UpdateSaveButton();
        }

        private void ConsiderAutoInput_Changed(object? sender, RoutedEventArgs? evt) {
            if (BillingData == null) {
                ControlUtils.ClearInputState(ConsiderAutoInput);
                return;
            }
            if (BillingData.ConsiderAutoBusinessShares != ConsiderAutoInput.IsChecked) {
                ControlUtils.SetInputChanged(ConsiderAutoInput);
            } else {
                ControlUtils.ClearInputState(ConsiderAutoInput);
            }
            UpdateSaveButton();
        }

        private void ConsiderCustomInput_Changed(object? sender, RoutedEventArgs? evt) {
            if (BillingData == null) {
                ControlUtils.ClearInputState(ConsiderCustomInput);
                return;
            }
            if (BillingData.ConsiderCustomModifiers != ConsiderCustomInput.IsChecked) {
                ControlUtils.SetInputChanged(ConsiderCustomInput);
            } else {
                ControlUtils.ClearInputState(ConsiderCustomInput);
            }
            UpdateSaveButton();
        }

        private void WeightModifierInput_TextChanged(object? sender, TextChangedEventArgs? evt) {
            var res = Validator.CheckDecimal(WeightModifierInput, false, 3, 2, true);
            if (BillingData == null) {
                ControlUtils.ClearInputState(WeightModifierInput);
                return;
            }
            var val = WeightModifierInput.Text.Length > 0 && res.IsValid ? double.Parse(WeightModifierInput.Text) : 0;
            WeightModifierChanged = (val != Math.Round(BillingData.NetWeightModifier * 100.0, 8) && val != Math.Round(BillingData.GrossWeightModifier * 100.0, 8)) ||
                (val == 0 && (BillingData.NetWeightModifier != 0 || BillingData.GrossWeightModifier != 0));
            if (WeightModifierChanged) {
                ControlUtils.SetInputChanged(WeightModifierInput);
            } else {
                ControlUtils.ClearInputState(WeightModifierInput);
            }
            UpdateSaveButton();
        }

        private void WeightModifierInput_LostFocus(object sender, RoutedEventArgs evt) {
            if (WeightModifierInput.Text.EndsWith(','))
                WeightModifierInput.Text = WeightModifierInput.Text[..^1];
        }
    }
}