using Elwig.Documents;
using Elwig.Helpers;
using Elwig.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Threading;
using Xceed.Wpf.Toolkit.Primitives;

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

        private bool IsUpdatingGradation = false;
        private readonly bool IsReceipt = false;
        private Member? Member = null;
        private readonly DispatcherTimer Timer;
        private List<string> TextFilter = new();
        private readonly RoutedCommand CtrlF = new();

        private string? ManualWeighingReason = null;

        public DeliveryAdminWindow(bool receipt = false) {
            InitializeComponent();
            CtrlF.InputGestures.Add(new KeyGesture(Key.F, ModifierKeys.Control));
            CommandBindings.Add(new CommandBinding(CtrlF, FocusSearchInput));
            RequiredInputs = new Control[] {
                MgNrInput, MemberInput,
                LsNrInput, DateInput, BranchInput,
                SortIdInput, WineVarietyInput,
                GradationOeInput, GradationKmwInput, WineQualityLevelInput,
                WineOriginInput, WineKgInput,
                WeightInput
            };
            ExemptInputs = new Control[] {
                SearchInput, TodayOnlyInput, SeasonOnlyInput,
                DeliveryList, DeliveryPartList,
                MemberAddressField,
            };
            IsReceipt = receipt;

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

            InitializeDelayTimer(SearchInput, SearchInput_TextChanged);
            SearchInput.TextChanged -= SearchInput_TextChanged;

            if (IsReceipt) {
                Title = "Übernahme - Elwig";
                TodayOnlyInput.IsChecked = true;
                var n = App.Scales.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";
            } else {
                WeighingManualButton.Visibility = Visibility.Hidden;
                WeighingAButton.Visibility = Visibility.Hidden;
                WeighingBButton.Visibility = Visibility.Hidden;
                WeighingCButton.Visibility = Visibility.Hidden;
                WeighingDButton.Visibility = Visibility.Hidden;
            }
        }

        public DeliveryAdminWindow(int mgnr) : this() {
            Member = Context.Members.Find(mgnr) ?? throw new ArgumentException("MgNr argument has invalid value");
            Title = $"Lieferungen - {Member.AdministrativeName} - Elwig";
        }

        private async void Window_Loaded(object sender, RoutedEventArgs evt) {
            OnSecondPassed(null, null);
            Timer.Start();
            LockInputs();
            if (IsReceipt) {
                NewDeliveryButton_Click(null, null);
                if ((await Context.Seasons.FindAsync(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.Error);
                }
            }
        }

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

        private void InitialInputs() {
            HandPickedInput.IsChecked = null;
            // FIXME if Matzen
            GerebeltGewogenInput.IsChecked = true;
            ClearOriginalValues();
            ValidateRequiredInputs();
        }

        private void InitInputs() {
            ControlUtils.SelectComboBoxItem(BranchInput, i => (i as Branch)?.ZwstId, 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;
            CancelCreatingButton.IsEnabled = DeliveryList.SelectedItem == null || DeliveryPartList.SelectedItem == null;
        }

        private async Task RefreshDeliveryList() {
            await RefreshDeliveryListQuery();
        }

        private async Task RefreshDeliveryListQuery(bool updateSort = false) {
            IQueryable<Delivery> deliveryQuery = Context.Deliveries;
            if (Member != null) {
                deliveryQuery = deliveryQuery.Where(d => d.MgNr == Member.MgNr);
            }
            if (TodayOnlyInput.IsChecked == true) {
                deliveryQuery = deliveryQuery
                    .Where(d => (d.DateString == Utils.Today.ToString("yyyy-MM-dd") && d.TimeString.CompareTo("03:00:00") > 0) || 
                                (d.DateString == Utils.Today.AddDays(1).ToString("yyyy-MM-dd") && d.TimeString.CompareTo("03:00:00") <= 0));
            } else if (SeasonOnlyInput.IsChecked == true) {
                deliveryQuery = deliveryQuery.Where(d => d.Year == Utils.CurrentLastSeason);
            }

            List<Delivery> deliveries = await deliveryQuery.OrderByDescending(d => d.DateString).ThenByDescending(d => d.TimeString).ToListAsync();
            if (TextFilter.Count > 0) {
                var dict = deliveries
                    .ToDictionary(d => d, d => d.SearchScore(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();
            }

            ControlUtils.RenewItemsSource(DeliveryList, deliveries, d => ((d as Delivery)?.Year, (d as Delivery)?.DId), DeliveryList_SelectionChanged, ControlUtils.RenewSourceDefault.IfOnly, !updateSort);
        }

        protected override async Task RenewContext() {
            await base.RenewContext();

            if (Member != null) {
                if (Context.Members.Find(Member.MgNr) is not Member m) {
                    Close();
                    return;
                }
                Member = m;
                Title = $"Lieferungen - {Member.AdministrativeName} - Elwig";
            }

            await RefreshDeliveryList();
            var d = DeliveryList.SelectedItem as Delivery;
            var y = d?.Year ?? Utils.CurrentLastSeason;
            ControlUtils.RenewItemsSource(MemberInput, await Context.Members.OrderBy(m => m.FamilyName).ThenBy(m => m.GivenName).ToListAsync(), i => (i as Member)?.MgNr);
            ControlUtils.RenewItemsSource(BranchInput, await Context.Branches.OrderBy(b => b.Name).ToListAsync(), i => (i as Branch)?.ZwstId);
            ControlUtils.RenewItemsSource(WineVarietyInput, await Context.WineVarieties.OrderBy(v => v.Name).ToListAsync(), i => (i as WineVar)?.SortId);
            ControlUtils.RenewItemsSource(AttributesInput, await Context.WineAttributes.OrderBy(a => a.Name).ToListAsync(), i => (i as WineAttr)?.AttrId);
            ControlUtils.RenewItemsSource(WineQualityLevelInput, await Context.WineQualityLevels.ToListAsync(), i => (i as WineQualLevel)?.QualId);
            ControlUtils.RenewItemsSource(ModifiersInput, await Context.Modifiers.Where(m => m.Year == y).OrderBy(m => m.Name).ToListAsync(), i => (i as Modifier)?.ModId);
            ControlUtils.RenewItemsSource(WineOriginInput, (await Context.WineOrigins.ToListAsync()).OrderByDescending(o => o.SortKey).ThenBy(o => o.HkId), i => (i as WineOrigin)?.HkId);
            var kgList = await Context.WbKgs.Select(k => k.AtKg).OrderBy(k => k.Name).Cast<object>().ToListAsync();
            kgList.Insert(0, new NullItem());
            ControlUtils.RenewItemsSource(WineKgInput, kgList, i => (i as AT_Kg)?.KgNr);
            UpdateRdInput();

            if (IsCreating) await UpdateLsNr();
        }

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

        private void RefreshDeliveryParts() {
            if (DeliveryList.SelectedItem is Delivery d) {
                ControlUtils.RenewItemsSource(ModifiersInput, Context.Modifiers.Where(m => m.Year == d.Year).OrderBy(m => m.Name).ToList(), i => (i as Modifier)?.ModId);
                ControlUtils.RenewItemsSource(DeliveryPartList, d.Parts.OrderBy(p => p.DPNr).ToList(), i => ((i as DeliveryPart)?.Year, (i as DeliveryPart)?.DId, (i as DeliveryPart)?.DPNr), DeliveryPartList_SelectionChanged, ControlUtils.RenewSourceDefault.First);
            } else {
                ControlUtils.RenewItemsSource(ModifiersInput, Context.Modifiers.Where(m => m.Year == Utils.CurrentLastSeason).OrderBy(m => m.Name).ToList(), i => (i as Modifier)?.ModId);
                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();
                ClearInputs(validate);
                ClearInputStates();
            }            
            GC.Collect();
        }

        private void FillInputs(Delivery d) {
            ClearOriginalValues();

            MgNrInput.Text = d.MgNr.ToString();
            ControlUtils.SelectComboBoxItem(BranchInput, i => (i as Branch)?.ZwstId, d.ZwstId);
            LsNrInput.Text = d.LsNr;
            DateInput.Text = d.Date.ToString("dd.MM.yyyy");
            TimeInput.Text = d.Time?.ToString("HH:mm") ?? "";
            CommentInput.Text = d.Comment ?? "";

            SortIdInput.Text = "";
            GradationKmwInput.Text = "";
            WeightInput.Text = "";
            ManualWeighingInput.IsChecked = false;
            PartCommentInput.Text = "";
            TemperatureInput.Text = "";
            AcidInput.Text = "";

            FillOriginalValues();
        }

        private void FillInputs(DeliveryPart p) {
            ClearOriginalValues();
            FillInputs(p.Delivery);

            SortIdInput.Text = p?.SortId ?? "";
            ControlUtils.SelectCheckComboBoxItems(AttributesInput, p?.Attributes, i => (i as WineAttr)?.AttrId);
            GradationKmwInput.Text = (p != null) ? $"{p.Kmw:N1}" : "";
            ControlUtils.SelectComboBoxItem(WineQualityLevelInput, q => (q as WineQualLevel)?.QualId, p?.QualId);
            ControlUtils.SelectComboBoxItem(WineKgInput, k => (k as AT_Kg)?.KgNr, p?.KgNr);
            ControlUtils.SelectComboBoxItem(WineRdInput, r => (r as WbRd)?.RdNr, p?.RdNr);
            ControlUtils.SelectComboBoxItem(WineOriginInput, r => (r as WineOrigin)?.HkId, p?.HkId);
            WeightInput.Text = (p != null) ? $"{p.Weight:N0}" : "";
            ManualWeighingInput.IsChecked = p?.ManualWeighing ?? false;
            GerebeltGewogenInput.IsChecked = p?.IsGerebelt ?? false;
            ControlUtils.SelectCheckComboBoxItems(ModifiersInput, p?.Modifiers, i => (i as Modifier)?.ModId);
            PartCommentInput.Text = p?.Comment ?? "";
            TemperatureInput.Text = (p != null && p.Temperature != null) ? $"{p.Temperature:N1}" : "";
            AcidInput.Text = (p != null && p.Acid != null) ? $"{p.Acid:N1}" : "";
            LesewagenInput.IsChecked = p?.IsLesewagen ?? false;
            HandPickedInput.IsChecked = p?.IsHandPicked;

            FillOriginalValues();
        }

        private async Task<DeliveryPart> UpdateDeliveryPart(Delivery? d, DeliveryPart? p) {
            int year, did, dpnr;
            bool deliveryNew = (d == null), partNew = (p == null);
            if (d == null) {
                d = Context.CreateProxy<Delivery>();
                year = Utils.CurrentNextSeason;
                did = await Context.NextDId(year);
            } else {
                year = d.Year;
                did = d.DId;
            }
            if (p == null) {
                p = Context.CreateProxy<DeliveryPart>();
                dpnr = await Context.NextDPNr(year, did);
            } else {
                dpnr = p.DPNr;
            }
            d.Year = year;
            d.DId = did;
            p.Year = year;
            p.DId = did;
            p.DPNr = dpnr;

            d.DateString = string.Join("-", DateInput.Text.Split(".").Reverse());
            if (IsCreating || InputHasChanged(DateInput)) {
                d.LNr = await Context.NextLNr(d.Date);
            }
            if (IsCreating) {
                d.TimeString = DateTime.Now.ToString("HH:mm:ss");
            } else if (InputHasChanged(TimeInput)) {
                d.TimeString = TimeInput.Text + ":00";
            }
            d.ZwstId = (BranchInput.SelectedItem as Branch)?.ZwstId;
            d.LsNr = LsNrInput.Text;
            d.MgNr = int.Parse(MgNrInput.Text);
            d.Comment = (CommentInput.Text == "") ? null : CommentInput.Text;

            p.SortId = (WineVarietyInput.SelectedItem as WineVar)?.SortId;
            p.Weight = int.Parse(WeightInput.Text.Replace(".", ""));
            p.Kmw = double.Parse(GradationKmwInput.Text);
            p.QualId = (WineQualityLevelInput.SelectedItem as WineQualLevel)?.QualId;
            p.HkId = (WineOriginInput.SelectedItem as WineOrigin)?.HkId;
            p.KgNr = (WineKgInput.SelectedItem as AT_Kg)?.KgNr;
            p.RdNr = (WineRdInput.SelectedItem as WbRd)?.RdNr;

            p.IsGerebelt = GerebeltGewogenInput.IsChecked ?? false;
            p.ManualWeighing = ManualWeighingInput.IsChecked ?? false;
            p.IsHandPicked = HandPickedInput.IsChecked;
            p.IsLesewagen = LesewagenInput.IsChecked;
            p.Temperature = (TemperatureInput.Text == "") ? null : double.Parse(TemperatureInput.Text);
            p.Acid = (TemperatureInput.Text == "") ? null : double.Parse(AcidInput.Text);
            p.Comment = (PartCommentInput.Text == "") ? null : PartCommentInput.Text;

            // TODO weighing/scale id
            // p.ScaleId
            // p.WeighingId

            // TODO update KgNr when MgNr changes (if default is selected)

            EntityEntry<Delivery>? dEntry = null;
            EntityEntry<DeliveryPart>? pEntry = null;
            try {
                if (IsEditing) {
                    dEntry = Context.Update(d);
                    pEntry = Context.Update(p);
                } else if (IsCreating) {
                    dEntry = deliveryNew ? await Context.AddAsync(d) : Context.Update(d);
                    pEntry = partNew ? await Context.AddAsync(p) : Context.Update(p);
                }

                foreach (var a in AttributesInput.ItemsSource.Cast<WineAttr>()) {
                    var attr = p.PartAttributes.Where(pa => pa.AttrId == a.AttrId).FirstOrDefault();
                    if (AttributesInput.SelectedItems.Contains(a)) {
                        DeliveryPartAttr dpa = attr ?? Context.CreateProxy<DeliveryPartAttr>();
                        dpa.Year = year;
                        dpa.DId = did;
                        dpa.DPNr = dpnr;
                        dpa.AttrId = a.AttrId;
                        if (attr == null) {
                            await Context.AddAsync(dpa);
                        } else {
                            Context.Update(dpa);
                        }
                    } else {
                        if (attr != null) {
                            Context.Remove(attr);
                        }
                    }
                }

                foreach (var m in ModifiersInput.ItemsSource.Cast<Modifier>()) {
                    var mod = p.PartModifiers.Where(pa => pa.ModId == m.ModId).FirstOrDefault();
                    if (ModifiersInput.SelectedItems.Contains(m)) {
                        DeliveryPartModifier dpm = mod ?? Context.CreateProxy<DeliveryPartModifier>();
                        dpm.Year = year;
                        dpm.DId = did;
                        dpm.DPNr = dpnr;
                        dpm.ModId = m.ModId;
                        if (mod == null) {
                            await Context.AddAsync(dpm);
                        } else {
                            Context.Update(dpm);
                        }
                    } else {
                        if (mod != null) {
                            Context.Remove(mod);
                        }
                    }
                }

                await Context.SaveChangesAsync();
            } catch (Exception exc) {
                if (dEntry != null) {
                    await dEntry.ReloadAsync();
                    if (deliveryNew) dEntry.State = EntityState.Detached;
                }
                if (pEntry != null) {
                    await pEntry.ReloadAsync();
                    if (partNew) pEntry.State = EntityState.Detached;
                }
                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);
            }

            return p;
        }

        private async void SearchInput_TextChanged(object sender, RoutedEventArgs evt) {
            TextFilter = SearchInput.Text.ToLower().Split(" ").ToList().FindAll(e => e.Length > 0);
            await RefreshDeliveryListQuery(true);
        }

        private async void TodayOnlyInput_Changed(object sender, RoutedEventArgs evt) {
            if (TodayOnlyInput.IsChecked == true && SeasonOnlyInput.IsChecked == true) SeasonOnlyInput.IsChecked = false;
            await RefreshDeliveryListQuery();
        }

        private async void SeasonOnlyInput_Changed(object sender, RoutedEventArgs evt) {
            if (!IsInitialized) return;
            if (SeasonOnlyInput.IsChecked == true && TodayOnlyInput.IsChecked == true) TodayOnlyInput.IsChecked = false;
            await RefreshDeliveryListQuery();
        }

        private void DeliveryList_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
            RefreshDeliveryParts();
            if (DeliveryList.SelectedItem != null) {
                DeleteDeliveryButton.IsEnabled = true;
            } else {
                DeleteDeliveryButton.IsEnabled = false;
            }
        }

        private void DeliveryPartList_SelectionChanged(object? sender, SelectionChangedEventArgs? evt) {
            RefreshInputs();
            if (DeliveryPartList.SelectedItem != null) {
                AbwertenButton.IsEnabled = true;
                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);
            MemberInput.SelectedItem = valid ? Context.Members.Find(int.Parse(MgNrInput.Text)) : null;
        }

        private void MgNrInput_LostFocus(object sender, RoutedEventArgs evt) {
            var valid = InputLostFocus((TextBox)sender, Validator.CheckMgNr);
            MemberInput.SelectedItem = valid ? Context.Members.Find(int.Parse(MgNrInput.Text)) : null;
        }

        private void MemberInput_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
            var m = MemberInput.SelectedItem as Member;
            if (m != null) MgNrInput.Text = m.MgNr.ToString();
            MemberAddressField.Text = m?.FullAddress;
            if (m == null) {
                UnsetOriginalValue(WineKgInput);
                WineKgInput.SelectedIndex = 0;
            } else {
                SetOriginalValue(WineKgInput, m.DefaultKg);
                WineKgInput.SelectedItem = m.DefaultKg;
            }
        }

        private async void NewDeliveryPartButton_Click(object sender, RoutedEventArgs evt) {
            DeliveryPartList.IsEnabled = false;
            var p = await UpdateDeliveryPart(DeliveryList.SelectedItem as Delivery, DeliveryPartList.SelectedItem as DeliveryPart);
            await RefreshDeliveryList();
            RefreshDeliveryParts();
            DeliveryList.SelectedItem = p.Delivery;
            DeliveryPartList.SelectedItem = null;
            InitialInputs();
        }

        private async void FinishButton_Click(object sender, RoutedEventArgs evt) {
            DeliveryPartList.IsEnabled = false;
            var p = await UpdateDeliveryPart(DeliveryList.SelectedItem as Delivery, DeliveryPartList.SelectedItem as DeliveryPart);
            await RefreshDeliveryList();
            RefreshDeliveryParts();
            if (p != null) {
                var doc = new DeliveryNote(p.Delivery);
                await doc.Generate();
                doc.Show();
                //await doc.Print(2);
            }
            DeliveryList.SelectedItem = null;
            InitInputs();
        }

        private void CancelCreatingButton_Click(object sender, RoutedEventArgs evt) {
            if (DeliveryList.SelectedItem is not Delivery d) {
                // switch away from creating mode
                IsCreating = false;
                IsEditing = false;
                DeliveryList.IsEnabled = true;
                DeliveryPartList.IsEnabled = true;
                WeighingManualButton.IsEnabled = false;
                WeighingAButton.IsEnabled = false;
                WeighingBButton.IsEnabled = false;
                WeighingCButton.IsEnabled = false;
                WeighingDButton.IsEnabled = false;
                HideFinishNewPartDeliveryCancelButtons();
                ShowNewEditDeleteButtons();
                RefreshInputs();
                ClearInputStates();
                LockInputs();
                UnlockSearchInputs();
            } else {
                // switch to last delivery part
                DeliveryPartList.IsEnabled = true;
                DeliveryPartList.SelectedItem = d.Parts.Last();
            }
        }

        private void NewDeliveryButton_Click(object? sender, RoutedEventArgs? evt) {
            IsCreating = true;
            DeliveryList.IsEnabled = false;
            DeliveryPartList.IsEnabled = false;
            WeighingManualButton.IsEnabled = true;
            WeighingAButton.IsEnabled = true;
            WeighingBButton.IsEnabled = true;
            WeighingCButton.IsEnabled = true;
            WeighingDButton.IsEnabled = true;
            DeliveryList.SelectedItem = null;
            HideNewEditDeleteButtons();
            ShowFinishNewPartDeliveryCancelButtons();
            UnlockInputs();
            InitInputs();
            LockSearchInputs();
        }

        private void AbwertenButton_Click(object sender, RoutedEventArgs evt) {
            // TODO abwerten dialog
        }

        private void WeighingManualButton_Click(object sender, RoutedEventArgs evt) {
            var res = Utils.ShowManualWeighingDialog();
            if (res == null) return;
            WeightInput.Text = $"{res?.Item1:N0}";
            ManualWeighingInput.IsChecked = true;
            ManualWeighingReason = res?.Item2;
        }

        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;
        }

        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.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
            if (r == MessageBoxResult.Yes) {
                Context.Remove(d);
                await Context.SaveChangesAsync();
                await RefreshDeliveryList();
                RefreshDeliveryParts();
            }
        }

        private async void SaveButton_Click(object sender, RoutedEventArgs evt) {
            DeliveryPart p = await UpdateDeliveryPart(DeliveryList.SelectedItem as Delivery, DeliveryPartList.SelectedItem as DeliveryPart);

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

            HideSaveResetCancelButtons();
            ShowNewEditDeleteButtons();
            LockInputs();
            UnlockSearchInputs();
            FillOriginalValues();
            await RefreshDeliveryList();
            RefreshDeliveryParts();
            RefreshInputs();

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

        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();
            ClearInputStates();
            LockInputs();
            UnlockSearchInputs();

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

        private void ExtractDeliveryPartButton_Click(object sender, RoutedEventArgs evt) {
            // TODO extract delivery part
        }

        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.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
            if (r == MessageBoxResult.Yes) {
                Context.Remove(p);
                await Context.SaveChangesAsync();
                RefreshDeliveryParts();
            }
        }

        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 = IsReceipt;
            AbwertenButton.IsEnabled = DeliveryPartList.SelectedItem != null;
            EditDeliveryButton.IsEnabled = DeliveryPartList.SelectedItem != null;
            DeleteDeliveryButton.IsEnabled = DeliveryList.SelectedItem != null;
            NewDeliveryButton.Visibility = IsReceipt ? Visibility.Visible : Visibility.Hidden;
            AbwertenButton.Visibility = !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 = IsCreating && IsValid;
            NewDeliveryPartButton.IsEnabled = IsCreating && IsValid;
            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;
            TodayOnlyInput.IsEnabled = false;
            SeasonOnlyInput.IsEnabled = false;
        }

        private void UnlockSearchInputs() {
            SearchInput.IsEnabled = true;
            TodayOnlyInput.IsEnabled = true;
            SeasonOnlyInput.IsEnabled = true;
        }

        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 = true;
            TimeInput.IsReadOnly = true;
            BranchInput.IsEnabled = false;
        }

        private async Task UpdateLsNr() {
            if (DateInput.Text == "" || BranchInput.SelectedItem == null) {
                LsNrInput.Text = "";
            } else {
                var branch = (Branch)BranchInput.SelectedItem;
                var date = DateOnly.ParseExact(DateInput.Text, "dd.MM.yyyy");
                var lnr = await Context.NextLNr(date);
                LsNrInput.Text = $"{date:yyyyMMdd}{branch.ZwstId}{lnr:000}";
            }
        }

        private void DateInput_TextChanged(object sender, TextChangedEventArgs evt) {
            if (IsCreating) UpdateLsNr().GetAwaiter().GetResult();
        }

        private void UpdateWineVariety(bool valid) {
            if (valid) {
                var text = SortIdInput.Text;
                WineVarietyInput.SelectedItem = Context.WineVarieties.Find(text[0..2]);
                if (text.Length >= 3) {
                    AttributesInput.UnSelectAll();
                    AttributesInput.SelectedItems.Add(Context.WineAttributes.Find(text[2..]));
                    SortIdInput.Text = text[0..2];
                }
            } else {
                WineVarietyInput.SelectedItem = null;
                AttributesInput.UnSelectAll();
            }
        }

        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)
                    SortIdInput.Text = s.SortId;
        }

        private void UpdateWineQualityLevels() {
            if (!GetInputValid(GradationKmwInput)) {
                UnsetOriginalValue(WineQualityLevelInput);
                WineQualityLevelInput.ItemsSource = Context.WineQualityLevels.Where(q => q.QualId == "WEI").ToList();
                return;
            }
            var kmw = double.Parse(GradationKmwInput.Text);
            WineQualityLevelInput.ItemsSource = Context.WineQualityLevels.Where(q => q.MinKmw == null || q.MinKmw <= kmw).ToList();
            var qual = Context.GetWineQualityLevel(kmw).GetAwaiter().GetResult();
            SetOriginalValue(WineQualityLevelInput, qual);
            if (WineQualityLevelInput.SelectedItem == null || (WineQualityLevelInput.SelectedItem is WineQualLevel selected && !selected.IsPredicate)) {
                WineQualityLevelInput.SelectedItem = 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 AttributesInput_SelectionChanged(object sender, ItemSelectionChangedEventArgs evt) {

        }

        private void ModifiersInput_SelectionChanged(object sender, ItemSelectionChangedEventArgs 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);
                return;
            }
            WineOriginInput.IsEnabled = false;
            var o = kg.Origin;
            while (o != null && o.Level > qual.OriginLevel) o = o.Parent;
            WineOriginInput.SelectedItem = o;
        }

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

        private void UpdateRdInput() {
            if (WineKgInput.SelectedItem is AT_Kg kg) {
                var list = Context.WbRde.Where(r => r.KgNr == kg.KgNr).OrderBy(r => r.Name).Cast<object>().ToList();
                list.Insert(0, new NullItem());
                ControlUtils.RenewItemsSource(WineRdInput, list, i => ((i as WbRd)?.KgNr, (i as WbRd)?.RdNr));
                if (WineRdInput.SelectedItem == null) WineRdInput.SelectedIndex = 0;
                WineRdInput.IsEnabled = (IsEditing || IsCreating) && list.Count > 1;
            } else {
                WineRdInput.ItemsSource = null;
                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;
            }
            var defQual = Context.GetWineQualityLevel(double.Parse(GradationKmwInput.Text)).GetAwaiter().GetResult();
            AbgewertetInput.IsChecked = !qual.IsPredicate && defQual != qual;
        }

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