using Elwig.Helpers;
using Elwig.Helpers.Billing;
using Elwig.Helpers.Export;
using Elwig.Models.Dtos;
using Elwig.Models.Entities;
using Elwig.Services;
using Elwig.ViewModels;
using Microsoft.EntityFrameworkCore;
using Microsoft.Win32;
using System;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace Elwig.Windows {
    public partial class PaymentVariantsWindow : ContextWindow {

        public PaymentVariantsViewModel ViewModel => (PaymentVariantsViewModel)DataContext;

        public readonly int Year;
        private bool DataValid, DataChanged, NameChanged, CommentChanged, DateValid, DateChanged, TransferDateValid, TransferDateChanged;

        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 CtrlÜ = new("CtrlÜ", typeof(MemberAdminWindow), [new KeyGesture(Key.Oem1, ModifierKeys.Control)]);
        private readonly RoutedCommand CtrlShiftP = new("CtrlShiftP", typeof(MemberAdminWindow), [new KeyGesture(Key.P, ModifierKeys.Control | ModifierKeys.Shift)]);

        public PaymentVariantsWindow(int year) {
            InitializeComponent();
            CommandBindings.Add(new CommandBinding(CtrlL, Menu_ExportSave_Click));
            CommandBindings.Add(new CommandBinding(CtrlP, Menu_SummaryShow_Click));
            CommandBindings.Add(new CommandBinding(CtrlÜ, Menu_EbicsSave_Click));
            CommandBindings.Add(new CommandBinding(CtrlShiftP, Menu_SummaryPrint_Click));
            Year = year;
            using (var ctx = new AppDbContext()) {
                ViewModel.SeasonLocked = ctx.Seasons.Find(Year + 1) != null;
            }
            Title = $"Auszahlungsvarianten - Lese {Year} - Elwig";
            if (!App.Config.Debug) {
                DataInput.Visibility = Visibility.Hidden;
            }
            if (App.Client.IsMatzen) {
                ConsiderCustomInput.Content = ConsiderCustomInput.Content.ToString()?.Replace("Benutzerdefinierte", "Bentz.def.").Replace("Mitglied", "Mg.") + " (inkl. Treuebonus)";
            } else {
                MatzenNote.Visibility = Visibility.Hidden;
            }
        }

        protected override async Task OnRenewContext(AppDbContext ctx) {
            ControlUtils.RenewItemsSource(PaymentVariantList, await ctx.PaymentVariants
                .Where(v => v.Year == Year)
                .OrderBy(v => v.AvNr)
                .Include(v => v.Season.Currency)
                .ToListAsync());
            if (PaymentVariantList.SelectedItem == null && PaymentVariantList.Items.Count > 0) {
                PaymentVariantList.SelectedIndex = PaymentVariantList.Items.Count - 1;
            }
            Update();
        }

        private void Update() {
            if (PaymentVariantList.SelectedItem is PaymentVar v) {
                ViewModel.FillInputs(v);
            } else {
                ViewModel.ClearInputs();
            }
            UpdateButtons();
        }

        private void UpdateButtons() {
            ViewModel.SaveIsEnabled = PaymentVariantList.SelectedItem != null &&
                ((DataChanged && DataValid) || NameChanged || CommentChanged ||
                (DateChanged && DateValid) ||
                (TransferDateChanged && TransferDateValid) ||
                (ViewModel.ConsiderModifiers != ViewModel.BillingData?.ConsiderDelieryModifiers) ||
                (ViewModel.ConsiderPenalties != ViewModel.BillingData?.ConsiderContractPenalties) ||
                (ViewModel.ConsiderPenalty != ViewModel.BillingData?.ConsiderTotalPenalty) ||
                (ViewModel.ConsiderAuto != ViewModel.BillingData?.ConsiderAutoBusinessShares) ||
                (ViewModel.ConsiderCustom != ViewModel.BillingData?.ConsiderCustomModifiers) ||
                ViewModel.WeightModifierChanged);
            ViewModel.CalculateIsEnabled = !ViewModel.SaveIsEnabled && PaymentVariantList.SelectedItem is PaymentVar { TestVariant: true };
            ViewModel.CommitIsEnabled = ViewModel.CalculateIsEnabled;
        }

        private void PaymentVariantList_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
            Update();
        }

        private async void AddButton_Click(object sender, RoutedEventArgs evt) {
            Mouse.OverrideCursor = Cursors.Wait;
            try {
                var v = await PaymentVariantService.CreatePaymentVariant(Year);
                App.HintContextChange();
                ControlUtils.SelectItem(PaymentVariantList, v);
            } 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, "Auszahlungsvariante erstellen", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            Mouse.OverrideCursor = null;
        }

        private async void CopyButton_Click(object sender, RoutedEventArgs evt) {
            if (PaymentVariantList.SelectedItem is not PaymentVar orig)
                return;
            Mouse.OverrideCursor = Cursors.Wait;
            try {
                PaymentVar n = await orig.Duplicate();
                App.HintContextChange();
                ControlUtils.SelectItem(PaymentVariantList, n);
            } 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, "Auszahlungsvariante kopieren", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            Mouse.OverrideCursor = null;
        }

        private async void DeleteButton_Click(object sender, RoutedEventArgs evt) {
            if (PaymentVariantList.SelectedItem is not PaymentVar v || !v.TestVariant)
                return;
            var res = MessageBox.Show(
                $"Soll die Auszahlungsvariante \"{v.Name}\" wirklich unwiderruflich gelöscht werden?",
                "Auszahlungsvariante löschen", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
            if (res != MessageBoxResult.OK)
                return;
            Mouse.OverrideCursor = Cursors.Wait;
            try {
                await PaymentVariantService.DeletePaymentVariant(v.Year, v.AvNr);
                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, "Auszahlungsvariante löschen", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            Mouse.OverrideCursor = null;
        }

        private async void CalculateButton_Click(object sender, RoutedEventArgs evt) {
            if (PaymentVariantList.SelectedItem is not PaymentVar v)
                return;
            ViewModel.CalculateIsEnabled = false;
            Mouse.OverrideCursor = Cursors.Wait;
            try {
                await PaymentVariantService.Calculate(v.Year, v.AvNr);
                App.HintContextChange();
            } catch (Exception exc) {
                MessageBox.Show(exc.Message, "Berechnungsfehler", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            Mouse.OverrideCursor = null;
            ViewModel.CalculateIsEnabled = true;
        }

        private void EditButton_Click(object sender, RoutedEventArgs evt) {
            if (PaymentVariantList.SelectedItem is not PaymentVar v)
                return;
            App.FocusChartWindow(v.Year, v.AvNr);
        }

        private void PaymentAdjustmentButton_Click(object sender, RoutedEventArgs evt) {
            App.FocusPaymentAdjustment(Year);
        }

        private async void MailButton_Click(object sender, RoutedEventArgs evt) {
            if (PaymentVariantList.SelectedItem is not PaymentVar pv)
                return;
            using var ctx = new AppDbContext();
            var vars = await ctx.PaymentVariants
                .Where(v => pv.Year == v.Year)
                .OrderBy(v => v.AvNr)
                .Select(v => v.AvNr)
                .ToArrayAsync();
            var w = App.FocusMailWindow(pv.Year);
            w.AddCreditNote(Array.IndexOf(vars, pv.AvNr));
        }

        private async void Menu_SummaryExport_Click(object sender, RoutedEventArgs evt) {
            if (PaymentVariantList.SelectedItem is not PaymentVar v)
                return;
            await PaymentVariantService.GenerateSummary(v, ExportMode.SaveList);
        }

        private async void Menu_SummaryShow_Click(object sender, RoutedEventArgs evt) {
            if (PaymentVariantList.SelectedItem is not PaymentVar v)
                return;
            await PaymentVariantService.GenerateSummary(v, ExportMode.Show);
        }

        private async void Menu_SummarySave_Click(object sender, RoutedEventArgs evt) {
            if (PaymentVariantList.SelectedItem is not PaymentVar v)
                return;
            await PaymentVariantService.GenerateSummary(v, ExportMode.SavePdf);
        }

        private async void Menu_SummaryPrint_Click(object sender, RoutedEventArgs evt) {
            if (PaymentVariantList.SelectedItem is not PaymentVar v)
                return;
            await PaymentVariantService.GenerateSummary(v, ExportMode.Print);
        }

        private async void CommitButton_Click(object sender, RoutedEventArgs evt) {
            if (PaymentVariantList.SelectedItem is not PaymentVar v)
                return;
            ViewModel.CommitIsEnabled = false;
            Mouse.OverrideCursor = Cursors.Wait;
            try {
                await PaymentVariantService.Commit(v.Year, v.AvNr);
                App.HintContextChange();
            } catch (Exception exc) {
                MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            Mouse.OverrideCursor = null;
            ViewModel.RevertIsEnabled = true;
        }

        private async void RevertButton_Click(object sender, RoutedEventArgs evt) {
            if (PaymentVariantList.SelectedItem is not PaymentVar v)
                return;
            var res = MessageBox.Show(
                "Sollen wirklich alle festgesetzten Traubengutschriften der ausgewählten Auszahlungsvariante unwiderruflich gelöscht werden?\n\n" +
                "Dies ist im Allgemeinen nie empfohlen. Handelt es sich um die aktuellste Auszahlungsvariante könnte das eine Ausnahme sein.",
                "Traubengutschriften löschen", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
            if (res != MessageBoxResult.Yes)
                return;
            ViewModel.RevertIsEnabled = false;
            Mouse.OverrideCursor = Cursors.Wait;
            try {
                await PaymentVariantService.Revert(v.Year, v.AvNr);
                App.HintContextChange();
            } catch (Exception exc) {
                MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            Mouse.OverrideCursor = null;
            ViewModel.CommitIsEnabled = true;
        }

        private async void Menu_EbicsSave_Click(object sender, RoutedEventArgs evt) {
            if (PaymentVariantList.SelectedItem is not PaymentVar v) {
                return;
            } else if (v.TransferDate == null) {
                MessageBox.Show("Überweisungsdatum muss gesetzt sein!", "Exportieren nicht möglich", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }
            await PaymentVariantService.GenerateEbics(v.Year, v.AvNr);
        }

        private async void Menu_ExportSave_Click(object sender, RoutedEventArgs evt) {
            if (PaymentVariantList.SelectedItem is not PaymentVar v)
                return;
            await PaymentVariantService.GenerateAccountingList(v.Year, v.AvNr, v.Name);
        }

        private async void SaveButton_Click(object sender, RoutedEventArgs evt) {
            if (PaymentVariantList.SelectedItem is not PaymentVar v || ViewModel.BillingData == null)
                return;
            Mouse.OverrideCursor = Cursors.Wait;
            try {
                await ViewModel.UpdatePaymentVariant(v.Year, v.AvNr);
                App.HintContextChange();
            } catch (Exception exc) {
                await HintContextChange();
                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, "Auszahlungsvariante aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            Mouse.OverrideCursor = null;
            await HintContextChange();
            CommentInput_TextChanged(null, null);
            DateInput_TextChanged(null, null);
            TransferDateInput_TextChanged(null, null);
            ConsiderModifiersInput_Changed(null, null);
            ConsiderPenaltiesInput_Changed(null, null);
            ConsiderPenaltyInput_Changed(null, null);
            ConsiderAutoInput_Changed(null, null);
            ConsiderCustomInput_Changed(null, null);
            WeightModifierInput_TextChanged(null, null);
        }

        private void ModifierButton_Click(object sender, RoutedEventArgs evt) {
            App.FocusBaseDataSeason(Year);
        }

        private void NameInput_TextChanged(object sender, TextChangedEventArgs evt) {
            if (PaymentVariantList.SelectedItem is not PaymentVar v) {
                ControlUtils.ClearInputState(NameInput);
                return;
            }
            if (NameInput.Text != v.Name) {
                ControlUtils.SetInputChanged(NameInput);
                NameChanged = true;
            } else {
                ControlUtils.ClearInputState(NameInput);
                NameChanged = false;
            }
            UpdateButtons();
        }

        private void CommentInput_TextChanged(object? sender, TextChangedEventArgs? evt) {
            if (PaymentVariantList.SelectedItem is not PaymentVar v) {
                ControlUtils.ClearInputState(CommentInput);
                return;
            }
            if (CommentInput.Text != (v.Comment ?? "")) {
                ControlUtils.SetInputChanged(CommentInput);
                CommentChanged = true;
            } else {
                ControlUtils.ClearInputState(CommentInput);
                CommentChanged = false;
            }
            UpdateButtons();
        }

        private void DateInput_TextChanged(object? sender, TextChangedEventArgs? evt) {
            if (PaymentVariantList.SelectedItem is not PaymentVar v) {
                ControlUtils.ClearInputState(DateInput);
                return;
            }
            var res = Validator.CheckDate(DateInput, true);
            if (!res.IsValid) {
                ControlUtils.SetInputInvalid(DateInput);
                DateValid = false;
            } else if (DateInput.Text != $"{v.Date:dd.MM.yyyy}") {
                ControlUtils.SetInputChanged(DateInput);
                DateValid = true;
                DateChanged = true;
            } else {
                ControlUtils.ClearInputState(DateInput);
                DateValid = true;
                DateChanged = false;
            }
            UpdateButtons();
        }

        private void TransferDateInput_TextChanged(object? sender, TextChangedEventArgs? evt) {
            if (PaymentVariantList.SelectedItem is not PaymentVar v) {
                ControlUtils.ClearInputState(TransferDateInput);
                return;
            }
            var res = Validator.CheckDate(TransferDateInput, false);
            if (!res.IsValid) {
                ControlUtils.SetInputInvalid(TransferDateInput);
                TransferDateValid = false;
            } else if (TransferDateInput.Text != $"{v.TransferDate:dd.MM.yyyy}") {
                ControlUtils.SetInputChanged(TransferDateInput);
                TransferDateValid = true;
                TransferDateChanged = true;
            } else {
                ControlUtils.ClearInputState(TransferDateInput);
                TransferDateValid = true;
                TransferDateChanged = false;
            }
            UpdateButtons();
        }

        private void DataInput_TextChanged(object sender, TextChangedEventArgs evt) {
            if (PaymentVariantList.SelectedItem is not PaymentVar v) {
                ControlUtils.ClearInputState(DataInput);
                return;
            }
            try {
                var data = BillingData.FromJson(DataInput.Text);
                var origJson = v.Data;
                try {
                    origJson = JsonSerializer.Serialize(BillingData.FromJson(v.Data).Data);
                } catch { }
                DataValid = true;
                if (JsonSerializer.Serialize(data.Data) != origJson) {
                    ControlUtils.SetInputChanged(DataInput);
                    DataChanged = true;
                } else {
                    ControlUtils.ClearInputState(DataInput);
                    DataChanged = false;
                }
            } catch {
                ControlUtils.SetInputInvalid(DataInput);
                DataValid = false;
            }
            UpdateButtons();
        }

        private void ConsiderModifiersInput_Changed(object? sender, RoutedEventArgs? evt) {
            if (ViewModel.BillingData == null) {
                ControlUtils.ClearInputState(ConsiderModifiersInput);
                return;
            }
            if (ViewModel.BillingData.ConsiderDelieryModifiers != ConsiderModifiersInput.IsChecked) {
                ControlUtils.SetInputChanged(ConsiderModifiersInput);
            } else {
                ControlUtils.ClearInputState(ConsiderModifiersInput);
            }
            UpdateButtons();
        }

        private void ConsiderPenaltiesInput_Changed(object? sender, RoutedEventArgs? evt) {
            if (ViewModel.BillingData == null) {
                ControlUtils.ClearInputState(ConsiderPenaltiesInput);
                return;
            }
            if (ViewModel.BillingData.ConsiderContractPenalties != ConsiderPenaltiesInput.IsChecked) {
                ControlUtils.SetInputChanged(ConsiderPenaltiesInput);
            } else {
                ControlUtils.ClearInputState(ConsiderPenaltiesInput);
            }
            UpdateButtons();
        }

        private void ConsiderPenaltyInput_Changed(object? sender, RoutedEventArgs? evt) {
            if (ViewModel.BillingData == null) {
                ControlUtils.ClearInputState(ConsiderPenaltyInput);
                return;
            }
            if (ViewModel.BillingData.ConsiderTotalPenalty != ConsiderPenaltyInput.IsChecked) {
                ControlUtils.SetInputChanged(ConsiderPenaltyInput);
            } else {
                ControlUtils.ClearInputState(ConsiderPenaltyInput);
            }
            UpdateButtons();
        }

        private void ConsiderAutoInput_Changed(object? sender, RoutedEventArgs? evt) {
            if (ViewModel.BillingData == null) {
                ControlUtils.ClearInputState(ConsiderAutoInput);
                return;
            }
            if (ViewModel.BillingData.ConsiderAutoBusinessShares != ConsiderAutoInput.IsChecked) {
                ControlUtils.SetInputChanged(ConsiderAutoInput);
            } else {
                ControlUtils.ClearInputState(ConsiderAutoInput);
            }
            UpdateButtons();
        }

        private void ConsiderCustomInput_Changed(object? sender, RoutedEventArgs? evt) {
            if (ViewModel.BillingData == null) {
                ControlUtils.ClearInputState(ConsiderCustomInput);
                return;
            }
            if (ViewModel.BillingData.ConsiderCustomModifiers != ConsiderCustomInput.IsChecked) {
                ControlUtils.SetInputChanged(ConsiderCustomInput);
            } else {
                ControlUtils.ClearInputState(ConsiderCustomInput);
            }
            UpdateButtons();
        }

        private void WeightModifierInput_TextChanged(object? sender, TextChangedEventArgs? evt) {
            var res = Validator.CheckDecimal(WeightModifierInput, false, 3, 2, true);
            if (ViewModel.BillingData == null) {
                ControlUtils.ClearInputState(WeightModifierInput);
                return;
            }
            var val = WeightModifierInput.Text.Length > 0 && res.IsValid ? double.Parse(WeightModifierInput.Text) : 0;
            ViewModel.WeightModifierChanged = (val != Math.Round(ViewModel.BillingData.NetWeightModifier * 100.0, 8) && val != Math.Round(ViewModel.BillingData.GrossWeightModifier * 100.0, 8)) ||
                (val == 0 && (ViewModel.BillingData.NetWeightModifier != 0 || ViewModel.BillingData.GrossWeightModifier != 0));
            if (ViewModel.WeightModifierChanged) {
                ControlUtils.SetInputChanged(WeightModifierInput);
            } else {
                ControlUtils.ClearInputState(WeightModifierInput);
            }
            UpdateButtons();
        }

        private void WeightModifierInput_LostFocus(object sender, RoutedEventArgs evt) {
            if (WeightModifierInput.Text.EndsWith(','))
                WeightModifierInput.Text = WeightModifierInput.Text[..^1];
        }
    }
}
