using Microsoft.EntityFrameworkCore;
using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using Elwig.Helpers;
using Elwig.Models.Entities;
using System.Threading.Tasks;
using Elwig.Documents;
using System.Diagnostics;
using Elwig.Dialogs;
using Elwig.Services;
using Elwig.ViewModels;
using System.Windows.Data;

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

        public MemberAdminViewModel ViewModel => (MemberAdminViewModel)DataContext;

        private readonly (ComboBox Type, TextBox Number, TextBox Comment)[] PhoneNrInputs;
        private readonly (Label Label, TextBox Address)[] EmailAddressInputs;

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

        public MemberAdminWindow() {
            InitializeComponent();
            CommandBindings.Add(new CommandBinding(CtrlF, FocusSearchInput));
            CommandBindings.Add(new CommandBinding(CtrlL, Menu_List_SaveActive_Click));
            CommandBindings.Add(new CommandBinding(CtrlP, Menu_MemberDataSheet_Show_Click));
            CommandBindings.Add(new CommandBinding(CtrlO, Menu_List_ShowActive_Click));
            CommandBindings.Add(new CommandBinding(CtrlShiftP, Menu_MemberDataSheet_Print_Click));
            CommandBindings.Add(new CommandBinding(CtrlShiftO, Menu_List_PrintActive_Click));
            ExemptInputs = [
                SearchInput, ActiveMemberInput, MemberList,
            ];
            RequiredInputs = [
                MgNrInput, GivenNameInput, NameInput,
                AddressInput, PlzInput, OrtInput, BillingOrtInput,
                BusinessSharesInput, BranchInput, DefaultKgInput
            ];
            EmailAddressInputs = [
                (EmailAddress1Label, EmailAddress1Input),
                (EmailAddress2Label, EmailAddress2Input),
                (EmailAddress3Label, EmailAddress3Input),
                (EmailAddress4Label, EmailAddress4Input),
                (EmailAddress5Label, EmailAddress5Input),
                (EmailAddress6Label, EmailAddress6Input),
                (EmailAddress7Label, EmailAddress7Input),
                (EmailAddress8Label, EmailAddress8Input),
                (EmailAddress9Label, EmailAddress9Input),
            ];
            PhoneNrInputs = [
                (PhoneNr1TypeInput, PhoneNr1Input, PhoneNr1CommentInput),
                (PhoneNr2TypeInput, PhoneNr2Input, PhoneNr2CommentInput),
                (PhoneNr3TypeInput, PhoneNr3Input, PhoneNr3CommentInput),
                (PhoneNr4TypeInput, PhoneNr4Input, PhoneNr4CommentInput),
                (PhoneNr5TypeInput, PhoneNr5Input, PhoneNr5CommentInput),
                (PhoneNr6TypeInput, PhoneNr6Input, PhoneNr6CommentInput),
                (PhoneNr7TypeInput, PhoneNr7Input, PhoneNr7CommentInput),
                (PhoneNr8TypeInput, PhoneNr8Input, PhoneNr8CommentInput),
                (PhoneNr9TypeInput, PhoneNr9Input, PhoneNr9CommentInput),
            ];

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

            ViewModel.MemberListOrderByMgNr = false;
            ViewModel.MemberListOrderByName = false;
            ViewModel.MemberListOrderByOrt = false;
            switch (App.Client.OrderingMemberList) {
                case 0: ViewModel.MemberListOrderByMgNr = true; break;
                case 1: ViewModel.MemberListOrderByName = true; break;
                case 2: ViewModel.MemberListOrderByOrt = true; break;
            }

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

        private void Window_Loaded(object sender, RoutedEventArgs evt) {
            ViewModel.ShowOnlyActiveMembers = true;
            UpdateContactInfoVisibility();
            LockInputs();
        }

        public void FocusMember(int mgnr) {
            if (IsEditing || IsCreating) return;
            var item = ViewModel.Members.FirstOrDefault(m => m.MgNr == mgnr);
            if (item == null) {
                using var ctx = new AppDbContext();
                var m = ctx.Members.Find(mgnr);
                if (m == null) return;
                if (ViewModel.SearchQuery?.Length > 0) {
                    ViewModel.SearchQuery = "";
                }
                if (!m.IsActive) {
                    ViewModel.ShowOnlyActiveMembers = false;
                }
                App.MainDispatcher.BeginInvoke(async () => {
                    await Task.Delay(500);
                    FocusMember(mgnr);
                });
            } else {
                ControlUtils.SelectItem(MemberList, item);
            }
        }

        private async Task RefreshList(bool updateSort = false) {
            using var ctx = new AppDbContext();
            var (_, memberQuery, filter) = await ViewModel.GetFilters(ctx);
            var members = await memberQuery
                .Include(m => m.Branch)
                .Include(m => m.DefaultWbKg!.AtKg)
                .Include(m => m.EmailAddresses)
                .Include(m => m.TelephoneNumbers)
                .Include(m => m.PostalDest.AtPlz!.Ort)
                .Include(m => m.PostalDest.AtPlz!.Country)
                .Include(m => m.BillingAddress!.PostalDest.AtPlz!.Ort)
                .Include(m => m.BillingAddress!.PostalDest.AtPlz!.Country)
                .ToListAsync();

            if (filter.Count > 0 && members.Count > 0) {
                var dict = members.AsParallel()
                    .ToDictionary(m => m, m => m.SearchScore(filter))
                    .OrderByDescending(a => a.Value)
                    .ThenBy(a => a.Key.Name)
                    .ThenBy(a => a.Key.GivenName)
                    .ThenBy(a => a.Key.MgNr);
                var threshold = dict.Select(a => a.Value).Max() * 3 / 4;
                members = dict
                    .Where(a => a.Value > threshold)
                    .Select(a => a.Key)
                    .ToList();
            } else {
                members = members
                    .OrderBy(m => m.Name)
                    .ThenBy(m => m.GivenName)
                    .ThenBy(m => m.MgNr)
                    .ToList();
            }

            ControlUtils.RenewItemsSource(MemberList, members,
                MemberList_SelectionChanged, ViewModel.TextFilter.Count > 0 ? ControlUtils.RenewSourceDefault.IfOnly : ControlUtils.RenewSourceDefault.None, !updateSort);
            if (updateSort && MemberList.SelectedItem != null)
                MemberList.ScrollIntoView(MemberList.SelectedItem);

            ViewModel.StatusMembers = $"{members.Count:N0} ({await ctx.Members.CountAsync():N0})";
            ViewModel.StatusBusinessShares = $"{members.Sum(m => m.BusinessShares):N0} ({await ctx.Members.SumAsync(m => m.BusinessShares):N0})";
        }

        private void RefreshInputs(bool validate = false) {
            ClearInputStates();
            if (ViewModel.SelectedMember is Member m) {
                EditMemberButton.IsEnabled = true;
                DeleteMemberButton.IsEnabled = true;
                FillInputs(m);
            } else {
                EditMemberButton.IsEnabled = false;
                DeleteMemberButton.IsEnabled = false;
                ClearOriginalValues();
                ClearDefaultValues();
                ClearInputs(validate);
                ClearInputStates();
            }
            GC.Collect();
        }

        private async Task InitInputs() {
            ClearOriginalValues();
            ClearDefaultValues();
            await ViewModel.InitInputs();
            SetDefaultValue(MgNrInput);
            SetDefaultValue(EntryDateInput);
            SetDefaultValue(ActiveInput);
            ValidateRequiredInputs();
        }

        protected override async Task OnRenewContext(AppDbContext ctx) {
            await base.OnRenewContext(ctx);
            ControlUtils.RenewItemsSource(BranchInput, await ctx.Branches.OrderBy(b => b.Name).ToListAsync());
            ControlUtils.RenewItemsSource(DefaultKgInput, await ctx.WbKgs.Select(k => k.AtKg).OrderBy(k => k.Name).ToListAsync());

            var font = new System.Windows.Media.FontFamily("Segoe MDL2 Assets");
            MenuItem? temp = null;
            var seasons = await ctx.Seasons.OrderByDescending(s => s.Year).ToListAsync();
            Menu_DeliveryConfirmation.Items.Clear();
            foreach (var s in seasons) {
                var i = new MenuItem {
                    Header = $"Saison {s.Year}...",
                    Tag = s.Year,
                    Icon = s.Year == seasons[0].Year ? new TextBlock { FontSize = 16, Text = "\uE734", FontFamily = font } : null,
                };
                i.SetBinding(IsEnabledProperty, new Binding() { Path = new("IsMemberSelected") });
                var show = new MenuItem { Header = "...anzeigen (PDF)", Icon = new TextBlock { FontSize = 16, Text = "\uE8FF", FontFamily = font } };
                show.Click += Menu_DeliveryConfirmation_Show_Click;
                i.Items.Add(show);
                var pdf = new MenuItem { Header = "...speichern... (PDF)", Icon = new TextBlock { FontSize = 16, Text = "\uEA90", FontFamily = font } };
                pdf.Click += Menu_DeliveryConfirmation_SavePdf_Click;
                i.Items.Add(pdf);
                var print = new MenuItem { Header = "...drucken", Icon = new TextBlock { FontSize = 16, Text = "\uE749", FontFamily = font } };
                print.Click += Menu_DeliveryConfirmation_Print_Click;
                i.Items.Add(print);
                var email = new MenuItem { Header = "...per E-Mail schicken", Icon = new TextBlock { FontSize = 16, Text = "\uE89C", FontFamily = font } };
                email.Click += Menu_DeliveryConfirmation_Email_Click;
                email.SetBinding(IsEnabledProperty, new Binding() { Path = new("MemberCanSendEmail") });
                i.Items.Add(email);
                var decade = s.Year / 10;
                if (seasons[0].Year / 10 != decade) {
                    if (temp == null || !temp.Header.ToString()!.Contains($"{decade}0er")) {
                        temp = new MenuItem { Header = $"Saisons {decade}0er..." };
                        Menu_DeliveryConfirmation.Items.Add(temp);
                    }
                    temp?.Items.Add(i);
                } else {
                    Menu_DeliveryConfirmation.Items.Add(i);
                }
            }
            temp = null;
            Menu_CreditNote.Items.Clear();
            foreach (var s in seasons) {
                var i1 = new MenuItem {
                    Header = $"Saison {s.Year}...",
                    Tag = s.Year,
                    IsEnabled = MemberList.SelectedItem != null,
                    Icon = s.Year == seasons[0].Year ? new TextBlock { FontSize = 16, Text = "\uE734", FontFamily = font } : null,
                };
                i1.SetBinding(IsEnabledProperty, new Binding() { Path = new($"MemberHasDeliveries[{s.Year}]") });
                foreach (var v in s.PaymentVariants.OrderByDescending(v => v.AvNr)) {
                    var i2 = new MenuItem { Header = $"...{v.Name}...", Tag = v.AvNr };
                    var show = new MenuItem { Header = "...anzeigen (PDF)", Icon = new TextBlock { FontSize = 16, Text = "\uE8FF", FontFamily = font } };
                    show.Click += Menu_CreditNote_Show_Click;
                    i2.Items.Add(show);
                    var pdf = new MenuItem { Header = "...speichern... (PDF)", Icon = new TextBlock { FontSize = 16, Text = "\uEA90", FontFamily = font } };
                    pdf.Click += Menu_CreditNote_SavePdf_Click;
                    i2.Items.Add(pdf);
                    var print = new MenuItem { Header = "...drucken", Icon = new TextBlock { FontSize = 16, Text = "\uE749", FontFamily = font } };
                    print.Click += Menu_CreditNote_Print_Click;
                    i2.Items.Add(print);
                    var email = new MenuItem { Header = "...per E-Mail schicken", Icon = new TextBlock { FontSize = 16, Text = "\uE89C", FontFamily = font } };
                    email.SetBinding(IsEnabledProperty, new Binding { Path = new("MemberCanSendEmail") });
                    email.Click += Menu_CreditNote_Email_Click;
                    i2.Items.Add(email);
                    i1.Items.Add(i2);
                }
                var decade = s.Year / 10;
                if (seasons[0].Year / 10 != decade) {
                    if (temp == null || !temp.Header.ToString()!.Contains($"{decade}0er")) {
                        temp = new MenuItem { Header = $"Saisons {decade}0er..." };
                        Menu_CreditNote.Items.Add(temp);
                    }
                    temp?.Items.Add(i1);
                } else {
                    Menu_CreditNote.Items.Add(i1);
                }
            }
            temp = null;

            await RefreshList();

            var (s1, s2) = ('\u2007', '\u202f');
            var mA = $"{await ctx.Members.CountAsync(m => m.IsActive):N0}";
            var mI = $"{await ctx.Members.CountAsync(m => !m.IsActive):N0}";
            var mT = $"{await ctx.Members.CountAsync():N0}";
            var mM = Math.Max(mA.Length, Math.Max(mI.Length, mT.Length));
            var mS = mM > 3;
            if (mS) mM--;
            ViewModel.StatusMembersToolTip =
                $"{new string(s1, Math.Max(0, mM - mA.Length))}{(mS && mA.Length < 4 ? s2 : "")}{mA} aktive Mitglieder\n" +
                $"{new string(s1, Math.Max(0, mM - mI.Length))}{(mS && mI.Length < 4 ? s2 : "")}{mI} nicht aktive Mitglieder\n" +
                $"{new string(s1, Math.Max(0, mM - mT.Length))}{(mS && mT.Length < 4 ? s2 : "")}{mT} Mitglieder gesamt";

            var bA = $"{await ctx.Members.Where(m => m.IsActive).SumAsync(m => m.BusinessShares):N0}";
            var bI = $"{await ctx.Members.Where(m => !m.IsActive).SumAsync(m => m.BusinessShares):N0}";
            var bT = $"{await ctx.Members.SumAsync(m => m.BusinessShares):N0}";
            var bM = Math.Max(bA.Length, Math.Max(bI.Length, bT.Length));
            var bS = bM > 3;
            if (bS) bM--;
            ViewModel.StatusBusinessSharesToolTip =
                $"{new string(s1, Math.Max(0, bM - bA.Length))}{(bS && bA.Length < 4 ? s2 : "")}{bA} Geschäftsanteile von aktiven Mitgliedern\n" +
                $"{new string(s1, Math.Max(0, bM - bI.Length))}{(bS && bI.Length < 4 ? s2 : "")}{bI} Geschäftsanteile von nicht aktiven Mitgliedern\n" +
                $"{new string(s1, Math.Max(0, bM - bT.Length))}{(bS && bT.Length < 4 ? s2 : "")}{bT} Geschäftsanteile gesamt";
        }

        private void SetPhoneNrInputVisible(int nr, bool visible, int? position = null) {
            var inputs = PhoneNrInputs[nr];
            if (position is int p) {
                var mt = 10 + p * 30;
                inputs.Type.Margin = new(6, mt, 5, 0);
                inputs.Number.Margin = new(0, mt, 5, 0);
                inputs.Comment.Margin = new(0, mt, 10, 0);
            }
            var vis = visible ? Visibility.Visible : Visibility.Hidden;
            inputs.Type.Visibility = vis;
            inputs.Number.Visibility = vis;
            inputs.Comment.Visibility = vis;
        }

        private void SetEmailAddressInputVisible(int nr, bool visible, int? position = null) {
            var inputs = EmailAddressInputs[nr];
            if (position is int p) {
                var mt = 10 + p * 30;
                inputs.Label.Margin = new(10, mt, 0, 0);
                inputs.Address.Margin = new(0, mt, 10, 0);
            }
            var vis = visible ? Visibility.Visible : Visibility.Hidden;
            inputs.Label.Visibility = vis;
            inputs.Address.Visibility = vis;
        }

        private void MemberList_SelectionChanged(object sender, RoutedEventArgs evt) {
            RefreshInputs();
            if (MemberList.SelectedItem is Member m) {
                Menu_Export_ExportSelected.IsEnabled = !IsEditing && !IsCreating;
                Menu_Export_UploadSelected.IsEnabled = !IsEditing && !IsCreating && App.Config.SyncUrl != null;
            } else {
                Menu_Export_ExportSelected.IsEnabled = false;
                Menu_Export_UploadSelected.IsEnabled = false;
            }
        }

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

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

        private async void NewMemberButton_Click(object? sender, RoutedEventArgs? evt) {
            IsCreating = true;
            MemberList.IsEnabled = false;
            ViewModel.SelectedMember = null;
            ViewModel.TransferPredecessorAreaComs = null;
            ViewModel.CancelAreaComs = null;
            HideNewEditDeleteButtons();
            ShowSaveResetCancelButtons();
            UnlockInputs();
            UpdateContactInfoVisibility(true);
            await InitInputs();
            ViewModel.EnableSearchInputs = false;
        }

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

        private void EditMemberButton_Click(object? sender, RoutedEventArgs? evt) {
            if (ViewModel.SelectedMember == null) return;

            IsEditing = true;
            MemberList.IsEnabled = false;
            ViewModel.TransferPredecessorAreaComs = null;
            ViewModel.CancelAreaComs = null;

            HideNewEditDeleteButtons();
            ShowSaveResetCancelButtons();
            UnlockInputs();
            UpdateContactInfoVisibility(true);
            ViewModel.EnableSearchInputs = false;
        }

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

        private async void DeleteMemberButton_Click(object? sender, RoutedEventArgs? evt) {
            if (ViewModel.SelectedMember is not Member m)
                return;

            int areaComs = 0, deliveries = 0, credits = 0;
            using (var ctx = new AppDbContext()) {
                var l = (await ctx.Members.FindAsync(m.MgNr))!;
                areaComs = l.AreaCommitments.Count;
                deliveries = l.Deliveries.Count;
                credits = l.Credits.Count;
            }
            var d = new DeleteMemberDialog(m.MgNr, m.AdministrativeName, areaComs, deliveries, credits);
            if (d.ShowDialog() == true) {
                Mouse.OverrideCursor = Cursors.AppStarting;
                try {
                    await MemberService.DeleteMember(m.MgNr, d.DeletePaymentData, d.DeleteDeliveries, d.DeleteAreaComs);
                } 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, "Mitglied löschen", MessageBoxButton.OK, MessageBoxImage.Error);
                }
                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) {
            Mouse.OverrideCursor = Cursors.AppStarting;
            SaveButton.IsEnabled = false;

            int mgnr;
            try {
                mgnr = await ViewModel.UpdateMember(ViewModel.SelectedMember?.MgNr);
            } 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, "Mitglied aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error);
                SaveButton.IsEnabled = true;
                return;
            } finally {
                Mouse.OverrideCursor = null;
            }

            IsEditing = false;
            IsCreating = false;
            MemberList.IsEnabled = true;
            HideSaveResetCancelButtons();
            ShowNewEditDeleteButtons();
            LockInputs();
            UpdateContactInfoVisibility();
            ViewModel.EnableSearchInputs = true;
            FinishInputFilling();
            await RefreshList();
            RefreshInputs();
            ViewModel.SearchQuery = "";
            if (mgnr is int m)
                FocusMember(m);
        }

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

        private async void ResetButton_Click(object? sender, RoutedEventArgs? evt) {
            ViewModel.TransferPredecessorAreaComs = null;
            ViewModel.CancelAreaComs = null;
            if (IsEditing) {
                RefreshInputs();
            } else if (IsCreating) {
                ClearInputs();
                await InitInputs();
            }
            UpdateButtons();
        }

        private void CancelButton_Click(object sender, RoutedEventArgs evt) {
            IsEditing = false;
            IsCreating = false;
            MemberList.IsEnabled = true;
            ViewModel.TransferPredecessorAreaComs = null;
            ViewModel.CancelAreaComs = null;
            HideSaveResetCancelButtons();
            ShowNewEditDeleteButtons();
            RefreshInputs();
            LockInputs();
            UpdateContactInfoVisibility();
            ViewModel.EnableSearchInputs = true;
        }

        private void AreaCommitmentButton_Click(object sender, RoutedEventArgs evt) {
            App.FocusMemberAreaComs(ViewModel.SelectedMember!.MgNr);
        }

        private void DeliveryButton_Click(object sender, RoutedEventArgs evt) {
            App.FocusMemberDeliveries(ViewModel.SelectedMember!.MgNr);
        }

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

        private void Menu_Contact_Email_Click(object sender, RoutedEventArgs evt) {
            if (ViewModel.SelectedMember is not Member m) return;
            Utils.MailTo(m.EmailAddresses.Select(a => a.Address));
        }

        private async void Menu_Contact_Letterhead_Click(object sender, RoutedEventArgs evt) {
            if (ViewModel.SelectedMember is not Member m) return;
            Mouse.OverrideCursor = Cursors.AppStarting;
            try {
                using var doc = new Letterhead(m);
                await doc.Generate();
                if (!App.Config.Debug) {
                    await doc.Print();
                } else {
                    doc.Show();
                }
            } catch (Exception exc) {
                MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            Mouse.OverrideCursor = null;
        }

        private async void Menu_MemberDataSheet_Show_Click(object sender, RoutedEventArgs evt) {
            if (ViewModel.SelectedMember is not Member m) return;
            await MemberService.GenerateMemberDataSheet(m, ExportMode.Show);
        }

        private async void Menu_MemberDataSheet_SavePdf_Click(object sender, RoutedEventArgs evt) {
            if (ViewModel.SelectedMember is not Member m) return;
            await MemberService.GenerateMemberDataSheet(m, ExportMode.SavePdf);
        }

        private async void Menu_MemberDataSheet_Print_Click(object sender, RoutedEventArgs evt) {
            if (ViewModel.SelectedMember is not Member m) return;
            await MemberService.GenerateMemberDataSheet(m, ExportMode.Print);
        }

        private async void Menu_MemberDataSheet_Email_Click(object sender, RoutedEventArgs evt) {
            if (ViewModel.SelectedMember is not Member m) return;
            var res = MessageBox.Show("Soll eine E-Mail verschickt werden?", "Stammdatenblatt verschicken",
                MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.Yes);
            if (res != MessageBoxResult.Yes)
                return;
            await MemberService.GenerateMemberDataSheet(m, ExportMode.Email);
        }

        private async void Menu_DeliveryConfirmation_Show_Click(object sender, RoutedEventArgs evt) {
            var year = (int?)((sender as MenuItem)?.Parent as MenuItem)?.Tag;
            if (ViewModel.SelectedMember is not Member m || year == null)
                return;
            await MemberService.GenerateDeliveryConfirmation(m, (int)year, ExportMode.Show);
        }

        private async void Menu_DeliveryConfirmation_SavePdf_Click(object sender, RoutedEventArgs evt) {
            var year = (int?)((sender as MenuItem)?.Parent as MenuItem)?.Tag;
            if (ViewModel.SelectedMember is not Member m || year == null)
                return;
            await MemberService.GenerateDeliveryConfirmation(m, (int)year, ExportMode.SavePdf);
        }

        private async void Menu_DeliveryConfirmation_Print_Click(object sender, RoutedEventArgs evt) {
            var year = (int?)((sender as MenuItem)?.Parent as MenuItem)?.Tag;
            if (ViewModel.SelectedMember is not Member m || year == null)
                return;
            await MemberService.GenerateDeliveryConfirmation(m, (int)year, ExportMode.Print);
        }

        private async void Menu_DeliveryConfirmation_Email_Click(object sender, RoutedEventArgs evt) {
            var year = (int?)((sender as MenuItem)?.Parent as MenuItem)?.Tag;
            if (ViewModel.SelectedMember is not Member m || year == null)
                return;
            var res = MessageBox.Show("Soll eine E-Mail verschickt werden?", "Anlieferungsbestätigung verschicken",
                MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.Yes);
            if (res != MessageBoxResult.Yes)
                return;
            await MemberService.GenerateDeliveryConfirmation(m, (int)year, ExportMode.Email);
        }

        private async void Menu_CreditNote_Show_Click(object sender, RoutedEventArgs evt) {
            var year = (int?)(((sender as MenuItem)?.Parent as MenuItem)?.Parent as MenuItem)?.Tag;
            var avnr = (int?)((sender as MenuItem)?.Parent as MenuItem)?.Tag;
            if (ViewModel.SelectedMember is not Member m || year == null || avnr == null)
                return;
            await MemberService.GenerateCreditNote(m, (int)year, (int)avnr, ExportMode.Show);
        }

        private async void Menu_CreditNote_SavePdf_Click(object sender, RoutedEventArgs evt) {
            var year = (int?)(((sender as MenuItem)?.Parent as MenuItem)?.Parent as MenuItem)?.Tag;
            var avnr = (int?)((sender as MenuItem)?.Parent as MenuItem)?.Tag;
            if (ViewModel.SelectedMember is not Member m || year == null || avnr == null)
                return;
            await MemberService.GenerateCreditNote(m, (int)year, (int)avnr, ExportMode.SavePdf);
        }

        private async void Menu_CreditNote_Print_Click(object sender, RoutedEventArgs evt) {
            var year = (int?)(((sender as MenuItem)?.Parent as MenuItem)?.Parent as MenuItem)?.Tag;
            var avnr = (int?)((sender as MenuItem)?.Parent as MenuItem)?.Tag;
            if (ViewModel.SelectedMember is not Member m || year == null || avnr == null)
                return;
            await MemberService.GenerateCreditNote(m, (int)year, (int)avnr, ExportMode.Print);
        }

        private async void Menu_CreditNote_Email_Click(object sender, RoutedEventArgs evt) {
            var year = (int?)(((sender as MenuItem)?.Parent as MenuItem)?.Parent as MenuItem)?.Tag;
            var avnr = (int?)((sender as MenuItem)?.Parent as MenuItem)?.Tag;
            if (ViewModel.SelectedMember is not Member m || year == null || avnr == null)
                return;
            var res = MessageBox.Show("Soll eine E-Mail verschickt werden?", "Traubengutschrift verschicken",
                MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.Yes);
            if (res != MessageBoxResult.Yes)
                return;
            await MemberService.GenerateCreditNote(m, (int)year, (int)avnr, ExportMode.Email);
        }

        private async void Menu_List_SaveActive_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateMemberList(MemberService.ExportSubject.AllActive, ExportMode.SaveList);
        private async void Menu_List_ShowActive_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateMemberList(MemberService.ExportSubject.AllActive, ExportMode.Show);
        private async void Menu_List_SavePdfActive_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateMemberList(MemberService.ExportSubject.AllActive, ExportMode.SavePdf);
        private async void Menu_List_PrintActive_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateMemberList(MemberService.ExportSubject.AllActive, ExportMode.Print);
        private async void Menu_List_SaveFilters_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateMemberList(MemberService.ExportSubject.FromFilters, ExportMode.SaveList);
        private async void Menu_List_ShowFilters_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateMemberList(MemberService.ExportSubject.FromFilters, ExportMode.Show);
        private async void Menu_List_SavePdfFilters_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateMemberList(MemberService.ExportSubject.FromFilters, ExportMode.SavePdf);
        private async void Menu_List_PrintFilters_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateMemberList(MemberService.ExportSubject.FromFilters, ExportMode.Print);
        private async void Menu_List_SaveAll_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateMemberList(MemberService.ExportSubject.All, ExportMode.SaveList);
        private async void Menu_List_ShowAll_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateMemberList(MemberService.ExportSubject.All, ExportMode.Show);
        private async void Menu_List_SavePdfAll_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateMemberList(MemberService.ExportSubject.All, ExportMode.SavePdf);
        private async void Menu_List_PrintAll_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateMemberList(MemberService.ExportSubject.All, ExportMode.Print);

        private async void Menu_Export_ExportAll_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateMemberList(MemberService.ExportSubject.All, ExportMode.Export);
        private async void Menu_Export_ExportFilters_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateMemberList(MemberService.ExportSubject.FromFilters, ExportMode.Export);
        private async void Menu_Export_ExportSelected_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateMemberList(MemberService.ExportSubject.Selected, ExportMode.Export);

        private async void Menu_Export_UploadAll_Click(object sender, RoutedEventArgs evt) {
            if (App.Config.SyncUrl == null) return;
            await ViewModel.GenerateMemberList(MemberService.ExportSubject.All, ExportMode.Upload);
        }

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

        private async void Menu_Export_UploadSelected_Click(object sender, RoutedEventArgs evt) {
            if (App.Config.SyncUrl == null) return;
            await ViewModel.GenerateMemberList(MemberService.ExportSubject.Selected, ExportMode.Upload);
        }

        private async void Menu_List_Order_Click(object sender, RoutedEventArgs evt) {
            Menu_List.IsSubmenuOpen = true;
            if (sender == Menu_List_OrderMgNr) {
                App.Client.OrderingMemberList = 0;
                ViewModel.MemberListOrderByMgNr = true;
                ViewModel.MemberListOrderByName = false;
                ViewModel.MemberListOrderByOrt = false;
            } else if (sender == Menu_List_OrderName) {
                App.Client.OrderingMemberList = 1;
                ViewModel.MemberListOrderByMgNr = false;
                ViewModel.MemberListOrderByName = true;
                ViewModel.MemberListOrderByOrt = false;
            } else if (sender == Menu_List_OrderOrt) {
                App.Client.OrderingMemberList = 2;
                ViewModel.MemberListOrderByMgNr = false;
                ViewModel.MemberListOrderByName = false;
                ViewModel.MemberListOrderByOrt = true;
            }
            await App.Client.UpdateValues();
        }

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

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

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

        private void ShowNewEditDeleteButtons() {
            NewMemberButton.IsEnabled = true;
            EditMemberButton.IsEnabled = ViewModel.SelectedMember != null;
            DeleteMemberButton.IsEnabled = ViewModel.SelectedMember != null;
            ViewModel.ControlButtonsVisibility = Visibility.Visible;
        }

        private void HideNewEditDeleteButtons() {
            NewMemberButton.IsEnabled = false;
            EditMemberButton.IsEnabled = false;
            DeleteMemberButton.IsEnabled = false;
            ViewModel.ControlButtonsVisibility = Visibility.Hidden;
        }

        private void UpdateContactInfoVisibility(bool extra = false) {
            var m = ViewModel.SelectedMember;
            bool lastVisible;
            int num = 0;
            lastVisible = true;
            for (int i = 0; i < EmailAddressInputs.Length; i++) {
                var input = EmailAddressInputs[i];
                var vis = !string.IsNullOrEmpty(input.Address.Text) || (m?.EmailAddresses.Any(a => a.Nr - 1 == i) ?? false);
                var cVis = vis || (extra && lastVisible);
                SetEmailAddressInputVisible(i, cVis, cVis ? num++ : null);
                lastVisible = vis;
            }
            lastVisible = true;
            for (int i = 0; i < PhoneNrInputs.Length; i++) {
                var input = PhoneNrInputs[i];
                var vis = !string.IsNullOrEmpty(input.Number.Text) || (m?.TelephoneNumbers.Any(n => n.Nr - 1 == i) ?? false);
                var cVis = vis || (extra && lastVisible);
                SetPhoneNrInputVisible(i, cVis, cVis ? num++ : null);
                lastVisible = vis;
            }
        }

        private void FillInputs(Member m) {
            ClearOriginalValues();
            ClearDefaultValues();
            ViewModel.FillInputs(m);
            UpdateContactInfoVisibility(IsEditing || IsCreating);
            FinishInputFilling();
        }

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

        override protected void UpdateButtons() {
            if (!IsEditing && !IsCreating) return;
            bool ch = HasChanged, v = IsValid;
            ResetButton.IsEnabled = ch;
            SaveButton.IsEnabled = v && ch;
        }

        protected void InputTextChanged(TextBox input, Func<TextBox, bool, Member?, ValidationResult> checker) {
            InputTextChanged(input, checker(input, SenderIsRequired(input), ViewModel.SelectedMember));
        }

        protected void InputLostFocus(TextBox input, Func<TextBox, bool, Member?, ValidationResult> checker, string? msg = null) {
            InputLostFocus(input, checker(input, SenderIsRequired(input), ViewModel.SelectedMember), msg);
        }

        private void MgNrInput_TextChanged(object sender, RoutedEventArgs evt) {
            InputTextChanged((TextBox)sender, Validator.CheckNewMgNr);
        }

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

        private void PredecessorMgNrInput_TextChanged(object sender, RoutedEventArgs evt) {
            InputTextChanged((TextBox)sender, Validator.CheckPredecessorMgNr);
        }

        private async void PredecessorMgNrInput_LostFocus(object sender, RoutedEventArgs evt) {
            var valid = InputLostFocus((TextBox)sender, Validator.CheckPredecessorMgNr);
            if (valid && ViewModel.PredecessorMgNr is int mgnr && (IsEditing || IsCreating)) {
                if (ViewModel.MgNr == mgnr)
                    return;
                using var ctx = new AppDbContext();
                var areaComs = await ctx.AreaCommitments
                    .Where(c => c.MgNr == mgnr && (c.YearTo == null || c.YearTo >= Utils.FollowingSeason))
                    .ToListAsync();
                if (areaComs.Count == 0)
                    return;

                var oldMember = (await ctx.Members.FindAsync(mgnr))!;
                var newName = $"{ViewModel.Name?.Replace('ß', 'ẞ').ToUpper()} " +
                    $"{ViewModel.Prefix}{(!string.IsNullOrEmpty(ViewModel.Prefix) ? " " : "")}" +
                    $"{ViewModel.GivenName}{(!string.IsNullOrEmpty(ViewModel.GivenName) ? " " : "")}" +
                    $"{ViewModel.Suffix}{(!string.IsNullOrEmpty(ViewModel.Suffix) ? " " : "")}";
                var d = new AreaComDialog(oldMember.AdministrativeName, newName, areaComs.Count, areaComs.Sum(c => c.Area));
                if (d.ShowDialog() != true)
                    return;
                ViewModel.TransferPredecessorAreaComs = d.SuccessorSeason;
                ViewModel.MaintainAreaComYearTo = d.MaintainYearTo;
                if (IsEditing)
                    SetOriginalValue(PredecessorMgNrInput, -1);  // hack to allow user to save
                UpdateButtons();
            }
        }

        private new void EmailAddressInput_TextChanged(object sender, TextChangedEventArgs evt) {
            if (sender == EmailAddress1Input && ViewModel.ContactViaEmail) {
                InputTextChanged((TextBox)sender, Validator.CheckEmailAddress((TextBox)sender, true));
            } else {
                base.EmailAddressInput_TextChanged(sender, evt);
            }
            UpdateContactInfoVisibility(IsEditing || IsCreating);
        }

        private new void PhoneNrInput_TextChanged(object sender, TextChangedEventArgs evt) {
            base.PhoneNrInput_TextChanged(sender, evt);
            UpdateContactInfoVisibility(IsEditing || IsCreating);
        }

        private void ContactEmailInput_Changed(object sender, RoutedEventArgs evt) {
            CheckBox_Changed(sender, evt);
            EmailAddressInput_TextChanged(EmailAddress1Input, new TextChangedEventArgs(evt.RoutedEvent, UndoAction.None));
        }

        private void KgDetailsButton_Click(object sender, RoutedEventArgs evt) {
            if (DefaultKgInput.SelectedItem is AT_Kg kg) {
                App.FocusOriginHierarchyKg(kg.KgNr);
            } else {
                App.FocusOriginHierarchy();
            }
        }

        private void OrganicButton_Click(object sender, RoutedEventArgs evt) {
            if (ViewModel.SelectedMember is not Member m) return;
            var url = "https://www.easy-cert.com/htm/suchergebnis.htm?" +
                //$"CustomerNumber={m.LfbisNr}&" +
                $"Name={(m.BillingAddress?.FullName ?? m.FullName).Replace(' ', '+')}&" +
                $"PostalCode={(m.BillingAddress?.PostalDest ?? m.PostalDest).AtPlz?.Plz}";
            Process.Start(new ProcessStartInfo(url) {
                UseShellExecute = true,
            });
        }

        private void MemberReferenceButton_Click(object sender, RoutedEventArgs evt) {
            if (ViewModel.SelectedMember is not Member m || m.PredecessorMgNr == null) return;
            FocusMember((int)m.PredecessorMgNr);
        }

        private async void ActiveInput_Changed(object sender, RoutedEventArgs evt) {
            if (MemberList.SelectedItem is not Member m)
                return;
            if ((IsEditing || IsCreating) && ViewModel.IsActive == false && ViewModel.MgNr is int mgnr) {
                using var ctx = new AppDbContext();
                var areaComs = await ctx.AreaCommitments
                    .Where(c => c.MgNr == mgnr && (c.YearTo == null || c.YearTo >= Utils.FollowingSeason))
                    .ToListAsync();
                if (areaComs.Count > 0) {
                    var d = new AreaComDialog(m.AdministrativeName, areaComs.Count, areaComs.Sum(c => c.Area));
                    if (d.ShowDialog() == true) {
                        ViewModel.CancelAreaComs = d.CancelSeason;
                    }
                }
            }
            CheckBox_Changed(sender, evt);
        }

        private void JuridicalPersonInput_Changed(object sender, RoutedEventArgs evt) {
            CheckBox_Changed(sender, evt);
            if (ViewModel.IsJuridicalPerson) {
                ViewModel.PersonalNameVisibility = Visibility.Hidden;
                ViewModel.JuridicalNameVisibility = Visibility.Visible;
                NameInput.Margin = new(0, 40, 10, 0);
                NameInput.SetValue(Grid.ColumnSpanProperty, 3);
                BirthdayLabel.Content = "Gründung:";
                DeceasedInput.Content = "Aufgelöst";
            } else {
                ViewModel.JuridicalNameVisibility = Visibility.Hidden;
                ViewModel.PersonalNameVisibility = Visibility.Visible;
                NameInput.Margin = new(0, 70, 0, 0);
                NameInput.SetValue(Grid.ColumnSpanProperty, 1);
                BirthdayLabel.Content = "Geburtstag:";
                DeceasedInput.Content = "Verstorben";
            }
            ValidateRequiredInputs();
            UpdateButtons();
        }
    }
}