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

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

        public DeliveryAncmtAdminViewModel ViewModel => (DeliveryAncmtAdminViewModel)DataContext;

        private readonly RoutedCommand CtrlF = new("CtrlF", typeof(DeliveryAncmtAdminWindow), [new KeyGesture(Key.F, ModifierKeys.Control)]);
        private readonly RoutedCommand CtrlP = new("CtrlP", typeof(DeliveryAncmtAdminWindow), [new KeyGesture(Key.P, ModifierKeys.Control)]);
        private readonly RoutedCommand CtrlShiftP = new("CtrlShiftP", typeof(DeliveryAncmtAdminWindow), [new KeyGesture(Key.P, ModifierKeys.Control | ModifierKeys.Shift)]);

        public DeliveryAncmtAdminWindow() {
            InitializeComponent();
            CommandBindings.Add(new CommandBinding(CtrlF, FocusSearchInput));
            CommandBindings.Add(new CommandBinding(CtrlP, Menu_DeliveryAncmtList_ShowSelected_Click));
            CommandBindings.Add(new CommandBinding(CtrlShiftP, Menu_DeliveryAncmtList_PrintSelected_Click));
            ExemptInputs = [
                SearchInput, SeasonInput, OnlyUpcomingInput, FromAllSchedulesInput, DeliveryScheduleList, DeliveryAncmtList,
            ];
            RequiredInputs = [
                MgNrInput, MemberInput, DeliveryScheduleInput, SortIdInput, WineVarietyInput, WeightInput,
            ];

            DoShowWarningWindows = false;

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

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

        private void Input_KeyUp(object sender, KeyEventArgs evt) {
            if (sender is not Control ctrl) return;
            if (evt.Key != Key.Enter) return;
            if (ctrl == MgNrInput || ctrl == MemberInput) {
                SortIdInput.Focus();
                SortIdInput.SelectAll();
            } else if (ctrl == SortIdInput || ctrl == WineVarietyInput) {
                WeightInput.Focus();
                WeightInput.SelectAll();
            } else if (ctrl == WeightInput) {
                ShortcutSave();
            }
        }

        private async void Menu_DeliveryAncmtList_SaveSelected_Click(object sender, RoutedEventArgs evt) {
            if (DeliveryScheduleList.SelectedItem is not DeliverySchedule s)
                return;
            await ViewModel.GenerateDeliveryAncmtList(DeliveryAncmtService.ExportSubject.FromSelectedSchedule, ExportMode.SaveList);
        }

        private async void Menu_DeliveryAncmtList_ShowSelected_Click(object sender, RoutedEventArgs evt) {
            if (DeliveryScheduleList.SelectedItem is not DeliverySchedule s)
                return;
            await ViewModel.GenerateDeliveryAncmtList(DeliveryAncmtService.ExportSubject.FromSelectedSchedule, ExportMode.Show);
        }

        private async void Menu_DeliveryAncmtList_SavePdfSelected_Click(object sender, RoutedEventArgs evt) {
            if (DeliveryScheduleList.SelectedItem is not DeliverySchedule s)
                return;
            await ViewModel.GenerateDeliveryAncmtList(DeliveryAncmtService.ExportSubject.FromSelectedSchedule, ExportMode.SavePdf);
        }

        private async void Menu_DeliveryAncmtList_PrintSelected_Click(object sender, RoutedEventArgs evt) {
            if (DeliveryScheduleList.SelectedItem is not DeliverySchedule s)
                return;
            await ViewModel.GenerateDeliveryAncmtList(DeliveryAncmtService.ExportSubject.FromSelectedSchedule, ExportMode.Print);
        }

        private async Task RefreshDeliveryScheduleList() {
            using var ctx = new AppDbContext();
            var list = await ctx.DeliverySchedules
                .Where(s => s.Year == ViewModel.FilterSeason)
                .Include(s => s.Branch)
                .OrderBy(s => s.DateString)
                .ThenBy(s => s.Branch.Name)
                .ThenBy(s => s.Description)
                .Select(s => new {
                    Schedule = s,
                    AnnouncedWeight = s.Announcements.Sum(a => a.Weight)
                })
                .ToListAsync();
            list.ForEach(v => v.Schedule.AnnouncedWeightOverride = v.AnnouncedWeight);
            var deliverySchedules = list.Select(v => v.Schedule).ToList();
            ControlUtils.RenewItemsSource(DeliveryScheduleList, deliverySchedules
                .Where(s => !ViewModel.FilterOnlyUpcoming || s.DateString.CompareTo(Utils.Today.ToString("yyyy-MM-dd")) >= 0)
                .ToList(), DeliveryScheduleList_SelectionChanged, ViewModel.FilterFromAllSchedules ? ControlUtils.RenewSourceDefault.None : ControlUtils.RenewSourceDefault.First);
            ControlUtils.RenewItemsSource(DeliveryScheduleInput, deliverySchedules, DeliveryScheduleInput_SelectionChanged);
        }

        private async Task RefreshList(bool updateSort = false) {
            using var ctx = new AppDbContext();
            var (_, deliveryAncmtQuery, filter) = await ViewModel.GetFilters(ctx);
            var deliveryAncmts = await deliveryAncmtQuery
                .Include(a => a.Member.BillingAddress)
                .Include(a => a.Schedule)
                .Include(a => a.Variety)
                .AsSplitQuery()
                .ToListAsync();

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

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

            ViewModel.StatusAncmts = $"{deliveryAncmts.Count:N0}";
            if (filter.Count == 0) {
                var (text, grid) = await DeliveryAncmtService.GenerateToolTip(deliveryAncmtQuery);
                ViewModel.StatusWeight = text;
                ViewModel.StatusWeightToolTip = grid;
            } else {
                ViewModel.StatusWeight = $"{deliveryAncmts.Sum(a => a.Weight):N0} kg";
                ViewModel.StatusWeightToolTip = null;
            }
        }

        private void RefreshInputs(bool validate = false) {
            ClearInputStates();
            if (ViewModel.SelectedDeliveryAncmt is DeliveryAncmt a) {
                EditDeliveryAncmtButton.IsEnabled = true;
                DeleteDeliveryAncmtButton.IsEnabled = true;
                FillInputs(a);
            } else {
                EditDeliveryAncmtButton.IsEnabled = false;
                DeleteDeliveryAncmtButton.IsEnabled = false;
                ClearOriginalValues();
                ClearDefaultValues();
                ClearInputs(validate);
                ClearInputStates();
            }
            GC.Collect();
        }

        private void InitInputs() {
            ClearOriginalValues();
            ClearDefaultValues();
            ViewModel.InitInputs();
            ValidateRequiredInputs();
        }

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

            ControlUtils.RenewItemsSource(MemberInput, await ctx.Members
                .Where(m => m.IsActive || !IsCreating)
                .Include(m => m.PostalDest.AtPlz!.Ort)
                .Include(m => m.DefaultWbKg!.AtKg)
                .OrderBy(m => m.Name)
                .ThenBy(m => m.GivenName)
                .ThenBy(m => m.MgNr)
                .ToListAsync());
            ControlUtils.RenewItemsSource(WineVarietyInput, await ctx.WineVarieties.OrderBy(v => v.Name).ToListAsync());

            await RefreshDeliveryScheduleList();
            await RefreshList();
        }

        private void DeliveryAncmtList_SelectionChanged(object sender, RoutedEventArgs evt) {
            RefreshInputs();
        }

        private async void DeliveryScheduleList_SelectionChanged(object sender, RoutedEventArgs evt) {
            await RefreshList();
            if (DeliveryScheduleList.SelectedItem is DeliverySchedule s) {
                Menu_DeliveryAncmtList_SaveSelected.IsEnabled = !IsEditing && !IsCreating;
                Menu_DeliveryAncmtList_ShowSelected.IsEnabled = !IsEditing && !IsCreating;
                Menu_DeliveryAncmtList_SavePdfSelected.IsEnabled = !IsEditing && !IsCreating;
                Menu_DeliveryAncmtList_PrintSelected.IsEnabled = !IsEditing && !IsCreating;
                ViewModel.FilterFromAllSchedules = false;
            } else {
                Menu_DeliveryAncmtList_SaveSelected.IsEnabled = false;
                Menu_DeliveryAncmtList_ShowSelected.IsEnabled = false;
                Menu_DeliveryAncmtList_SavePdfSelected.IsEnabled = false;
                Menu_DeliveryAncmtList_PrintSelected.IsEnabled = false;
                ViewModel.FilterFromAllSchedules = true;
            }
        }

        private void DeliveryScheduleInput_SelectionChanged(object sender, RoutedEventArgs evt) {

        }

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

        private async void FromAllSchedulesInput_Changed(object sender, RoutedEventArgs evt) {
            if (ViewModel.FilterFromAllSchedules) {
                DeliveryScheduleList.SelectedItem = null;
            } else if (DeliveryScheduleList.SelectedItem == null) {
                ViewModel.FilterFromAllSchedules = true;
            }
            await RefreshList(true);
        }

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

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

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

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

        private void MemberInput_SelectionChanged(object? sender, SelectionChangedEventArgs? evt) {
            var m = MemberInput.SelectedItem as Member;
            ViewModel.MgNr = m?.MgNr;
            ViewModel.MemberAddress = m?.FullAddress;
        }

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

        private async void NewDeliveryAncmtButton_Click(object? sender, RoutedEventArgs? evt) {
            IsCreating = true;
            DeliveryAncmtList.IsEnabled = false;
            var mgnr = ViewModel.MgNr;
            ViewModel.SelectedDeliveryAncmt = null;

            using var ctx = new AppDbContext();
            ControlUtils.RenewItemsSource(MemberInput, await ctx.Members
                .Where(m => m.IsActive || !IsCreating)
                .Include(m => m.PostalDest.AtPlz!.Ort)
                .Include(m => m.DefaultWbKg!.AtKg)
                .OrderBy(m => m.Name)
                .ThenBy(m => m.GivenName)
                .ThenBy(m => m.MgNr)
                .ToListAsync());

            HideNewEditDeleteButtons();
            ShowSaveResetCancelButtons();
            UnlockInputs();
            InitInputs();
            ViewModel.MgNr = mgnr;
            ViewModel.EnableSearchInputs = false;

            MgNrInput.Focus();
            MgNrInput.SelectAll();
        }

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

        private void EditDeliveryAncmtButton_Click(object? sender, RoutedEventArgs? evt) {
            if (ViewModel.SelectedDeliveryAncmt == null) return;
            IsEditing = true;
            DeliveryAncmtList.IsEnabled = false;
            HideNewEditDeleteButtons();
            ShowSaveResetCancelButtons();
            UnlockInputs();
            ViewModel.EnableSearchInputs = false;
        }

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

        private async void DeleteDeliveryAncmtButton_Click(object? sender, RoutedEventArgs? evt) {
            if (ViewModel.SelectedDeliveryAncmt is not DeliveryAncmt a)
                return;
            var r = MessageBox.Show(
                $"Soll die Anmeldung wirklich unwiderruflich gelöscht werden?",
                "Anmeldung löschen", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
            if (r == MessageBoxResult.OK) {
                Mouse.OverrideCursor = Cursors.Wait;
                try {
                    await Task.Run(async () => {
                        using var ctx = new AppDbContext();
                        ctx.Remove(a);
                        await ctx.SaveChangesAsync();
                    });
                    App.HintContextChange();
                } catch (Exception exc) {
                    var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
                    if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
                    MessageBox.Show(str, "Anmeldung 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.Wait;
            SaveButton.IsEnabled = false;

            int year = -1, dsnr = -1, mgnr = -1;
            string? sortid = null;
            try {
                var s = ViewModel.SelectedDeliveryAncmt;
                (year, dsnr, mgnr, sortid) = await ViewModel.UpdateDeliveryAncmt(s?.Year, s?.DsNr, s?.MgNr, s?.SortId, s?.Type);
                App.HintContextChange();
            } catch (Exception exc) {
                var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
                if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
                MessageBox.Show(str, "Anmeldung aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error);
                SaveButton.IsEnabled = true;
                return;
            } finally {
                Mouse.OverrideCursor = null;
            }

            IsEditing = false;
            IsCreating = false;
            DeliveryAncmtList.IsEnabled = true;
            HideSaveResetCancelButtons();
            ShowNewEditDeleteButtons();
            LockInputs();
            ViewModel.EnableSearchInputs = true;
            FinishInputFilling();
            await RefreshList();
            RefreshInputs();
            ViewModel.SearchQuery = "";
            ControlUtils.SelectItemWithPk(DeliveryScheduleList, year, dsnr);
            if (sortid != null)
                ControlUtils.SelectItemWithPk(DeliveryAncmtList, year, dsnr, mgnr, sortid);
        }

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

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

        private async void CancelButton_Click(object? sender, RoutedEventArgs? evt) {
            IsEditing = false;
            IsCreating = false;
            DeliveryAncmtList.IsEnabled = true;

            using var ctx = new AppDbContext();
            ControlUtils.RenewItemsSource(MemberInput, await ctx.Members
                .Where(m => m.IsActive || !IsCreating)
                .Include(m => m.PostalDest.AtPlz!.Ort)
                .Include(m => m.DefaultWbKg!.AtKg)
                .OrderBy(m => m.Name)
                .ThenBy(m => m.GivenName)
                .ThenBy(m => m.MgNr)
                .ToListAsync());

            HideSaveResetCancelButtons();
            ShowNewEditDeleteButtons();
            RefreshInputs();
            LockInputs();
            ViewModel.EnableSearchInputs = true;
        }

        private void FillInputs(DeliveryAncmt a) {
            ClearOriginalValues();
            ClearDefaultValues();
            ViewModel.FillInputs(a);
            FinishInputFilling();
        }

        private void UpdateWineVariety(bool valid) {
            if (valid) {
                ViewModel.SortId = ViewModel.SortId![0..2];
                ControlUtils.SelectItemWithPk(WineVarietyInput, ViewModel.SortId);
            } else {
                WineVarietyInput.SelectedItem = null;
            }
        }

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

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

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

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

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

        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() {
            NewDeliveryAncmtButton.IsEnabled = true;
            EditDeliveryAncmtButton.IsEnabled = ViewModel.SelectedDeliveryAncmt != null;
            DeleteDeliveryAncmtButton.IsEnabled = ViewModel.SelectedDeliveryAncmt != null;
            ViewModel.ControlButtonsVisibility = Visibility.Visible;
        }

        private void HideNewEditDeleteButtons() {
            NewDeliveryAncmtButton.IsEnabled = false;
            EditDeliveryAncmtButton.IsEnabled = false;
            DeleteDeliveryAncmtButton.IsEnabled = false;
            ViewModel.ControlButtonsVisibility = Visibility.Hidden;
        }

        private void WeightInput_TextChanged(object sender, TextChangedEventArgs evt) {
            InputTextChanged((TextBox)sender, Validator.CheckInteger((TextBox)sender, false, 5));
        }

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

        private void DeliveryScheduleButton_Click(object sender, RoutedEventArgs evt) {
            App.FocusDeliverySchedule();
        }
    }
}
