using Elwig.Documents;
using Elwig.Helpers;
using Elwig.Helpers.Export;
using Elwig.Helpers.Weighing;
using Elwig.Models.Entities;
using Elwig.Services;
using Elwig.ViewModels;
using Microsoft.EntityFrameworkCore;
using Microsoft.Win32;
using System;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Threading;

namespace Elwig.Windows {
    public partial class DeliveryAdminWindow : AdministrationWindow {

        public DeliveryAdminViewModel ViewModel => (DeliveryAdminViewModel)DataContext;

        private bool IsUpdatingGradation = false;
        private readonly DispatcherTimer SecondsTimer;

        private readonly RoutedCommand CtrlF = new("CtrlF", typeof(DeliveryAdminWindow), [new KeyGesture(Key.F, ModifierKeys.Control)]);
        private readonly RoutedCommand CtrlL = new("CtrlL", typeof(DeliveryAdminWindow), [new KeyGesture(Key.L, ModifierKeys.Control)]);
        private readonly RoutedCommand CtrlP = new("CtrlP", typeof(DeliveryAdminWindow), [new KeyGesture(Key.P, ModifierKeys.Control)]);
        private readonly RoutedCommand CtrlO = new("CtrlO", typeof(DeliveryAdminWindow), [new KeyGesture(Key.O, ModifierKeys.Control)]);
        private readonly RoutedCommand CtrlJ = new("CtrlJ", typeof(DeliveryAdminWindow), [new KeyGesture(Key.J, ModifierKeys.Control)]);
        private readonly RoutedCommand CtrlQ = new("CtrlQ", typeof(DeliveryAdminWindow), [new KeyGesture(Key.Q, ModifierKeys.Control)]);
        private readonly RoutedCommand CtrlShiftP = new("CtrlShiftP", typeof(DeliveryAdminWindow), [new KeyGesture(Key.P, ModifierKeys.Control | ModifierKeys.Shift)]);
        private readonly RoutedCommand CtrlShiftO = new("CtrlShiftO", typeof(DeliveryAdminWindow), [new KeyGesture(Key.O, ModifierKeys.Control | ModifierKeys.Shift)]);

        private readonly Button[] WeighingButtons;

        public DeliveryAdminWindow(bool receipt = false) {
            InitializeComponent();
            CommandBindings.Add(new CommandBinding(CtrlF, FocusSearchInput));
            CommandBindings.Add(new CommandBinding(CtrlL, Menu_DeliveryJournal_SaveFilters_Click));
            CommandBindings.Add(new CommandBinding(CtrlP, Menu_DeliveryNote_Show_Click));
            CommandBindings.Add(new CommandBinding(CtrlO, Menu_DeliveryJournal_ShowFilters_Click));
            CommandBindings.Add(new CommandBinding(CtrlJ, Menu_DeliveryJournal_PrintToday_Click));
            CommandBindings.Add(new CommandBinding(CtrlQ, Menu_WineQualityStatistics_PrintToday_Click));
            CommandBindings.Add(new CommandBinding(CtrlShiftP, Menu_DeliveryNote_Print_Click));
            CommandBindings.Add(new CommandBinding(CtrlShiftO, Menu_DeliveryJournal_PrintFilters_Click));
            RequiredInputs = [
                MgNrInput, MemberInput,
                LsNrInput, DateInput, BranchInput,
                SortIdInput, WineVarietyInput,
                GradationOeInput, GradationKmwInput, WineQualityLevelInput,
                WineOriginInput, WineKgInput,
                WeightInput, GerebeltGewogenInput,
            ];
            ExemptInputs = [
                SearchInput, SeasonInput, TodayOnlyInput, AllSeasonsInput,
                DeliveryList, DeliveryPartList,
                MemberAddressField,
            ];
            WeighingButtons = [
                WeighingAButton, WeighingBButton, WeighingCButton, WeighingDButton,
            ];
            ViewModel.IsReceipt = receipt;

            SecondsTimer = new DispatcherTimer();
            SecondsTimer.Tick += new EventHandler(OnSecondPassed);
            SecondsTimer.Interval = new TimeSpan(0, 0, 1);

            InitializeDelayTimer(SearchInput, SearchInput_TextChanged);
            SearchInput.TextChanged -= SearchInput_TextChanged;
            ViewModel.FilterSeason = Utils.CurrentLastSeason;

            DoShowWarningWindows = false;

            if (ViewModel.IsReceipt) {
                ViewModel.Title = $"Übernahme - {App.BranchName} - Elwig";
                ViewModel.FilterTodayOnly = true;
                var n = App.CommandScales.Count;
                if (n < 1) WeighingAButton.Visibility = Visibility.Hidden;
                if (n < 2) WeighingBButton.Visibility = Visibility.Hidden;
                if (n < 3) WeighingCButton.Visibility = Visibility.Hidden;
                if (n < 4) WeighingDButton.Visibility = Visibility.Hidden;
                if (n == 1) WeighingAButton.Content = "Wiegen";
                if (n > 1) WeighingAButton.Content = $"Wiegen {App.CommandScales[0].ScaleId}";
                if (n >= 2) WeighingBButton.Content = $"Wiegen {App.CommandScales[1].ScaleId}";
                if (n >= 3) WeighingCButton.Content = $"Wiegen {App.CommandScales[2].ScaleId}";
                if (n >= 4) WeighingDButton.Content = $"Wiegen {App.CommandScales[3].ScaleId}";
                WeighingManualButton.Margin = new Thickness(10, 10 + n * 32, 10, 10);
                foreach (var s in App.EventScales) {
                    s.WeighingEvent += Scale_Weighing;
                }
            } else {
                WeighingManualButton.Visibility = Visibility.Hidden;
                WeighingAButton.Visibility = Visibility.Hidden;
                WeighingBButton.Visibility = Visibility.Hidden;
                WeighingCButton.Visibility = Visibility.Hidden;
                WeighingDButton.Visibility = Visibility.Hidden;
            }

            Menu_WineQualityStatistics_ModeOe.IsChecked = false;
            Menu_WineQualityStatistics_ModeKmw1.IsChecked = false;
            Menu_WineQualityStatistics_ModeKmw2.IsChecked = false;
            Menu_WineQualityStatistics_ModeKmw5.IsChecked = false;
            Menu_WineQualityStatistics_ModeKmw10.IsChecked = false;
            switch (App.Client.OrderingMemberList) {
                case 0: Menu_WineQualityStatistics_ModeOe.IsChecked = true; break;
                case 1: Menu_WineQualityStatistics_ModeKmw1.IsChecked = true; break;
                case 2: Menu_WineQualityStatistics_ModeKmw2.IsChecked = true; break;
                case 3: Menu_WineQualityStatistics_ModeKmw5.IsChecked = true; break;
                case 4: Menu_WineQualityStatistics_ModeKmw10.IsChecked = true; break;
            }

            Menu_Export_UploadFilters.IsEnabled = App.Config.SyncUrl != null;
            Menu_Export_UploadSeason.IsEnabled = App.Config.SyncUrl != null;
        }

        public DeliveryAdminWindow(int mgnr) : this() {
            ViewModel.FilterMember = DeliveryService.GetMember(mgnr) ?? throw new ArgumentException("MgNr argument has invalid value");
            ViewModel.Title = $"Lieferungen - {ViewModel.FilterMember.AdministrativeName} - Elwig";
            ViewModel.EnableAllSeasons = true;
        }

        private void Window_Loaded(object sender, RoutedEventArgs evt) {
            OnSecondPassed(null, null);
            SecondsTimer.Start();
            LockInputs();
            if (ViewModel.IsReceipt) {
                NewDeliveryButton_Click(null, null);
                using var ctx = new AppDbContext();
                if (ctx.Seasons.Find(Utils.CurrentYear) == null) {
                    MessageBox.Show("Die Saison für das aktuelle Jahr wurde noch nicht erstellt. Neue Lieferungen können nicht abgespeichert werden.",
                        "Saison noch nicht erstellt", MessageBoxButton.OK, MessageBoxImage.Warning);
                }
            }
        }

        private async void Menu_DeliveryNote_Show_Click(object sender, RoutedEventArgs evt) {
            if (DeliveryList.SelectedItem is not Delivery d)
                return;
            await DeliveryService.GenerateDeliveryNote(d.Year, d.DId, ExportMode.Show);
        }

        private async void Menu_DeliveryNote_SavePdf_Click(object sender, RoutedEventArgs evt) {
            if (DeliveryList.SelectedItem is not Delivery d)
                return;
            await DeliveryService.GenerateDeliveryNote(d.Year, d.DId, ExportMode.SavePdf);
        }

        private async void Menu_DeliveryNote_Print_Click(object sender, RoutedEventArgs evt) {
            if (DeliveryList.SelectedItem is not Delivery d)
                return;
            await DeliveryService.GenerateDeliveryNote(d.Year, d.DId, ExportMode.Print);
        }

        private async void Menu_DeliveryNote_Email_Click(object sender, RoutedEventArgs evt) {
            if (DeliveryList.SelectedItem is not Delivery d)
                return;
            await DeliveryService.GenerateDeliveryNote(d.Year, d.DId, ExportMode.Email);
        }

        private async void Menu_Bki_SaveList_Click(object sender, RoutedEventArgs evt) {
            if (sender is not MenuItem m) return;
            var year = int.Parse(m.Header.ToString()?.Split(" ")[^1] ?? Utils.CurrentLastSeason.ToString());
            var d = new SaveFileDialog() {
                FileName = $"{App.Client.NameToken}-Traubentransportscheinliste-{year}.{Bki.FileExtension}",
                DefaultExt = Bki.FileExtension,
                Filter = "CSV-Datei (*.csv)|*.csv",
                Title = $"Traubentransportscheinliste (BKI) speichern unter - Elwig"
            };
            if (d.ShowDialog() == true) {
                Mouse.OverrideCursor = Cursors.AppStarting;
                using var file = new Bki(d.FileName);
                await file.ExportAsync(year);
                Mouse.OverrideCursor = null;
            }
        }

        private async void Menu_DeliveryJournal_SaveToday_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateDeliveryJournal(DeliveryService.ExportSubject.FromToday, ExportMode.SaveList);
        private async void Menu_DeliveryJournal_SavePdfToday_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateDeliveryJournal(DeliveryService.ExportSubject.FromToday, ExportMode.SavePdf);
        private async void Menu_DeliveryJournal_ShowToday_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateDeliveryJournal(DeliveryService.ExportSubject.FromToday, ExportMode.Show);
        private async void Menu_DeliveryJournal_PrintToday_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateDeliveryJournal(DeliveryService.ExportSubject.FromToday, ExportMode.Print);
        private async void Menu_DeliveryJournal_SaveFilters_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateDeliveryJournal(DeliveryService.ExportSubject.FromFilters, ExportMode.SaveList);
        private async void Menu_DeliveryJournal_SavePdfFilters_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateDeliveryJournal(DeliveryService.ExportSubject.FromFilters, ExportMode.SavePdf);
        private async void Menu_DeliveryJournal_ShowFilters_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateDeliveryJournal(DeliveryService.ExportSubject.FromFilters, ExportMode.Show);
        private async void Menu_DeliveryJournal_PrintFilters_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateDeliveryJournal(DeliveryService.ExportSubject.FromFilters, ExportMode.Print);

        private async void Menu_Export_ExportSeason_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateDeliveryJournal(DeliveryService.ExportSubject.FromSeasonAndBranch, ExportMode.Export);
        private async void Menu_Export_ExportFilters_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateDeliveryJournal(DeliveryService.ExportSubject.FromFilters, ExportMode.Export);
        private async void Menu_Export_ExportSelected_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateDeliveryJournal(DeliveryService.ExportSubject.Selected, ExportMode.Export);

        private async void Menu_Export_UploadSeason_Click(object sender, RoutedEventArgs evt) {
            if (App.Config.SyncUrl == null) return;
            await ViewModel.GenerateDeliveryJournal(DeliveryService.ExportSubject.FromSeasonAndBranch, ExportMode.Upload);
        }
        private async void Menu_Export_UploadFilters_Click(object sender, RoutedEventArgs evt) {
            if (App.Config.SyncUrl == null) return;
            await ViewModel.GenerateDeliveryJournal(DeliveryService.ExportSubject.FromFilters, ExportMode.Upload);
        }
        private async void Menu_Export_UploadSelected_Click(object sender, RoutedEventArgs evt) {
            if (App.Config.SyncUrl == null) return;
            await ViewModel.GenerateDeliveryJournal(DeliveryService.ExportSubject.FromSeasonAndBranch, ExportMode.Upload);
        }

        private async void Menu_WineQualityStatistics_ShowToday_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateWineQualityStatistics(DeliveryService.ExportSubject.FromToday, ExportMode.Show);
        private async void Menu_WineQualityStatistics_SavePdfToday_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateWineQualityStatistics(DeliveryService.ExportSubject.FromToday, ExportMode.SavePdf);
        private async void Menu_WineQualityStatistics_PrintToday_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateWineQualityStatistics(DeliveryService.ExportSubject.FromToday, ExportMode.Print);
        private async void Menu_WineQualityStatistics_ShowFilters_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateWineQualityStatistics(DeliveryService.ExportSubject.FromFilters, ExportMode.Show);
        private async void Menu_WineQualityStatistics_SavePdfFilters_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateWineQualityStatistics(DeliveryService.ExportSubject.FromFilters, ExportMode.SavePdf);
        private async void Menu_WineQualityStatistics_PrintFilters_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateWineQualityStatistics(DeliveryService.ExportSubject.FromFilters, ExportMode.Print);

        private async void Menu_WineQualityStatistics_Mode_Click(object sender, RoutedEventArgs evt) {
            Menu_WineQualityStatistics.IsSubmenuOpen = true;
            if (sender == Menu_WineQualityStatistics_ModeOe) {
                App.Client.OrderingMemberList = 0;
            } else if (sender == Menu_WineQualityStatistics_ModeKmw1) {
                App.Client.OrderingMemberList = 1;
            } else if (sender == Menu_WineQualityStatistics_ModeKmw2) {
                App.Client.OrderingMemberList = 2;
            } else if (sender == Menu_WineQualityStatistics_ModeKmw5) {
                App.Client.OrderingMemberList = 3;
            } else if (sender == Menu_WineQualityStatistics_ModeKmw10) {
                App.Client.OrderingMemberList = 4;
            }
            Menu_WineQualityStatistics_ModeOe.IsChecked = App.Client.OrderingMemberList == 0;
            Menu_WineQualityStatistics_ModeKmw1.IsChecked = App.Client.OrderingMemberList == 1;
            Menu_WineQualityStatistics_ModeKmw2.IsChecked = App.Client.OrderingMemberList == 2;
            Menu_WineQualityStatistics_ModeKmw5.IsChecked = App.Client.OrderingMemberList == 3;
            Menu_WineQualityStatistics_ModeKmw10.IsChecked = App.Client.OrderingMemberList == 4;
            await App.Client.UpdateValues();
        }

        private void Menu_Settings_EnableFreeEditing_Checked(object sender, RoutedEventArgs evt) {
            if (IsEditing || IsCreating) {
                DateInput.IsReadOnly = false;
                TimeInput.IsReadOnly = false;
                BranchInput.IsEnabled = true;
                if (IsCreating) ViewModel.Time = "";
                OnSecondPassed(null, null);
            }
        }

        private void Menu_Settings_EnableFreeEditing_Unchecked(object sender, RoutedEventArgs evt) {
            DateInput.IsReadOnly = true;
            TimeInput.IsReadOnly = true;
            BranchInput.IsEnabled = false;
            OnSecondPassed(null, null);
        }

        private void OnSecondPassed(object? sender, EventArgs? evt) {
            if (ViewModel.IsReceipt && IsCreating && !Menu_Settings_EnableFreeEditing.IsChecked) {
                var now = DateTime.Now;
                ViewModel.Time = now.ToString("HH:mm");
                ViewModel.Date = now.ToString("dd.MM.yyyy");
                SetDefaultValue(TimeInput);
                SetDefaultValue(DateInput);
            }
        }

        private void InitialDefaultInputs() {
            if (App.Client.HasNetWeighing(ViewModel.Branch)) {
                GerebeltGewogenInput.IsEnabled = false;
                SetDefaultValue(GerebeltGewogenInput, true);
            } else {
                GerebeltGewogenInput.IsEnabled = true;
                GerebeltGewogenInput.IsChecked = null;
                UnsetDefaultValue(GerebeltGewogenInput);
            }

            if (!App.Client.HasNetWeighing(ViewModel.Branch)) {
                LesewagenInput.IsEnabled = false;
                SetDefaultValue(LesewagenInput, false);
                HandPickedInput.IsThreeState = false;
                UnsetDefaultValue(HandPickedInput);
            } else {
                LesewagenInput.IsEnabled = true;
                UnsetDefaultValue(LesewagenInput);
                HandPickedInput.IsThreeState = true;
                SetDefaultValue(HandPickedInput, null);
            }

            if (App.Client.IsMatzen || App.Client.IsWinzerkeller) {
                GebundenInput.IsEnabled = false;
                SetDefaultValue(GebundenInput, null);
            } else {
                GebundenInput.IsEnabled = true;
                UnsetDefaultValue(GebundenInput);
            }
        }

        private void InitialInputs() {
            ViewModel.LastScaleError = null;
            ViewModel.WeighingData = null;
            ViewModel.ScaleId = null;
            ViewModel.ManualWeighingReason = null;

            ClearOriginalValues();
            ClearDefaultValues();

            ViewModel.IsNetWeight = App.Client.HasNetWeighing(ViewModel.Branch);
            ViewModel.IsLesewagen = false;
            ViewModel.IsHandPicked = !App.Client.HasNetWeighing(ViewModel.Branch) ? true : null;
            ViewModel.IsGebunden = null;
            InitialDefaultInputs();

            WineQualityLevelInput.IsEnabled = false;
            ValidateRequiredInputs();
        }

        private void InitInputs() {
            ControlUtils.SelectItemWithPk(BranchInput, App.ZwstId);
            OnSecondPassed(null, null);
            UpdateLsNr().GetAwaiter().GetResult();
            InitialInputs();
        }

        protected override void UpdateButtons() {
            if (!IsEditing && !IsCreating) return;
            bool ch = HasChanged, v = IsValid;
            ResetButton.IsEnabled = ch;
            SaveButton.IsEnabled = v && ch;
            FinishButton.IsEnabled = v && ch;
            NewDeliveryPartButton.IsEnabled = v && ch;
            CancelCreatingButton.IsEnabled = DeliveryList.SelectedItem == null || DeliveryPartList.SelectedItem == null;
        }

        private void Input_KeyUp(object sender, KeyEventArgs evt) {
            if (sender is not Control ctrl) return;
            if (evt.Key != Key.Enter) return;
            if (ctrl == MgNrInput || ctrl == MemberInput) {
                SortIdInput.Focus();
                SortIdInput.SelectAll();
            } else if (ctrl == SortIdInput || ctrl == WineVarietyInput || ctrl == AttributeInput || ctrl == CultivationInput) {
                GradationOeInput.Focus();
                GradationOeInput.SelectAll();
            } else if (ctrl == GradationKmwInput || ctrl == GradationOeInput || ctrl == WineQualityLevelInput) {
                if (WeighingAButton.IsVisible) WeighingAButton.Focus();
                else WeighingManualButton.Focus();
            }
        }

        private async Task RefreshList(bool updateSort = false) {
            using var ctx = new AppDbContext();
            var (_, deliveryQuery, deliveryPartsQuery, predicate, filter) = await ViewModel.GetFilters(ctx);
            var deliveries = await deliveryQuery
                .Include(d => d.Parts).ThenInclude(p => p.PartModifiers).ThenInclude(m => m.Modifier)
                .Include(d => d.Parts).ThenInclude(p => p.Attribute)
                .Include(d => d.Parts).ThenInclude(p => p.Cultivation)
                .Include(d => d.Parts).ThenInclude(p => p.Variety)
                .Include(d => d.Member.EmailAddresses)
                .AsSplitQuery()
                .ToListAsync();
            deliveries.Reverse();

            if (filter.Count > 0 && deliveries.Count > 0) {
                var dict = deliveries.AsParallel()
                    .ToDictionary(d => d, d => d.SearchScore(ViewModel.TextFilter))
                    .OrderByDescending(a => a.Value)
                    .ThenBy(a => a.Key.DateTime);
                var threshold = dict.Select(a => a.Value).Max() * 3 / 4;
                deliveries = dict
                    .Where(a => a.Value > threshold)
                    .Select(a => a.Key)
                    .ToList();
            }

            deliveries.ForEach(d => { d.PartFilter = predicate; });
            ControlUtils.RenewItemsSource(DeliveryList, deliveries,
                DeliveryList_SelectionChanged, filter.Count > 0 ? ControlUtils.RenewSourceDefault.IfOnly : ControlUtils.RenewSourceDefault.None, !updateSort);
            await RefreshDeliveryParts();

            var members = deliveries.Select(d => d.Member).DistinctBy(m => m.MgNr).ToList();
            ViewModel.StatusMembers = $"{members.Count}" + (members.Count > 0 && members.Count <= 4 ? $" ({string.Join(", ", members.Select(m => m.AdministrativeName))})" : "");
            ViewModel.StatusDeliveries = $"{deliveries.Count}";

            if (filter.Count == 0) {
                var deliveryParts = deliveryPartsQuery;
                ViewModel.StatusDeliveries = $"{deliveries.Count} ({await deliveryParts.CountAsync()})";
                var varieties = await deliveryParts.Select(d => d.SortId).Distinct().ToListAsync();
                ViewModel.StatusVarieties = $"{varieties.Count}" + (varieties.Count > 0 && varieties.Count <= 10 ? $" ({string.Join(", ", varieties)})" : "");
                (ViewModel.StatusWeight, ViewModel.StatusWeightToolTip,
                 ViewModel.StatusGradation, ViewModel.StatusGradationToolTip) = await DeliveryService.GenerateToolTip(deliveryParts);
            } else {
                ViewModel.StatusVarieties = "-";
                ViewModel.StatusWeight = "-";
                ViewModel.StatusGradation = "-";
            }

            if (updateSort && DeliveryList.SelectedItem != null)
                DeliveryList.ScrollIntoView(DeliveryList.SelectedItem);
        }

        protected override async Task OnRenewContext(AppDbContext ctx) {
            await base.OnRenewContext(ctx);

            if (ViewModel.FilterMember != null) {
                if (await DeliveryService.GetMemberAsync(ViewModel.FilterMember.MgNr) is not Member m) {
                    Close();
                    return;
                }
                ViewModel.FilterMember = m;
                ViewModel.Title = $"Lieferungen - {ViewModel.FilterMember.AdministrativeName} - Elwig";
            }

            Menu_Bki_SaveList.Items.Clear();
            foreach (var s in await ctx.Seasons.OrderByDescending(s => s.Year).ToListAsync()) {
                var i = new MenuItem {
                    Header = $"Saison {s.Year}",
                };
                i.Click += Menu_Bki_SaveList_Click;
                Menu_Bki_SaveList.Items.Add(i);
            }

            await RefreshList();
            var d = DeliveryList.SelectedItem as Delivery;
            var y = d?.Year ?? ViewModel.FilterSeason;
            ControlUtils.RenewItemsSource(MemberInput, await ctx.Members
                .Where(m => m.IsActive || !IsCreating)
                .Include(m => m.PostalDest.AtPlz!.Ort)
                .Include(m => m.DefaultWbKg!.AtKg)
                .OrderBy(m => m.Name)
                .ThenBy(m => m.GivenName)
                .ToListAsync());
            ControlUtils.RenewItemsSource(BranchInput, await ctx.Branches.OrderBy(b => b.Name).ToListAsync());
            ControlUtils.RenewItemsSource(WineVarietyInput, await ctx.WineVarieties.OrderBy(v => v.Name).ToListAsync());
            var attrList = await ctx.WineAttributes.Where(a => !IsCreating || a.IsActive).OrderBy(a => a.Name).Cast<object>().ToListAsync();
            attrList.Insert(0, new NullItem(""));
            ControlUtils.RenewItemsSource(AttributeInput, attrList, null, ControlUtils.RenewSourceDefault.First);
            var cultList = await ctx.WineCultivations.OrderBy(a => a.Name).Cast<object>().ToListAsync();
            cultList.Insert(0, new NullItem(""));
            ControlUtils.RenewItemsSource(CultivationInput, cultList, null, ControlUtils.RenewSourceDefault.First);
            ControlUtils.RenewItemsSource(WineQualityLevelInput, await ctx.WineQualityLevels.ToListAsync());
            ControlUtils.RenewItemsSource(ModifiersInput, await ctx.Modifiers
                .Where(m => m.Year == y && (!IsCreating || m.IsActive))
                .OrderBy(m => m.Ordering)
                .Include(m => m.Season.Currency)
                .ToListAsync());
            ControlUtils.RenewItemsSource(WineOriginInput, (await ctx.WineOrigins.ToListAsync()).OrderByDescending(o => o.SortKey).ThenBy(o => o.HkId));
            var kgList = (await ctx.Katastralgemeinden
                .Where(k => k.WbKg != null)
                .Include(k => k.WbKg)
                .Include(k => k.Gem.WbGem)
                .OrderBy(k => k.Name)
                .AsSplitQuery()
                .ToListAsync()).Cast<object>().ToList();
            kgList.Insert(0, new NullItem());
            ControlUtils.RenewItemsSource(WineKgInput, kgList);
            UpdateRdInput();
            if (IsCreating) await UpdateLsNr();

            await RefreshDeliveryParts();
            RefreshInputs();
        }

        private void FocusSearchInput(object sender, RoutedEventArgs evt) {
            if (!IsEditing && !IsCreating) {
                SearchInput.Focus();
                SearchInput.SelectAll();
            }
        }

        private async Task RefreshDeliveryParts() {
            using var ctx = new AppDbContext();
            if (DeliveryList.SelectedItem is Delivery d) {
                ControlUtils.RenewItemsSource(ModifiersInput, await ctx.Modifiers
                    .Where(m => m.Year == d.Year && (!IsCreating || m.IsActive))
                    .OrderBy(m => m.Ordering)
                    .Include(m => m.Season.Currency)
                    .ToListAsync());
                ControlUtils.RenewItemsSource(DeliveryPartList, d.FilteredParts.OrderBy(p => p.DPNr).ToList(), DeliveryPartList_SelectionChanged, ControlUtils.RenewSourceDefault.First);
            } else {
                ControlUtils.RenewItemsSource(ModifiersInput, await ctx.Modifiers
                    .Where(m => m.Year == ViewModel.FilterSeason && (!IsCreating || m.IsActive))
                    .OrderBy(m => m.Ordering)
                    .Include(m => m.Season.Currency)
                    .ToListAsync());
                DeliveryPartList.ItemsSource = null;
            }
        }

        private void RefreshInputs(bool validate = false) {
            ClearInputStates();
            if (DeliveryPartList.SelectedItem is DeliveryPart p) {
                FillInputs(p);
            } else if (DeliveryList.SelectedItem is Delivery d) {
                FillInputs(d);
            } else {
                ClearOriginalValues();
                ClearDefaultValues();
                ClearInputs(validate);
                ClearInputStates();
            }            
            GC.Collect();
        }

        private void FillInputs(Delivery d) {
            ClearOriginalValues();
            ClearDefaultValues();
            ViewModel.FillInputs(d);
            FinishInputFilling();
        }

        private void FillInputs(DeliveryPart p) {
            FillInputs(p.Delivery);
            ClearOriginalValues();
            ClearDefaultValues();
            ViewModel.FillInputs(p);
            FinishInputFilling();
        }

        new protected void ClearInputs(bool validate = false) {
            ViewModel.ClearInputs();
            base.ClearInputs(validate);
        }

        private void WeighingButton_Click(object sender, RoutedEventArgs evt) {
            int index = Array.IndexOf(WeighingButtons, sender as Button);
            if (index >= 0) WeighingButton_Click(index);
        }

        private async void WeighingButton_Click(int index) {
            DisableWeighingButtons();
            var start = DateTimeOffset.Now.ToUnixTimeMilliseconds();
            FinishButton.IsEnabled = false;
            NewDeliveryPartButton.IsEnabled = false;
            CancelCreatingButton.IsEnabled = false;
            var s = App.CommandScales[index];
            try {
                var res = await s.Weigh();
                OnWeighingResult(s, res);
            } catch (Exception ex) {
                ViewModel.LastScaleError = ex.Message.Split(": ")[^1];
                OnWeighingResult(s, new());
                MessageBox.Show($"Beim Wiegen ist ein Fehler aufgetreten:\n\n{ex.Message}", "Waagenfehler",
                       MessageBoxButton.OK, MessageBoxImage.Error);
            }
            ViewModel.ManualWeighingReason = null;
            ManualWeighingInput.IsChecked = false;
            var end = DateTimeOffset.Now.ToUnixTimeMilliseconds();
            int diff = (int)(end - start);
            if (diff < 1000 && ViewModel.Weight != null) await Task.Delay(1000 - diff);
            EnableWeighingButtons();
        }

        private void OnWeighingResult(IScale scale, WeighingResult res) {
            if ((res.NetWeight ?? 0) > 0 && res.FullWeighingId != null) {
                ViewModel.Weight = res.NetWeight;
                ViewModel.ScaleId = scale.ScaleId;
                ViewModel.WeighingData = res.ToJson().ToJsonString();
                ViewModel.ManualWeighingReason = null;
                ManualWeighingInput.IsChecked = false;
            } else {
                ViewModel.Weight = null;
                ViewModel.ScaleId = null;
                ViewModel.WeighingData = null;
            }
            ViewModel.LastScaleError = null;
            TextBox_TextChanged(WeightInput, null);
            UpdateButtons();
        }

        private void Scale_Weighing(object sender, WeighingEventArgs evt) {
            if (sender is not IScale scale) return;
            App.MainDispatcher.BeginInvoke(() => OnWeighingResult(scale, evt.Result));
        }

        private async void SearchInput_TextChanged(object sender, RoutedEventArgs evt) {
            await RefreshList(true);
        }

        private async void SeasonInput_TextChanged(object sender, TextChangedEventArgs evt) {
            if (ViewModel.FilterSeason == null || TodayOnlyInput == null || AllSeasonsInput == null) return;
            TodayOnlyInput.IsChecked = false;
            AllSeasonsInput.IsChecked = false;
            await RefreshList();
        }

        private async void TodayOnlyInput_Changed(object sender, RoutedEventArgs evt) {
            if (TodayOnlyInput.IsChecked == true && AllSeasonsInput.IsChecked == false) {
                ViewModel.FilterSeason = Utils.Today.Year;
                ViewModel.FilterTodayOnly = true;
            }
            await RefreshList();
        }

        private async void AllSeasonsInput_Changed(object sender, RoutedEventArgs evt) {
            if (AllSeasonsInput.IsChecked == true) {
                SeasonInput.IsEnabled = false;
                ViewModel.FilterSeason = null;
            } else {
                SeasonInput.IsEnabled = true;
                var today = ViewModel.FilterTodayOnly;
                ViewModel.FilterSeason = Utils.CurrentLastSeason;
                ViewModel.FilterTodayOnly = today;
            }
            await RefreshList();
        }

        private async void DeliveryList_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
            await RefreshDeliveryParts();
            if (DeliveryList.SelectedItem is Delivery d) {
                DeleteDeliveryButton.IsEnabled = true;
                Menu_DeliveryNote_Show.IsEnabled = !IsEditing && !IsCreating;
                Menu_DeliveryNote_SavePdf.IsEnabled = !IsEditing && !IsCreating;
                Menu_DeliveryNote_Print.IsEnabled = !IsEditing && !IsCreating;
                Menu_DeliveryNote_Email.IsEnabled = !IsEditing && !IsCreating && App.Config.Smtp != null && d.Member.EmailAddresses.Count > 0;
                Menu_Export_ExportSelected.IsEnabled = !IsEditing && !IsCreating;
                Menu_Export_UploadSelected.IsEnabled = !IsEditing && !IsCreating && App.Config.SyncUrl != null;
            } else {
                DeleteDeliveryButton.IsEnabled = false;
                Menu_DeliveryNote_Show.IsEnabled = false;
                Menu_DeliveryNote_SavePdf.IsEnabled = false;
                Menu_DeliveryNote_Print.IsEnabled = false;
                Menu_DeliveryNote_Email.IsEnabled = false;
                Menu_Export_ExportSelected.IsEnabled = false;
                Menu_Export_UploadSelected.IsEnabled = false;
            }
        }

        private void DeliveryPartList_SelectionChanged(object? sender, SelectionChangedEventArgs? evt) {
            RefreshInputs();
            if (DeliveryPartList.SelectedItem is DeliveryPart p) {
                AbwertenButton.IsEnabled = p.QualId != "WEI";
                EditDeliveryButton.IsEnabled = true;
                ExtractDeliveryPartButton.IsEnabled = !IsCreating;
                DeleteDeliveryPartButton.IsEnabled = DeliveryList.SelectedItem is Delivery { Parts.Count: > 1 } && !IsCreating;
            } else {
                AbwertenButton.IsEnabled = false;
                EditDeliveryButton.IsEnabled = false;
                ExtractDeliveryPartButton.IsEnabled = false;
                DeleteDeliveryPartButton.IsEnabled = false;
            }
        }

        private void MgNrInput_TextChanged(object sender, TextChangedEventArgs evt) {
            var valid = InputTextChanged((TextBox)sender, Validator.CheckMgNr);
            var text = MgNrInput.Text;
            var caret = MgNrInput.CaretIndex;
            ControlUtils.SelectItemWithPk(MemberInput, valid ? ViewModel.MgNr : null);
            MgNrInput.Text = text;
            MgNrInput.CaretIndex = caret;
        }

        private void MgNrInput_LostFocus(object sender, RoutedEventArgs evt) {
            var valid = InputLostFocus((TextBox)sender, Validator.CheckMgNr);
            ControlUtils.SelectItemWithPk(MemberInput, valid ? ViewModel.MgNr : null);
        }

        private void MemberInput_SelectionChanged(object? sender, SelectionChangedEventArgs? evt) {
            var m = MemberInput.SelectedItem as Member;
            ViewModel.MgNr = m?.MgNr;
            ViewModel.MemberAddress = m?.FullAddress;
            if (m == null) {
                UnsetDefaultValue(WineKgInput);
                WineKgInput.SelectedItem = null;
                ComboBox_SelectionChanged(WineKgInput, null);
            } else {
                if (m.DefaultKgNr is int kgnr) {
                    ControlUtils.SelectItemWithPk(WineKgInput, kgnr);
                } else {
                    WineKgInput.SelectedItem = null;
                }
                SetDefaultValue(WineKgInput);
                ComboBox_SelectionChanged(WineKgInput, null);
            }
        }

        private void EmptyScale() {
            var scale = App.Scales.Where(s => s.ScaleId == ViewModel.ScaleId).FirstOrDefault();
            if (scale is not ICommandScale cs) return;
            cs.Empty();
        }

        private async void NewDeliveryPartButton_Click(object sender, RoutedEventArgs evt) {
            FinishButton.IsEnabled = false;
            NewDeliveryPartButton.IsEnabled = false;
            Mouse.OverrideCursor = Cursors.AppStarting;
            DeliveryPartList.IsEnabled = false;
            var p = await ViewModel.UpdateDeliveryPart(
                (DeliveryList.SelectedItem as Delivery)?.Year,
                (DeliveryList.SelectedItem as Delivery)?.DId,
                (DeliveryPartList.SelectedItem as DeliveryPart)?.DPNr,
                InputHasChanged(DateInput),
                InputHasChanged(TimeInput),
                !InputIsNotDefault(TimeInput)
            );
            EmptyScale();
            await RefreshList();
            await RefreshDeliveryParts();
            Mouse.OverrideCursor = null;
            ControlUtils.SelectItem(DeliveryList, p?.Delivery);
            DeliveryPartList.SelectedItem = null;
            DeliveryPartList.ScrollIntoView(DeliveryPartList.ItemsSource.Cast<object>().Last());
            RefreshInputs();
            InitialInputs();
        }

        private async void FinishButton_Click(object sender, RoutedEventArgs evt) {
            FinishButton.IsEnabled = false;
            NewDeliveryPartButton.IsEnabled = false;
            Mouse.OverrideCursor = Cursors.AppStarting;
            DeliveryPartList.IsEnabled = false;
            var p = await ViewModel.UpdateDeliveryPart(
                (DeliveryList.SelectedItem as Delivery)?.Year,
                (DeliveryList.SelectedItem as Delivery)?.DId,
                (DeliveryPartList.SelectedItem as DeliveryPart)?.DPNr,
                InputHasChanged(DateInput),
                InputHasChanged(TimeInput),
                !InputIsNotDefault(TimeInput)
            );
            EmptyScale();
            await RefreshList();
            await RefreshDeliveryParts();
            if (p?.Delivery != null) {
                try {
                    using var ctx = new AppDbContext();
                    using var doc = new DeliveryNote((await ctx.Deliveries.FindAsync(p.Year, p.DId))!, ctx);
                    await doc.Generate();
                    if (App.Config.Debug) {
                        doc.Show();
                    } else {
                        await doc.Print(2);
                    }
                } catch (Exception exc) {
                    MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
            Mouse.OverrideCursor = null;
            DeliveryList.SelectedItem = null;
            await RenewContext();
            RefreshInputs();
            InitInputs();
        }

        private async void CancelCreatingButton_Click(object sender, RoutedEventArgs evt) {
            if (IsCreating && HasChanged) {
                var r = MessageBox.Show("Soll der Vorgang wirklich abgebrochen werden?", "Abbrechen bestätigen",
                    MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
                if (r != MessageBoxResult.Yes) return;
            }

            using var ctx = new AppDbContext();
            var attrList = await ctx.WineAttributes.OrderBy(a => a.Name).Cast<object>().ToListAsync();
            attrList.Insert(0, new NullItem(""));
            ControlUtils.RenewItemsSource(AttributeInput, attrList, null, ControlUtils.RenewSourceDefault.First);
            ControlUtils.RenewItemsSource(MemberInput, await ctx.Members
                .Where(m => m.IsActive || !ViewModel.IsReceipt)
                .Include(m => m.PostalDest.AtPlz!.Ort)
                .Include(m => m.DefaultWbKg!.AtKg)
                .OrderBy(m => m.Name)
                .ThenBy(m => m.GivenName)
                .ToListAsync());
            if (DeliveryList.SelectedItem is not Delivery d) {
                // switch away from creating mode
                IsCreating = false;
                IsEditing = false;
                DeliveryList.IsEnabled = true;
                DeliveryPartList.IsEnabled = true;
                DisableWeighingButtons();
                HideFinishNewPartDeliveryCancelButtons();
                ShowNewEditDeleteButtons();
                await RenewContext();
                RefreshInputs();
                ClearInputStates();
                LockInputs();
                UnlockSearchInputs();
            } else {
                // switch to last delivery part
                DeliveryPartList.IsEnabled = true;
                ControlUtils.SelectItem(DeliveryPartList, d.FilteredParts.Last());
            }
        }

        protected override void ShortcutNew() {
            if (!NewDeliveryButton.IsEnabled || NewDeliveryButton.Visibility != Visibility.Visible)
                return;
            NewDeliveryButton_Click(null, null);
        }

        private async void NewDeliveryButton_Click(object? sender, RoutedEventArgs? evt) {
            ViewModel.FilterTodayOnly = true;
            ViewModel.SearchQuery = "";
            using var ctx = new AppDbContext();
            var attrList = await ctx.WineAttributes.Where(a => a.IsActive).OrderBy(a => a.Name).Cast<object>().ToListAsync();
            attrList.Insert(0, new NullItem(""));
            ControlUtils.RenewItemsSource(AttributeInput, attrList, null, ControlUtils.RenewSourceDefault.First);
            ControlUtils.RenewItemsSource(ModifiersInput, await ctx.Modifiers
                .Where(m => m.Year == ViewModel.FilterSeason && m.IsActive)
                .OrderBy(m => m.Ordering)
                .Include(m => m.Season.Currency)
                .ToListAsync());
            ControlUtils.RenewItemsSource(MemberInput, await ctx.Members
                .Where(m => m.IsActive || !ViewModel.IsReceipt)
                .Include(m => m.PostalDest.AtPlz!.Ort)
                .Include(m => m.DefaultWbKg!.AtKg)
                .OrderBy(m => m.Name)
                .ThenBy(m => m.GivenName)
                .ToListAsync());
            IsCreating = true;
            DeliveryList.IsEnabled = false;
            DeliveryPartList.IsEnabled = false;
            EnableWeighingButtons();
            DeliveryList.SelectedItem = null;
            HideNewEditDeleteButtons();
            UnlockInputs();
            InitInputs();
            ShowFinishNewPartDeliveryCancelButtons();
            LockSearchInputs();
        }

        private async void AbwertenButton_Click(object sender, RoutedEventArgs evt) {
            if (DeliveryPartList.SelectedItem is not DeliveryPart p) return;
            var res = Utils.ShowAbwertenDialog($"{p.Delivery.LsNr}/{p.DPNr}", p.Delivery.Member.AdministrativeName, p.Weight);
            if (res == null || res <= 0)
                return;

            Mouse.OverrideCursor = Cursors.AppStarting;
            try {
                using var ctx = new AppDbContext();
                ClearOriginalValues();
                if (res >= p.Weight) {
                    ControlUtils.SelectItemWithPk(WineQualityLevelInput, "WEI");
                    ControlUtils.SelectItemWithPk(WineOriginInput, "OEST");
                    p.QualId = "WEI";
                    p.HkId = "OEST";
                    ctx.Update(p);
                } else {
                    var w = p.Weight - res.Value;
                    ViewModel.Weight = w;
                    p.Weight = w;
                    ctx.Update(p);

                    var d = p.Delivery;
                    var p2 = ctx.CreateProxy<DeliveryPart>();
                    var values = ctx.Entry(p).CurrentValues;
                    ctx.Entry(p2).CurrentValues.SetValues(values);
                    p2.DPNr = await ctx.NextDPNr(d.Year, d.DId);
                    p2.Weight = res.Value;
                    p2.QualId = "WEI";
                    p2.HkId = "OEST";
                    ctx.Add(p2);

                    ctx.UpdateDeliveryPartModifiers(p2, [], p.Modifiers);
                }
                await ctx.SaveChangesAsync();
                await RefreshDeliveryParts();
                FinishInputFilling();
            } 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, "Lieferung aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            Mouse.OverrideCursor = null;
        }

        private void WeighingManualButton_Click(object sender, RoutedEventArgs evt) {
            var res = Utils.ShowManualWeighingDialog(ViewModel.LastScaleError);
            if (res == null) return;
            ViewModel.Weight = res?.Item1;
            ManualWeighingInput.IsChecked = true;
            ViewModel.ManualWeighingReason = res?.Item2;
            ViewModel.ScaleId = null;
            ViewModel.WeighingData = null;
        }

        protected override void ShortcutEdit() {
            if (!EditDeliveryButton.IsEnabled || EditDeliveryButton.Visibility != Visibility.Visible)
                return;
            EditDeliveryButton_Click(null, null);
        }

        private void EditDeliveryButton_Click(object? sender, RoutedEventArgs? evt) {
            if (DeliveryPartList.SelectedItem == null)
                return;

            IsEditing = true;
            DeliveryList.IsEnabled = false;
            DeliveryPartList.IsEnabled = false;

            HideNewEditDeleteButtons();
            ShowSaveResetCancelButtons();
            UnlockInputs();
            LockSearchInputs();

            AbwertenButton.IsEnabled = false;
            ExtractDeliveryPartButton.IsEnabled = false;
            DeleteDeliveryPartButton.IsEnabled = false;
        }

        protected override void ShortcutDelete() {
            if (!DeleteDeliveryButton.IsEnabled || DeleteDeliveryButton.Visibility != Visibility.Visible)
                return;
            DeleteDeliveryButton_Click(null, null);
        }

        private async void DeleteDeliveryButton_Click(object? sender, RoutedEventArgs? evt) {
            if (DeliveryList.SelectedItem is not Delivery d)
                return;

            var r = MessageBox.Show(
                $"Soll die Lieferung {d.LsNr} ({d.Member.AdministrativeName}, MgNr. {d.Member.MgNr}) wirklich unwiderruflich gelöscht werden?",
                "Lieferung löschen", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
            if (r == MessageBoxResult.OK) {
                Mouse.OverrideCursor = Cursors.AppStarting;
                using (var ctx = new AppDbContext()) {
                    ctx.Remove(d);
                    await ctx.SaveChangesAsync();
                }
                await RefreshList();
                await RefreshDeliveryParts();
                Mouse.OverrideCursor = null;
            }
        }

        protected override void ShortcutSave() {
            if (!SaveButton.IsEnabled || SaveButton.Visibility != Visibility.Visible)
                return;
            SaveButton_Click(null, null);
        }

        private async void SaveButton_Click(object? sender, RoutedEventArgs? evt) {
            SaveButton.IsEnabled = false;
            Mouse.OverrideCursor = Cursors.AppStarting;

            IsEditing = false;
            IsCreating = false;
            DeliveryList.IsEnabled = true;
            DeliveryPartList.IsEnabled = true;

            var p = await ViewModel.UpdateDeliveryPart(
                (DeliveryList.SelectedItem as Delivery)?.Year,
                (DeliveryList.SelectedItem as Delivery)?.DId,
                (DeliveryPartList.SelectedItem as DeliveryPart)?.DPNr,
                InputHasChanged(DateInput),
                InputHasChanged(TimeInput),
                !InputIsNotDefault(TimeInput)
            );

            Mouse.OverrideCursor = null;

            HideSaveResetCancelButtons();
            ShowNewEditDeleteButtons();
            LockInputs();
            UnlockSearchInputs();
            FinishInputFilling();
            await RefreshList();
            await RefreshDeliveryParts();
            RefreshInputs();

            AbwertenButton.IsEnabled = p.QualId != "WEI";
            ExtractDeliveryPartButton.IsEnabled = DeliveryPartList.SelectedItem != null && !IsCreating;
            DeleteDeliveryPartButton.IsEnabled = DeliveryList.SelectedItem is Delivery { Parts.Count: > 1 } && !IsCreating;
        }

        protected override void ShortcutReset() {
            if (!ResetButton.IsEnabled || ResetButton.Visibility != Visibility.Visible)
                return;
            ResetButton_Click(null, null);
        }

        private void ResetButton_Click(object? sender, RoutedEventArgs? evt) {
            if (IsEditing) {
                RefreshInputs();
            } else if (IsCreating) {
                ClearInputs();
                InitInputs();
            }
            UpdateButtons();
        }

        private void CancelButton_Click(object sender, RoutedEventArgs evt) {
            IsEditing = false;
            IsCreating = false;
            DeliveryList.IsEnabled = true;
            DeliveryPartList.IsEnabled = true;

            HideSaveResetCancelButtons();
            ShowNewEditDeleteButtons();
            RefreshInputs();
            LockInputs();
            UnlockSearchInputs();

            AbwertenButton.IsEnabled = DeliveryPartList.SelectedItem is DeliveryPart p && p.QualId != "WEI";
            ExtractDeliveryPartButton.IsEnabled = DeliveryPartList.SelectedItem != null && !IsCreating;
            DeleteDeliveryPartButton.IsEnabled = DeliveryList.SelectedItem is Delivery { Parts.Count: > 1 } && !IsCreating;
        }

        private async void ExtractDeliveryPartButton_Click(object sender, RoutedEventArgs evt) {
            if (DeliveryPartList.SelectedItem is not DeliveryPart p)
                return;

            var delivery = p.Delivery;
            var day = delivery.Date;
            var count = delivery.Parts.Count;

            if (delivery.Time <= new TimeOnly(3, 0))
                day = day.AddDays(-1);

            string? res;
            using (var ctx = new AppDbContext()) {
                var lsnrs = await ctx.Deliveries
                    .Where(d => d.ZwstId == delivery.ZwstId)
                    .Where(d => (d.DateString == day.ToString("yyyy-MM-dd") && (d.TimeString == null || d.TimeString.CompareTo("03:00:00") > 0)) ||
                                (d.DateString == day.AddDays(1).ToString("yyyy-MM-dd") && (d.TimeString == null || d.TimeString.CompareTo("03:00:00") <= 0)))
                    .Where(d => d.LsNr != delivery.LsNr)
                    .OrderBy(d => d.LsNr)
                    .Select(d => d.LsNr)
                    .ToListAsync();

                res = Utils.ShowDeliveryExtractionDialog($"{delivery.LsNr}/{p.DPNr}", delivery.Member.AdministrativeName, count == 1, lsnrs);
                if (res == null)
                    return;
            }

            Mouse.OverrideCursor = Cursors.AppStarting;
            try {
                using var ctx = new AppDbContext();
                if (res == "new") {
                    var lnr = await ctx.NextLNr(delivery.Date);
                    ctx.Add(new Delivery {
                        Year = p.Year,
                        DId = await ctx.NextDId(p.Year),
                        LNr = lnr,
                        DateString = $"{delivery.Date:yyyy-MM-dd}",
                        TimeString = $"{delivery.Time:HH:mm:ss}",
                        ZwstId = delivery.ZwstId,
                        MgNr = delivery.MgNr,
                        Comment = delivery.Comment,
                        LsNr = Utils.GenerateLsNr(delivery.Date, delivery.ZwstId, lnr),
                    });
                    await ctx.SaveChangesAsync();
                }
                Delivery? d = await ctx.Deliveries.Where(d => d.LsNr == res).FirstOrDefaultAsync();
                if (d == null) return;

                await ctx.Database.ExecuteSqlAsync($"UPDATE delivery_part SET year = {d.Year}, did = {d.DId}, dpnr = {await ctx.NextDPNr(d.Year, d.DId)} WHERE (year, did, dpnr) = ({p.Year}, {p.DId}, {p.DPNr})");
                if (count == 1) {
                    await ctx.Database.ExecuteSqlAsync($"DELETE FROM delivery WHERE (year, did) = ({delivery.Year}, {delivery.DId})");
                }
                await ctx.SaveChangesAsync();

                await RefreshList();
                ControlUtils.SelectItem(DeliveryList, d);
            } 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, "Lieferung aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            Mouse.OverrideCursor = null;
        }

        private async void DeleteDeliveryPartButton_Click(object sender, RoutedEventArgs evt) {
            if (DeliveryPartList.SelectedItem is not DeliveryPart p)
                return;

            var r = MessageBox.Show(
                $"Soll die Teillieferung Nr. {p.DPNr} wirklich unwiderruflich gelöscht werden?",
                "Lieferung löschen", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
            if (r == MessageBoxResult.OK) {
                Mouse.OverrideCursor = Cursors.AppStarting;
                using (var ctx = new AppDbContext()) {
                    ctx.Remove(p);
                    await ctx.SaveChangesAsync();
                }
                await RefreshDeliveryParts();
                Mouse.OverrideCursor = null;
            }
        }

        private void ShowSaveResetCancelButtons() {
            SaveButton.IsEnabled = false;
            ResetButton.IsEnabled = false;
            CancelButton.IsEnabled = true;
            SaveButton.Visibility = Visibility.Visible;
            ResetButton.Visibility = Visibility.Visible;
            CancelButton.Visibility = Visibility.Visible;
        }

        private void HideSaveResetCancelButtons() {
            SaveButton.IsEnabled = false;
            ResetButton.IsEnabled = false;
            CancelButton.IsEnabled = false;
            SaveButton.Visibility = Visibility.Hidden;
            ResetButton.Visibility = Visibility.Hidden;
            CancelButton.Visibility = Visibility.Hidden;
        }

        private void ShowNewEditDeleteButtons() {
            NewDeliveryButton.IsEnabled = ViewModel.IsReceipt;
            AbwertenButton.IsEnabled = DeliveryPartList.SelectedItem is DeliveryPart p && p.QualId == "WEI";
            EditDeliveryButton.IsEnabled = DeliveryPartList.SelectedItem != null;
            DeleteDeliveryButton.IsEnabled = DeliveryList.SelectedItem != null;
            NewDeliveryButton.Visibility = ViewModel.IsReceipt ? Visibility.Visible : Visibility.Hidden;
            AbwertenButton.Visibility = !ViewModel.IsReceipt ? Visibility.Visible : Visibility.Hidden;
            EditDeliveryButton.Visibility = Visibility.Visible;
            DeleteDeliveryButton.Visibility = Visibility.Visible;
        }

        private void HideNewEditDeleteButtons() {
            NewDeliveryButton.IsEnabled = false;
            AbwertenButton.IsEnabled = false;
            EditDeliveryButton.IsEnabled = false;
            DeleteDeliveryButton.IsEnabled = false;
            NewDeliveryButton.Visibility = Visibility.Hidden;
            AbwertenButton.Visibility = Visibility.Hidden;
            EditDeliveryButton.Visibility = Visibility.Hidden;
            DeleteDeliveryButton.Visibility = Visibility.Hidden;
        }

        private void ShowFinishNewPartDeliveryCancelButtons() {
            FinishButton.IsEnabled = false;
            NewDeliveryPartButton.IsEnabled = false;
            CancelCreatingButton.IsEnabled = true;
            FinishButton.Visibility = Visibility.Visible;
            NewDeliveryPartButton.Visibility = Visibility.Visible;
            CancelCreatingButton.Visibility = Visibility.Visible;
        }

        private void HideFinishNewPartDeliveryCancelButtons() {
            FinishButton.IsEnabled = false;
            NewDeliveryPartButton.IsEnabled = false;
            CancelCreatingButton.IsEnabled = false;
            FinishButton.Visibility = Visibility.Hidden;
            NewDeliveryPartButton.Visibility = Visibility.Hidden;
            CancelCreatingButton.Visibility = Visibility.Hidden;
        }

        private void LockSearchInputs() {
            SearchInput.IsEnabled = false;
            SeasonInput.IsEnabled = false;
            TodayOnlyInput.IsEnabled = false;
            AllSeasonsInput.IsEnabled = false;
        }

        private void UnlockSearchInputs() {
            SearchInput.IsEnabled = true;
            SeasonInput.IsEnabled = true;
            TodayOnlyInput.IsEnabled = true;
            AllSeasonsInput.IsEnabled = (ViewModel.FilterMember != null);
        }

        new protected void UnlockInputs() {
            base.UnlockInputs();
            if (WineQualityLevelInput.SelectedItem != null && WineKgInput.SelectedItem != null)
                WineOriginInput.IsEnabled = false;
            if (WineKgInput.SelectedItem == null)
                WineRdInput.IsEnabled = false;
            WeightInput.IsReadOnly = true;
            AbgewertetInput.IsEnabled = false;
            ManualWeighingInput.IsEnabled = false;
            LsNrInput.IsReadOnly = true;
            DateInput.IsReadOnly = !Menu_Settings_EnableFreeEditing.IsChecked;
            TimeInput.IsReadOnly = !Menu_Settings_EnableFreeEditing.IsChecked;
            BranchInput.IsEnabled = Menu_Settings_EnableFreeEditing.IsChecked;
        }

        private void DisableWeighingButtons() {
            WeighingManualButton.IsEnabled = false;
            WeighingAButton.IsEnabled = false;
            WeighingBButton.IsEnabled = false;
            WeighingCButton.IsEnabled = false;
            WeighingDButton.IsEnabled = false;
        }

        private void EnableWeighingButtons() {
            WeighingManualButton.IsEnabled = true;
            var n = App.CommandScales.Count;
            WeighingAButton.IsEnabled = n > 0 && App.CommandScales[0].IsReady;
            WeighingBButton.IsEnabled = n > 1 && App.CommandScales[1].IsReady;
            WeighingCButton.IsEnabled = n > 2 && App.CommandScales[2].IsReady;
            WeighingDButton.IsEnabled = n > 3 && App.CommandScales[3].IsReady;
        }

        private async Task UpdateLsNr() {
            if (string.IsNullOrEmpty(ViewModel.Date) || ViewModel.Branch == null) {
                ViewModel.LsNr = "";
            } else {
                try {
                    var branch = (Branch)BranchInput.SelectedItem;
                    var date = DateOnly.ParseExact(ViewModel.Date!, "dd.MM.yyyy");
                    using var ctx = new AppDbContext();
                    var lnr = await ctx.NextLNr(date);
                    ViewModel.LsNr = Utils.GenerateLsNr(date, branch.ZwstId, lnr);
                } catch {
                    ViewModel.LsNr = "";
                }
            }
        }

        private new async void DateInput_TextChanged(object sender, TextChangedEventArgs evt) {
            base.DateInput_TextChanged(sender, evt);
            if (IsEditing || IsCreating) await UpdateLsNr();
        }

        private async void BranchInput_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
            base.ComboBox_SelectionChanged(sender, evt);
            if (IsEditing || IsCreating) {
                await UpdateLsNr();
                InitialDefaultInputs();
            }
        }

        private void UpdateWineVariety(bool valid) {
            if (valid) {
                var text = ViewModel.SortId!;
                ControlUtils.SelectItemWithPk(WineVarietyInput, text[0..2]);
                if (text.Length >= 3) {
                    ControlUtils.SelectItemWithPk(AttributeInput, text[2..]);
                    ControlUtils.SelectItemWithPk(CultivationInput, text[2..]);
                    ViewModel.SortId = text[0..2];
                }
            } else {
                WineVarietyInput.SelectedItem = null;
                AttributeInput.SelectedIndex = 0;
                CultivationInput.SelectedIndex = 0;
            }
        }

        private void SortIdInput_TextChanged(object sender, TextChangedEventArgs evt) {
            UpdateWineVariety(InputTextChanged((TextBox)sender, Validator.CheckSortId));
        }

        private void SortIdInput_LostFocus(object sender, RoutedEventArgs evt) {
            UpdateWineVariety(InputLostFocus((TextBox)sender, Validator.CheckSortId));
        }

        private void WineVarietyInput_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
            if (WineVarietyInput.SelectedItem is WineVar s)
                ViewModel.SortId = s.SortId;
        }

        private void UpdateWineQualityLevels() {
            using var ctx = new AppDbContext();
            if (!GetInputValid(GradationKmwInput)) {
                UnsetDefaultValue(WineQualityLevelInput);
                ComboBox_SelectionChanged(WineQualityLevelInput, null);
                WineQualityLevelInput.ItemsSource = ctx.WineQualityLevels.Where(q => q.QualId == "WEI").ToList();
                return;
            }
            var kmw = (double)ViewModel.GradationKmw!;
            WineQualityLevelInput.ItemsSource = ctx.WineQualityLevels.Where(q => q.MinKmw == null || q.MinKmw <= kmw).ToList();
            var qual = ctx.GetWineQualityLevel(kmw).GetAwaiter().GetResult();
            SetDefaultValue(WineQualityLevelInput, qual);
            if (WineQualityLevelInput.SelectedItem == null || (WineQualityLevelInput.SelectedItem is WineQualLevel selected && !selected.IsPredicate)) {
                ControlUtils.SelectItem(WineQualityLevelInput, qual);
            }
        }

        private void UpdateGradationKmw() {
            IsUpdatingGradation = true;
            var caret = GradationKmwInput.CaretIndex;
            GradationKmwInput.Text = $"{Utils.OeToKmw(double.Parse(GradationOeInput.Text)):#.0}";
            GradationKmwInput.CaretIndex = caret;
            IsUpdatingGradation = false;
        }

        private void UpdateGradationOe() {
            IsUpdatingGradation = true;
            var caret = GradationOeInput.CaretIndex;
            GradationOeInput.Text = $"{Utils.KmwToOe(double.Parse(GradationKmwInput.Text)):#}";
            GradationOeInput.CaretIndex = caret;
            IsUpdatingGradation = false;
        }

        private void GradationOeInput_TextChanged(object sender, TextChangedEventArgs evt) {
            var valid = InputTextChanged((TextBox)sender, Validator.CheckGradatoinOe);
            if (!IsUpdatingGradation) {
                if (valid) UpdateGradationKmw();
                else if (GradationOeInput.Text.Length == 0) GradationKmwInput.Text = "";
                if (valid || GradationOeInput.Text.Length == 0) UpdateWineQualityLevels();
            }
        }

        private void GradationOeInput_LostFocus(object sender, RoutedEventArgs evt) {
            InputLostFocus((TextBox)sender, Validator.CheckGradatoinOe);
        }

        private void GradationKmwInput_TextChanged(object sender, TextChangedEventArgs evt) {
            var valid = InputTextChanged((TextBox)sender, Validator.CheckGradationKmw);
            if (!IsUpdatingGradation) {
                if (valid) UpdateGradationOe();
                else if (GradationKmwInput.Text.Length == 0) GradationOeInput.Text = "";
                if (valid || GradationKmwInput.Text.Length == 0) UpdateWineQualityLevels();
            }
        }

        private void GradationKmwInput_LostFocus(object sender, RoutedEventArgs evt) {
            if (GradationKmwInput.Text.EndsWith(',')) GradationKmwInput.Text += "0";
            InputLostFocus((TextBox)sender, Validator.CheckGradationKmw);
            if (GradationKmwInput.Text.Length != 0 && !GradationKmwInput.Text.Contains(','))
                GradationKmwInput.Text += ",0";
        }

        private void ModifiersInput_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
            if (!IsEditing && !IsCreating) return;
            var mod = ModifiersInput.SelectedItems.Cast<Modifier>().ToList();
            if (App.Client.IsMatzen) {
                var kl = mod.Where(m => m.Name.StartsWith("Klasse "));
                if (kl.Count() > 1) {
                    App.MainDispatcher.BeginInvoke(() => {
                        foreach (var r in kl.Take(kl.Count() - 1))
                            ModifiersInput.SelectedItems.Remove(r);
                    });
                }
            } else if (App.Client.IsWinzerkeller) {
                if (mod.Any(m => m.Name.Contains("Lesewagen"))) {
                    ViewModel.IsLesewagen = true;
                } else {
                    ViewModel.IsLesewagen = false;
                }
            }
        }

        private void LesewagenInput_Changed(object sender, RoutedEventArgs evt) {
            if (!IsEditing && !IsCreating) return;
            var mod = ModifiersInput.SelectedItems.Cast<Modifier>().ToList();
            var source = ModifiersInput.ItemsSource.Cast<Modifier>().ToList();
            var lw = LesewagenInput.IsChecked == true;
            if (App.Client.IsMatzen) {
                var kl = mod.Where(m => m.Name.StartsWith("Klasse ")).Select(m => m.ModId).LastOrDefault("A")[0];
                if (lw) kl++; else kl--;
                var newKl = source.FirstOrDefault(m => m.ModId == kl.ToString());
                if (newKl != null) ModifiersInput.SelectedItems.Add(newKl);
            } else if (App.Client.IsWinzerkeller) {
                if (lw && !mod.Any(m => m.Name.Contains("Lesewagen"))) {
                    ModifiersInput.SelectedItems.Add(source.First(m => m.Name.Contains("Lesewagen")));
                } else if (!lw && mod.Any(m => m.Name.Contains("Lesewagen"))) {
                    ModifiersInput.SelectedItems.Remove(mod.First(m => m.Name.Contains("Lesewagen")));
                }
            }
            CheckBox_Changed(sender, evt);
        }

        private void UpdateWineOrigin() {
            var qual = WineQualityLevelInput.SelectedItem as WineQualLevel;
            var kg = (WineKgInput.SelectedItem as AT_Kg)?.WbKg;
            if (qual == null || kg == null) {
                WineOriginInput.IsEnabled = (IsEditing || IsCreating);
                UnsetDefaultValue(WineOriginInput);
                ComboBox_SelectionChanged(WineOriginInput, null);
                return;
            }
            WineOriginInput.IsEnabled = false;
            var o = kg.Origin;
            while (o != null && o.Level > qual.OriginLevel) o = o.Parent;
            SetDefaultValue(WineOriginInput, o);
            ControlUtils.SelectItem(WineOriginInput, o);
        }

        private void WineQualityLevelInput_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
            UpdateWineOrigin();
            UpdateAbgewertet();
        }

        private void UpdateRdInput() {
            if (WineKgInput.SelectedItem is AT_Kg kg) {
                using var ctx = new AppDbContext();
                var list = ctx.WbRde.Where(r => r.KgNr == kg.KgNr).OrderBy(r => r.Name).Cast<object>().ToList();
                list.Insert(0, new NullItem());
                ControlUtils.RenewItemsSource(WineRdInput, list);
                if (WineRdInput.SelectedItem == null) WineRdInput.SelectedIndex = 0;
                WineRdInput.IsEnabled = (IsEditing || IsCreating) && list.Count > 1;
            } else {
                ViewModel.WineRdSource = [];
                WineRdInput.IsEnabled = false;
            }
        }

        private void WineKgInput_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
            UpdateWineOrigin();
            UpdateRdInput();
        }

        private void UpdateAbgewertet() {
            if (!GetInputValid(GradationKmwInput))
                return;
            var qual = WineQualityLevelInput.SelectedItem as WineQualLevel;
            if (qual == null) {
                AbgewertetInput.IsChecked = false;
                return;
            }
            using var ctx = new AppDbContext();
            var defQual = ctx.GetWineQualityLevel(double.Parse(GradationKmwInput.Text)).GetAwaiter().GetResult();
            AbgewertetInput.IsChecked = !qual.IsPredicate && defQual != qual;
        }

        private void WeightInput_TextChanged(object sender, TextChangedEventArgs evt) {
            InputTextChanged((TextBox)sender);
        }

        private void TemperatureAcidInput_TextChanged(object sender, TextChangedEventArgs evt) {
            InputTextChanged((TextBox)sender, Validator.CheckDecimal((TextBox)sender, false, 2, 1));
        }

        private void TemperatureAcidInput_LostFocus(object sender, RoutedEventArgs evt) {
            if (sender is not TextBox tb) return;
            if (tb.Text.Length > 0) {
                if (!tb.Text.Contains(',')) tb.Text += ",0";
                if (tb.Text.EndsWith(',')) tb.Text += "0";
            }
            InputLostFocus(tb, Validator.CheckDecimal(tb, false, 2, 1));
        }

        private void GerebeltGewogenInput_Changed(object sender, RoutedEventArgs evt) {
            if (!App.Client.HasNetWeighing(ViewModel.Branch)) {
                HandPickedInput.IsChecked = !GerebeltGewogenInput.IsChecked;
            }
            if (!ViewModel.IsReceipt || App.Client.HasNetWeighing(ViewModel.Branch)) {
                GerebeltGewogenInput.IsChecked ??= false;
            }
            CheckBox_Changed(sender, evt);
        }

        private void HandPickedInput_Changed(object sender, RoutedEventArgs evt) {
            if (!App.Client.HasNetWeighing(ViewModel.Branch)) {
                GerebeltGewogenInput.IsChecked = !HandPickedInput.IsChecked;
            }
            CheckBox_Changed(sender, evt);
        }

        private void MemberReferenceButton_Click(object sender, RoutedEventArgs evt) {
            if (MemberInput.SelectedItem is not Member m) return;
            App.FocusMember(m.MgNr);
        }
    }
}