using Elwig.Helpers;
using Elwig.Models.Entities;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System;
using Microsoft.EntityFrameworkCore;
using Elwig.Documents;
using System.Windows.Input;
using System.Windows;
using Elwig.Helpers.Billing;
using Elwig.Models.Dtos;
using Elwig.Helpers.Export;
using Microsoft.Win32;
using Elwig.ViewModels;
using System.IO;
using System.Net.Http;

namespace Elwig.Services {
    public static class MemberService {

        public enum ExportSubject {
            All, AllActive, FromFilters, Selected,
        };

        public static async Task InitInputs(this MemberAdminViewModel vm) {
            using var ctx = new AppDbContext();
            vm.MgNr = await ctx.NextMgNr();
            vm.EntryDate = DateTime.Now.ToString("dd.MM.yyyy");
            if (vm.BranchSource.Count() == 1)
                vm.Branch = vm.BranchSource.First();
            vm.IsActive = true;
            vm.ContactViaPost = true;
            vm.EnableMemberReferenceButton = false;
        }

        public static void ClearInputs(this MemberAdminViewModel vm) {
            vm.IsMemberSelected = false;
            vm.MemberHasEmail = false;
            vm.MemberCanSendEmail = false;
            vm.EnableMemberReferenceButton = false;
            vm.StatusDeliveriesLastSeason = "-";
            vm.StatusDeliveriesLastSeasonInfo = $"{Utils.CurrentLastSeason - 1}";
            vm.StatusDeliveriesLastSeasonToolTip = null;
            vm.StatusDeliveriesThisSeason = "-";
            vm.StatusDeliveriesThisSeasonInfo = $"{Utils.CurrentLastSeason}";
            vm.StatusDeliveriesThisSeasonToolTip = null;
            vm.StatusAreaCommitment = "-";
            vm.StatusAreaCommitmentInfo = $"{Utils.CurrentLastSeason}";
            vm.StatusAreaCommitmentToolTip = null;
            vm.Age = "-";
        }

        public static void FillInputs(this MemberAdminViewModel vm, Member m) {
            vm.IsMemberSelected = true;
            vm.MgNr = m.MgNr;
            vm.PredecessorMgNr = m.PredecessorMgNr;
            vm.IsJuridicalPerson = m.IsJuridicalPerson;
            vm.EnableMemberReferenceButton = m.PredecessorMgNr != null;
            vm.Prefix = m.Prefix;
            vm.GivenName = m.GivenName;
            vm.Name = m.Name;
            vm.Suffix = m.Suffix;
            vm.ForTheAttentionOf = m.ForTheAttentionOf;
            vm.Birthday = (m.Birthday != null) ? string.Join(".", m.Birthday.Split("-").Reverse()) : null;
            if (m.Birthday?.Length == 10) {
                vm.Age = Utils.GetAge(DateOnly.ParseExact(m.Birthday, "yyyy-MM-dd")).ToString();
            } else if (m.Birthday != null) {
                vm.Age = "ca. " + (DateTime.Now.Year - int.Parse(m.Birthday[^4..])).ToString();
            } else {
                vm.Age = "-";
            }
            vm.IsDeceased = m.IsDeceased;
            vm.Address = m.Address;
            if (m.PostalDest.AtPlz is AT_PlzDest p) {
                vm.Plz = p.Plz;
                vm.Ort = ControlUtils.GetItemFromSource(vm.OrtSource, p);
            } else {
                vm.Plz = null;
                vm.Ort = null;
            }

            var emailAddrs = m.EmailAddresses.OrderBy(a => a.Nr).ToList();
            for (int i = 0; i < vm.EmailAddresses.Count; i++) {
                if (i < emailAddrs.Count) {
                    var emailAddr = emailAddrs[i];
                    vm.EmailAddresses[i] = emailAddr.Address;
                } else {
                    vm.EmailAddresses[i] = null;
                }
            }

            var phoneNrs = m.TelephoneNumbers.OrderBy(p => p.Nr).ToList();
            for (int i = 0; i < vm.PhoneNrs.Count; i++) {
                if (i < phoneNrs.Count) {
                    var phoneNr = phoneNrs[i];
                    var idx = vm.PhoneNrTypes.Select((e, i) => (e, i)).FirstOrDefault(kv => kv.e.Key == phoneNr.Type).i;
                    vm.PhoneNrs[i] = new(idx, phoneNr.Number, phoneNr.Comment);
                } else {
                    vm.PhoneNrs[i] = new();
                }
            }

            vm.Iban = m.Iban;
            vm.Bic = m.Bic;

            vm.UstIdNr = m.UstIdNr;
            vm.LfbisNr = m.LfbisNr;
            vm.IsBuchführend = m.IsBuchführend;
            vm.IsOrganic = m.IsOrganic;

            var billingAddr = m.BillingAddress;
            if (billingAddr != null) {
                vm.BillingName = billingAddr.FullName;
                vm.BillingAddress = billingAddr.Address;
                if (billingAddr.PostalDest.AtPlz is AT_PlzDest b) {
                    vm.BillingPlz = b.Plz;
                    vm.BillingOrt = ControlUtils.GetItemFromSource(vm.BillingOrtSource, b);
                }
            } else {
                vm.BillingName = null;
                vm.BillingAddress = null;
                vm.BillingPlz = null;
                vm.BillingOrt = null;
            }

            vm.EntryDate = (m.EntryDateString != null) ? string.Join(".", m.EntryDateString.Split("-").Reverse()) : null;
            vm.ExitDate = (m.ExitDateString != null) ? string.Join(".", m.ExitDateString.Split("-").Reverse()) : null;
            vm.BusinessShares = m.BusinessShares;
            vm.AccountingNr = m.AccountingNr;
            vm.Branch = (Branch?)ControlUtils.GetItemFromSourceWithPk(vm.BranchSource, m.ZwstId);
            vm.DefaultKg = (AT_Kg?)ControlUtils.GetItemFromSourceWithPk(vm.DefaultKgSource, m.DefaultKgNr);
            vm.Comment = m.Comment;
            vm.IsActive = m.IsActive;
            vm.IsVollLieferant = m.IsVollLieferant;
            vm.IsFunktionär = m.IsFunktionär;
            vm.ContactViaPost = m.ContactViaPost;
            vm.ContactViaEmail = m.ContactViaEmail;

            vm.StatusDeliveriesLastSeasonInfo = $"{Utils.CurrentLastSeason - 1}";
            vm.StatusDeliveriesLastSeason = "...";
            vm.StatusDeliveriesLastSeasonToolTip = null;
            vm.StatusDeliveriesThisSeasonInfo = $"{Utils.CurrentLastSeason}";
            vm.StatusDeliveriesThisSeason = "...";
            vm.StatusDeliveriesThisSeasonToolTip = null;
            vm.StatusAreaCommitmentInfo = $"{Utils.CurrentLastSeason}";
            vm.StatusAreaCommitment = "...";
            vm.StatusAreaCommitmentToolTip = null;

            Utils.RunBackground("Mitgliederdaten laden", async () => {
                if (App.MainDispatcher == null)
                    return;
                using var ctx = new AppDbContext();

                var d1 = ctx.Deliveries.Where(d => d.Year == Utils.CurrentLastSeason - 1 && d.MgNr == m.MgNr);
                var (_, d1GridData, _, _) = await DeliveryService.GenerateToolTipData(d1.SelectMany(d => d.Parts));
                var textLast = $"{await d1.CountAsync():N0} ({await d1.SumAsync(d => d.Parts.Count):N0}), {await d1.SelectMany(d => d.Parts).SumAsync(p => p.Weight):N0} kg";

                var d2 = ctx.Deliveries.Where(d => d.Year == Utils.CurrentLastSeason && d.MgNr == m.MgNr);
                var (_, d2GridData, _, _) = await DeliveryService.GenerateToolTipData(d2.SelectMany(d => d.Parts));
                var textThis = $"{await d2.CountAsync():N0} ({await d2.SumAsync(d => d.Parts.Count):N0}), {await d2.SelectMany(d => d.Parts).SumAsync(p => p.Weight):N0} kg";

                var c = m.ActiveAreaCommitments(ctx, Utils.CurrentLastSeason);
                int maxKgPerHa = 10_000;
                try {
                    var s = await ctx.Seasons.FindAsync(await ctx.Seasons.MaxAsync(s => s.Year));
                    if (s != null) maxKgPerHa = s.MaxKgPerHa;
                } catch { }
                var (text, gridData) = await AreaComService.GenerateToolTipData(c, maxKgPerHa);

                var deliveries = await ctx.Deliveries
                    .Where(d => d.MgNr == m.MgNr)
                    .GroupBy(d => d.Year)
                    .ToDictionaryAsync(g => g.Key, g => g.Any());

                if (m.MgNr != vm.MgNr)
                    return;

                await App.MainDispatcher.BeginInvoke(() => {
                    var (d1Grid, _) = DeliveryService.GenerateToolTip(d1GridData, []);
                    var (d2Grid, _) = DeliveryService.GenerateToolTip(d2GridData, []);
                    var grid = AreaComService.GenerateToolTip(gridData);
                    vm.StatusDeliveriesLastSeasonInfo = $"{Utils.CurrentLastSeason - 1}";
                    vm.StatusDeliveriesLastSeason = textLast;
                    vm.StatusDeliveriesLastSeasonToolTip = d1Grid;
                    vm.StatusDeliveriesThisSeasonInfo = $"{Utils.CurrentLastSeason}";
                    vm.StatusDeliveriesThisSeason = textThis;
                    vm.StatusDeliveriesThisSeasonToolTip = d2Grid;
                    vm.StatusAreaCommitmentInfo = $"{Utils.CurrentLastSeason}";
                    vm.StatusAreaCommitment = text;
                    vm.StatusAreaCommitmentToolTip = grid;
                    vm.MemberHasDeliveries = Enumerable.Range(0, 9999).Select(i => deliveries.GetValueOrDefault(i, false)).ToList();
                });
            });

            vm.MemberHasEmail = m.EmailAddresses.Count > 0;
            vm.MemberCanSendEmail = App.Config.Smtp != null && m.EmailAddresses.Count > 0;
            vm.MemberHasDeliveries = Enumerable.Range(0, 9999).Select(i => false).ToList();
        }

        public static async Task<(List<string>, IQueryable<Member>, List<string>)> GetFilters(this MemberAdminViewModel vm, AppDbContext ctx) {
            List<string> filterNames = [];
            IQueryable<Member> memberQuery = ctx.Members;
            if (vm.ShowOnlyActiveMembers) {
                memberQuery = memberQuery.Where(m => m.IsActive);
                filterNames.Add("aktive Mitglieder");
            }

            var filterMgNr = new List<int>();
            var filterZwst = new List<string>();
            var filterKgNr = new List<int>();
            var filterLfbisNr = new List<string>();
            var filterUstIdNr = new List<string>();
            var filterAreaCom = new List<string>();
            var filterNotAreaCom = new List<string>();

            var filter = vm.TextFilter;
            if (filter.Count > 0) {
                var branches = await ctx.Branches.ToListAsync();
                var mgnr = await ctx.Members.ToDictionaryAsync(m => m.MgNr.ToString(), m => m);
                var kgs = await ctx.WbKgs.ToDictionaryAsync(k => k.AtKg.Name.ToLower(), k => k.AtKg);
                var areaComs = await ctx.AreaCommitmentTypes.ToDictionaryAsync(t => $"{t.SortId}{t.AttrId}", t => t);

                for (int i = 0; i < filter.Count; i++) {
                    var e = filter[i];

                    if (e.Equals("aktiv", StringComparison.CurrentCultureIgnoreCase)) {
                        memberQuery = memberQuery.Where(m => m.IsActive);
                        filter.RemoveAt(i--);
                        filterNames.Add("aktive Mitglieder");
                    } else if (e.Equals("!aktiv", StringComparison.CurrentCultureIgnoreCase)) {
                        memberQuery = memberQuery.Where(m => !m.IsActive);
                        filter.RemoveAt(i--);
                        filterNames.Add("inaktive Mitglieder");
                    } else if (e.Length >= 5 && e.Length <= 10 && "funktionär".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
                        memberQuery = memberQuery.Where(m => m.IsFunktionär);
                        filter.RemoveAt(i--);
                        filterNames.Add("Funktionäre");
                    } else if (e.Length >= 6 && e.Length <= 11 && e[0] == '!' && "funktionär".StartsWith(e[1..], StringComparison.CurrentCultureIgnoreCase)) {
                        memberQuery = memberQuery.Where(m => !m.IsFunktionär);
                        filter.RemoveAt(i--);
                        filterNames.Add("Nicht-Funktionäre");
                    } else if (e.Equals("bio", StringComparison.CurrentCultureIgnoreCase)) {
                        memberQuery = memberQuery.Where(m => m.IsOrganic);
                        filter.RemoveAt(i--);
                        filterNames.Add("Bio-Betriebe");
                    } else if (e.Equals("!bio", StringComparison.CurrentCultureIgnoreCase)) {
                        memberQuery = memberQuery.Where(m => !m.IsOrganic);
                        filter.RemoveAt(i--);
                        filterNames.Add("Nicht-Bio-Betriebe");
                    } else if (e.Length >= 4 && e.Length <= 13 && "volllieferant".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
                        memberQuery = memberQuery.Where(m => m.IsVollLieferant);
                        filter.RemoveAt(i--);
                        filterNames.Add("Volllieferanten");
                    } else if (e.Length >= 5 && e.Length <= 14 && e[0] == '!' && "volllieferant".StartsWith(e[1..], StringComparison.CurrentCultureIgnoreCase)) {
                        memberQuery = memberQuery.Where(m => !m.IsVollLieferant);
                        filter.RemoveAt(i--);
                        filterNames.Add("Nicht-Vollieferanten");
                    } else if (e.Length >= 5 && e.Length <= 11 && "buchführend".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
                        memberQuery = memberQuery.Where(m => m.IsBuchführend);
                        filter.RemoveAt(i--);
                        filterNames.Add("buchführend");
                    } else if (e.Length >= 6 && e.Length <= 12 && e[0] == '!' && "buchführend".StartsWith(e[1..], StringComparison.CurrentCultureIgnoreCase)) {
                        memberQuery = memberQuery.Where(m => !m.IsBuchführend);
                        filter.RemoveAt(i--);
                        filterNames.Add("pauschaliert");
                    } else if (e.Length >= 8 && e.Length <= 12 && "pauschaliert".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
                        memberQuery = memberQuery.Where(m => !m.IsBuchführend);
                        filter.RemoveAt(i--);
                        filterNames.Add("pauschaliert");
                    } else if (e.Length >= 9 && e.Length <= 13 && e[0] == '!' && "pauschaliert".StartsWith(e[1..], StringComparison.CurrentCultureIgnoreCase)) {
                        memberQuery = memberQuery.Where(m => m.IsBuchführend);
                        filter.RemoveAt(i--);
                        filterNames.Add("buchführend");
                    } else if (e.Equals("email", StringComparison.CurrentCultureIgnoreCase)) {
                        memberQuery = memberQuery.Where(m => m.EmailAddresses.Count > 0);
                        filter.RemoveAt(i--);
                        filterNames.Add("mind. eine E-Mail-Adresse");
                    } else if (e.Equals("!email", StringComparison.CurrentCultureIgnoreCase)) {
                        memberQuery = memberQuery.Where(m => m.EmailAddresses.Count == 0);
                        filter.RemoveAt(i--);
                        filterNames.Add("keine E-Mail-Adresse");
                    } else if (e.Equals("telnr", StringComparison.CurrentCultureIgnoreCase)) {
                        memberQuery = memberQuery.Where(m => m.TelephoneNumbers.Count > 0);
                        filter.RemoveAt(i--);
                        filterNames.Add("mind. eine Tel.-Nr.");
                    } else if (e.Equals("!telnr", StringComparison.CurrentCultureIgnoreCase)) {
                        memberQuery = memberQuery.Where(m => m.TelephoneNumbers.Count == 0);
                        filter.RemoveAt(i--);
                        filterNames.Add("keine Tel.-Nr.");
                    } else if (e.Equals("kontakt:email", StringComparison.CurrentCultureIgnoreCase)) {
                        memberQuery = memberQuery.Where(m => m.ContactViaEmail);
                        filter.RemoveAt(i--);
                        filterNames.Add("Kontaktart E-Mail");
                    } else if (e.Equals("!kontakt:email", StringComparison.CurrentCultureIgnoreCase)) {
                        memberQuery = memberQuery.Where(m => !m.ContactViaEmail);
                        filter.RemoveAt(i--);
                        filterNames.Add("nicht Kontaktart E-Mail");
                    } else if (e.Equals("kontakt:post", StringComparison.CurrentCultureIgnoreCase)) {
                        memberQuery = memberQuery.Where(m => m.ContactViaPost);
                        filter.RemoveAt(i--);
                        filterNames.Add("Kontaktart Post");
                    } else if (e.Equals("!kontakt:post", StringComparison.CurrentCultureIgnoreCase)) {
                        memberQuery = memberQuery.Where(m => !m.ContactViaPost);
                        filter.RemoveAt(i--);
                        filterNames.Add("nicht Kontaktart Post");
                    } else if (e.All(char.IsAsciiDigit) && mgnr.ContainsKey(e)) {
                        filterMgNr.Add(int.Parse(e));
                        filter.RemoveAt(i--);
                        filterNames.Add($"MgNr. {e}");
                    } else if (kgs.TryGetValue(e, out var kg)) {
                        filterKgNr.Add(kg.KgNr);
                        filter.RemoveAt(i--);
                        filterNames.Add($"Stammgemeinde {kg.Name}");
                    } else if (e.StartsWith("zwst:")) {
                        try {
                            var branch = branches.Where(b => b.Name.StartsWith(e[5..], StringComparison.CurrentCultureIgnoreCase)).Single();
                            filterZwst.Add(branch.ZwstId);
                            filter.RemoveAt(i--);
                            filterNames.Add($"Zweigstelle {branch.Name}");
                        } catch (InvalidOperationException) { }
                    } else if (e.StartsWith('+') && e[1..].All(char.IsAsciiDigit)) {
                        memberQuery = memberQuery.Where(m => m.TelephoneNumbers.Any(t => t.Number.Replace(" ", "").StartsWith(e)));
                        filter.RemoveAt(i--);
                        filterNames.Add($"Tel.-Nr. {e}");
                    } else if (e.Length >= 5 && e.Length <= 14 && "flächenbindung".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
                        filterAreaCom.AddRange(areaComs.Keys);
                        filter.RemoveAt(i--);
                        filterNames.Add($"Flächenbindung");
                    } else if (e.Length >= 6 && e.Length <= 15 && "!flächenbindung".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
                        filterNotAreaCom.AddRange(areaComs.Keys);
                        filter.RemoveAt(i--);
                        filterNames.Add($"keine Flächenbindung");
                    } else if (areaComs.ContainsKey(e.ToUpper())) {
                        filterAreaCom.Add(e.ToUpper());
                        filter.RemoveAt(i--);
                        filterNames.Add($"Flächenbindung {e.ToUpper()}");
                    } else if (e.Length >= 3 && e[0] == '!' && areaComs.ContainsKey(e[1..].ToUpper())) {
                        filterNotAreaCom.Add(e[1..].ToUpper());
                        filter.RemoveAt(i--);
                        filterNames.Add($"ohne Flächenbindung {e[1..].ToUpper()}");
                    } else if (Validator.CheckLfbisNr(e)) {
                        filterLfbisNr.Add(e);
                        filter.RemoveAt(i--);
                        filterNames.Add($"Betriebsnummer {e}");
                    } else if (Validator.CheckUstIdNr(e.ToUpper())) {
                        filterUstIdNr.Add(e.ToUpper());
                        filter.RemoveAt(i--);
                        filterNames.Add($"UID {e.ToUpper()}");
                    } else if (e.Length > 2 && e.StartsWith('"') && e.EndsWith('"')) {
                        filter[i] = e[1..^1];
                    } else if (e.Length < 2) {
                        filter.RemoveAt(i--);
                    }
                }

                if (filterMgNr.Count > 0) memberQuery = memberQuery.Where(m => filterMgNr.Contains(m.MgNr));
                if (filterKgNr.Count > 0) memberQuery = memberQuery.Where(m => m.DefaultKgNr != null && filterKgNr.Contains((int)m.DefaultKgNr));
                if (filterZwst.Count > 0) memberQuery = memberQuery.Where(m => m.ZwstId != null && filterZwst.Contains(m.ZwstId));
                if (filterAreaCom.Count > 0) memberQuery = memberQuery.Where(m => m.AreaCommitments.AsQueryable().Where(Utils.ActiveAreaCommitments()).Any(c => filterAreaCom.Contains(c.VtrgId)));
                if (filterNotAreaCom.Count > 0) memberQuery = memberQuery.Where(m => !m.AreaCommitments.AsQueryable().Where(Utils.ActiveAreaCommitments()).All(c => filterNotAreaCom.Contains(c.VtrgId)));
                if (filterLfbisNr.Count > 0) memberQuery = memberQuery.Where(m => m.LfbisNr != null && filterLfbisNr.Contains(m.LfbisNr));
                if (filterUstIdNr.Count > 0) memberQuery = memberQuery.Where(m => m.UstIdNr != null && filterUstIdNr.Contains(m.UstIdNr));
            }

            return (filterNames, memberQuery, filter);
        }

        public static async Task GenerateMemberDataSheet(Member m, ExportMode mode) {
            Mouse.OverrideCursor = Cursors.Wait;
            await Task.Run(async () => {
                try {
                    using var ctx = new AppDbContext();
                    using var doc = new MemberDataSheet(m, ctx);
                    await Utils.ExportDocument(doc, mode, emailData: (m, MemberDataSheet.Name, "Im Anhang finden Sie das aktuelle Stammdatenblatt"));
                } catch (Exception exc) {
                    MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
                }
            });
            Mouse.OverrideCursor = null;
        }

        public static async Task GenerateDeliveryConfirmation(Member m, int year, ExportMode mode) {
            Mouse.OverrideCursor = Cursors.Wait;
            await Task.Run(async () => {
                try {
                    var b = new Billing(year);
                    await b.FinishSeason();
                    await b.CalculateBuckets();
                    App.HintContextChange();

                    using var ctx = new AppDbContext();
                    var data = await DeliveryConfirmationDeliveryData.ForMember(ctx.DeliveryParts, year, m);
                    using var doc = new DeliveryConfirmation(ctx, year, m, data);
                    await Utils.ExportDocument(doc, mode, emailData: (m, $"{DeliveryConfirmation.Name} {year}", $"Im Anhang finden Sie die Anlieferungsbestätigung {year}"));
                } catch (Exception exc) {
                    MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
                }
            });
            Mouse.OverrideCursor = null;
        }

        public static async Task GenerateCreditNote(Member m, int year, int avnr, ExportMode mode) {
            Mouse.OverrideCursor = Cursors.Wait;
            await Task.Run(async () => {
                try {
                    using var ctx = new AppDbContext();
                    var v = (await ctx.PaymentVariants.FindAsync(year, avnr))!;
                    var data = await CreditNoteDeliveryData.ForPaymentVariant(ctx.CreditNoteDeliveryRows, ctx.Seasons, year, avnr);
                    var p = (await ctx.MemberPayments.FindAsync(year, avnr, m.MgNr))!;
                    var b = BillingData.FromJson((await ctx.PaymentVariants.FindAsync(year, avnr))!.Data);

                    using var doc = new CreditNote(ctx, p, data[m.MgNr],
                        b.ConsiderContractPenalties, b.ConsiderTotalPenalty, b.ConsiderAutoBusinessShares, b.ConsiderCustomModifiers,
                        await ctx.GetMemberUnderDelivery(year, m.MgNr));
                    await Utils.ExportDocument(doc, mode, emailData: (m, $"{CreditNote.Name} {v.Name}", $"Im Anhang finden Sie die Traubengutschrift {v.Name}"));
                } catch (Exception exc) {
                    MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
                }
            });
            Mouse.OverrideCursor = null;
        }

        public static async Task GenerateMemberList(this MemberAdminViewModel vm, ExportSubject subject, ExportMode mode) {
            using var ctx = new AppDbContext();
            IQueryable<Member> query;
            List<string> filterNames = [];
            if (subject == ExportSubject.All) {
                query = ctx.Members;
            } else if (subject == ExportSubject.AllActive) {
                query = ctx.Members.Where(m => m.IsActive);
                filterNames.Add("aktive Mitglieder");
            } else if (subject == ExportSubject.FromFilters) {
                var (f, q, _) = await vm.GetFilters(ctx);
                query = q;
                filterNames.AddRange(f);
            } else if (subject == ExportSubject.Selected) {
                var mgnr = vm.SelectedMember?.MgNr;
                if (mgnr == null) return;
                query = ctx.Members.Where(m => m.MgNr == mgnr);
                filterNames.Add($"{mgnr}");
            } else {
                throw new ArgumentException("Invalid value for ExportSubject");
            }

            if (vm.MemberListOrderByMgNr) {
                query = query
                    .OrderBy(m => m.Branch!.Name)
                    .ThenBy(m => m.MgNr);
            } else if (vm.MemberListOrderByName) {
                query = query
                    .OrderBy(m => m.Branch!.Name)
                    .ThenBy(m => m.Name)
                    .ThenBy(m => m.GivenName)
                    .ThenBy(m => m.MgNr);
            } else if (vm.MemberListOrderByOrt) {
                query = query
                    .OrderBy(m => m.Branch!.Name)
                    .ThenBy(m => m.DefaultWbKg!.AtKg.Name)
                    .ThenBy(m => m.Name)
                    .ThenBy(m => m.GivenName)
                    .ThenBy(m => m.MgNr);
            }

            if (mode == ExportMode.SaveList) {
                var d = new SaveFileDialog() {
                    FileName = $"{MemberList.Name}.ods",
                    DefaultExt = "ods",
                    Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods",
                    Title = $"{MemberList.Name} speichern unter - Elwig"
                };
                if (d.ShowDialog() == true) {
                    Mouse.OverrideCursor = Cursors.Wait;
                    await Task.Run(async () => {
                        try {
                            var data = await MemberListData.FromQuery(query, filterNames, filterNames.Where(f => f.StartsWith("Flächenbindung")).Select(f => f.Split(' ')[^1]));
                            using var ods = new OdsFile(d.FileName);
                            await ods.AddTable(data);
                        } catch (Exception exc) {
                            MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
                        }
                    });
                    Mouse.OverrideCursor = null;
                }
            } else if (mode == ExportMode.Export) {
                var d = new SaveFileDialog() {
                    FileName = subject == ExportSubject.Selected ? $"Mitglied_{vm.SelectedMember?.MgNr}.elwig.zip" : $"Mitglieder_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}.elwig.zip",
                    DefaultExt = ".elwig.zip",
                    Filter = "Elwig-Export-Datei (*.elwig.zip)|*.elwig.zip",
                    Title = $"{MemberList.Name} speichern unter - Elwig"
                };
                if (d.ShowDialog() == true) {
                    Mouse.OverrideCursor = Cursors.Wait;
                    await Task.Run(async () => {
                        try {
                            var members = await query
                                .OrderBy(m => m.MgNr)
                                .Include(m => m.BillingAddress)
                                .Include(m => m.TelephoneNumbers)
                                .Include(m => m.EmailAddresses)
                                .AsSplitQuery()
                                .ToListAsync();
                            var areaComs = await query
                                .SelectMany(m => m.AreaCommitments)
                                .Include(c => c.Rd)
                                .ToListAsync();
                            await ElwigData.Export(d.FileName, members, areaComs, filterNames);
                        } catch (Exception exc) {
                            MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
                        }
                    });
                    Mouse.OverrideCursor = null;
                }
            } else if (mode == ExportMode.Upload && App.Config.SyncUrl != null) {
                Mouse.OverrideCursor = Cursors.Wait;
                await Task.Run(async () => {
                    try {
                        var filename = $"{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}.elwig.zip";
                        var path = Path.Combine(App.TempPath, filename);
                        var members = await query
                            .OrderBy(m => m.MgNr)
                            .Include(m => m.BillingAddress)
                            .Include(m => m.TelephoneNumbers)
                            .Include(m => m.EmailAddresses)
                            .AsSplitQuery()
                            .ToListAsync();
                        var areaComs = await query
                            .SelectMany(m => m.AreaCommitments)
                            .Include(c => c.Rd)
                            .ToListAsync();
                        if (members.Count == 0) {
                            MessageBox.Show("Es wurden keine Mitglieder zum Hochladen ausgewählt!", "Mitglieder hochladen",
                                MessageBoxButton.OK, MessageBoxImage.Error);
                        } else {
                            await ElwigData.Export(path, members, areaComs, filterNames);
                            await Utils.UploadExportData(path, App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
                            MessageBox.Show($"Hochladen von {members.Count:N0} Mitgliedern erfolgreich!", "Mitglieder hochgeladen",
                                MessageBoxButton.OK, MessageBoxImage.Information);
                        }
                    } catch (HttpRequestException exc) {
                        MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Mitglieder hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
                    } catch (TaskCanceledException exc) {
                        MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Mitglieder hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
                    } catch (Exception exc) {
                        MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
                    }
                });
                Mouse.OverrideCursor = null;
            } else {
                Mouse.OverrideCursor = Cursors.Wait;
                await Task.Run(async () => {
                    try {
                        var data = await MemberListData.FromQuery(query, filterNames, filterNames.Where(f => f.StartsWith("Flächenbindung")).Select(f => f.Split(' ')[^1]));
                        using var doc = new MemberList(string.Join(" / ", filterNames), data);
                        await Utils.ExportDocument(doc, mode);
                    } catch (Exception exc) {
                        MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
                    }
                });
                Mouse.OverrideCursor = null;
            }
        }

        public static async Task<int> UpdateMember(this MemberAdminViewModel vm, int? oldMgNr) {
            var newMgNr = (int)vm.MgNr!;
            var m = new Member {
                MgNr = oldMgNr ?? newMgNr,
                PredecessorMgNr = vm.PredecessorMgNr,
                IsJuridicalPerson = vm.IsJuridicalPerson,
                Prefix = vm.IsJuridicalPerson || string.IsNullOrWhiteSpace(vm.Prefix) ? null : vm.Prefix,
                GivenName = vm.IsJuridicalPerson || string.IsNullOrWhiteSpace(vm.GivenName) ? null : vm.GivenName,
                Name = vm.Name!,
                Suffix = vm.IsJuridicalPerson || string.IsNullOrWhiteSpace(vm.Suffix) ? null : vm.Suffix,
                ForTheAttentionOf = !vm.IsJuridicalPerson || string.IsNullOrWhiteSpace(vm.ForTheAttentionOf) ? null : vm.ForTheAttentionOf,
                Birthday = string.IsNullOrEmpty(vm.Birthday) ? null : string.Join("-", vm.Birthday!.Split(".").Reverse()),
                IsDeceased = vm.IsDeceased,
                CountryNum = 40,  // Austria AT AUT
                PostalDestId = vm.Ort!.Id,
                Address = vm.Address!,

                Iban = string.IsNullOrEmpty(vm.Iban) ? null : vm.Iban?.Replace(" ", ""),
                Bic = string.IsNullOrEmpty(vm.Bic) ? null : vm.Bic,

                UstIdNr = string.IsNullOrEmpty(vm.UstIdNr) ? null : vm.UstIdNr,
                LfbisNr = string.IsNullOrEmpty(vm.LfbisNr) ? null : vm.LfbisNr,
                IsBuchführend = vm.IsBuchführend,
                IsOrganic = vm.IsOrganic,

                EntryDateString = string.IsNullOrEmpty(vm.EntryDate) ? null : string.Join("-", vm.EntryDate.Split(".").Reverse()),
                ExitDateString = string.IsNullOrEmpty(vm.ExitDate) ? null : string.Join("-", vm.ExitDate.Split(".").Reverse()),
                BusinessShares = (int)vm.BusinessShares!,
                AccountingNr = string.IsNullOrEmpty(vm.AccountingNr) ? null : vm.AccountingNr,
                IsActive = vm.IsActive,
                IsVollLieferant = vm.IsVollLieferant,
                IsFunktionär = vm.IsFunktionär,
                ZwstId = vm.Branch?.ZwstId,
                DefaultKgNr = vm.DefaultKg?.KgNr,
                Comment = string.IsNullOrEmpty(vm.Comment) ? null : vm.Comment,
                ContactViaPost = vm.ContactViaPost,
                ContactViaEmail = vm.ContactViaEmail,
            };

            await Task.Run(async () => {
                using var ctx = new AppDbContext();
                if (oldMgNr != null) {
                    ctx.Update(m);
                } else {
                    ctx.Add(m);
                }

                ctx.RemoveRange(ctx.BillingAddresses.Where(a => a.MgNr == oldMgNr));
                if (vm.BillingOrt != null && vm.BillingName != null) {
                    var p = vm.BillingOrt;
                    ctx.Add(new BillingAddr {
                        MgNr = m.MgNr,
                        FullName = vm.BillingName,
                        Address = vm.BillingAddress ?? "",
                        CountryNum = p.CountryNum,
                        PostalDestId = p.Id,
                    });
                }

                ctx.RemoveRange(ctx.MemberTelephoneNrs.Where(t => t.MgNr == oldMgNr));
                ctx.AddRange(vm.PhoneNrs
                    .Where(input => input.Number != null && input.Number != "")
                    .Select((input, i) => new MemberTelNr {
                        MgNr = m.MgNr,
                        Nr = i + 1,
                        Type = input.Type == -1 ? (input.Number!.StartsWith("+43 ") && input.Number![4] == '6' ? "mobile" : "landline") : vm.PhoneNrTypes[input.Type].Key,
                        Number = input.Number!,
                        Comment = input.Comment,
                    }));

                ctx.RemoveRange(ctx.MemberEmailAddrs.Where(e => e.MgNr == oldMgNr));
                ctx.AddRange(vm.EmailAddresses
                    .Where(input => input != null && input != "")
                    .Select((input, i) => new MemberEmailAddr {
                        MgNr = m.MgNr,
                        Nr = i + 1,
                        Address = input!,
                        Comment = null,
                    }));

                await ctx.SaveChangesAsync();

                if (vm.TransferPredecessorAreaComs is int year && m.PredecessorMgNr is int predecessor) {
                    var areaComs = await ctx.AreaCommitments
                        .Where(c => c.MgNr == predecessor && (c.YearTo == null || c.YearTo >= year))
                        .ToListAsync();

                    var fbNr = await ctx.NextFbNr();
                    ctx.AddRange(areaComs.Select((c, i) => new AreaCom {
                        FbNr = fbNr + i,
                        MgNr = m.MgNr,
                        VtrgId = c.VtrgId,
                        CultId = c.CultId,
                        Area = c.Area,
                        KgNr = c.KgNr,
                        GstNr = c.GstNr,
                        RdNr = c.RdNr,
                        YearFrom = vm.MaintainAreaComYearTo ? c.YearFrom : year,
                        YearTo = c.YearTo,
                    }));

                    foreach (var ac in areaComs)
                        ac.YearTo = year - 1;
                    ctx.UpdateRange(areaComs);
                    await ctx.SaveChangesAsync();
                }
                vm.TransferPredecessorAreaComs = null;

                if (vm.CancelAreaComs is int yearTo) {
                    var areaComs = await ctx.AreaCommitments
                        .Where(c => c.MgNr == m.MgNr && (c.YearTo == null || c.YearTo > yearTo))
                        .ToListAsync();

                    foreach (var ac in areaComs)
                        ac.YearTo = yearTo;
                    ctx.UpdateRange(areaComs);
                    await ctx.SaveChangesAsync();
                }
                vm.CancelAreaComs = null;

                if (newMgNr != m.MgNr) {
                    await ctx.Database.ExecuteSqlAsync($"UPDATE member SET mgnr = {newMgNr} WHERE mgnr = {oldMgNr}");
                }
            });

            App.HintContextChange();

            return newMgNr;
        }

        public static async Task DeleteMember(int mgnr, bool deletePaymentData, bool deleteDeliveries, bool deleteAreaComs) {
            await Task.Run(async () => {
                using var ctx = new AppDbContext();
                var l = (await ctx.Members.FindAsync(mgnr))!;
                if (deletePaymentData) {
                    ctx.RemoveRange(l.Credits);
                }
                if (deleteDeliveries) {
                    ctx.RemoveRange(l.Deliveries);
                }
                if (deleteAreaComs) {
                    ctx.RemoveRange(l.AreaCommitments);
                }
                ctx.Remove(l);
                await ctx.SaveChangesAsync();
            });

            App.HintContextChange();
        }
    }
}