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

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

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

        private static readonly JsonSerializerOptions JsonOpt = new() { WriteIndented = true };

        public PaymentVariantsWindow(int year) {
            InitializeComponent();
            Year = year;
            SeasonLocked = Context.Seasons.Find(Year + 1) != null;
            Title = $"Auszahlungsvarianten - Lese {Year} - Elwig";
            if (!App.Config.Debug) {
                DataInput.Visibility = Visibility.Hidden;
            }
        }

        protected override async Task OnRenewContext() {
            ControlUtils.RenewItemsSource(PaymentVariantList, await Context.PaymentVariants.Where(v => v.Year == Year).OrderBy(v => v.AvNr).ToListAsync(), v => (v as PaymentVar)?.AvNr);
            Update();
        }

        private void Update() {
            if (PaymentVariantList.SelectedItem is PaymentVar v) {
                var locked = !v.TestVariant;
                DeleteButton.IsEnabled = !locked;
                CalculateButton.IsEnabled = !locked;
                CommitButton.IsEnabled = !locked && !SeasonLocked;
                CommitButton.Visibility = !locked ? Visibility.Visible : Visibility.Hidden;
                RevertButton.IsEnabled = locked && !SeasonLocked;
                RevertButton.Visibility = locked ? Visibility.Visible : Visibility.Hidden;
                Arrow3.Content = locked ? "\xF0B0" : "\xF0AF";
                CopyButton.IsEnabled = true;
                EditButton.Content = locked ? "Ansehen" : "Bearbeiten";
                ShowButton.IsEnabled = true;
                PrintButton.IsEnabled = true;
                ExportButton.IsEnabled = locked;

                NameInput.Text = v.Name;
                CommentInput.Text = v.Comment;
                DateInput.Text = $"{v.Date:dd.MM.yyyy}";
                TransferDateInput.Text = $"{v.TransferDate:dd.MM.yyyy}";
                if (App.Config.Debug) {
                    try {
                        var json = BillingData.ParseJson(v.Data);
                        DataInput.Text = JsonSerializer.Serialize(json, JsonOpt);
                    } catch {
                        DataInput.Text = v.Data;
                    }
                }
            } else {
                EditButton.Content = "Bearbeiten";
                CopyButton.IsEnabled = false;
                CalculateButton.IsEnabled = false;
                CommitButton.IsEnabled = false;
                CommitButton.Visibility = Visibility.Visible;
                RevertButton.IsEnabled = false;
                RevertButton.Visibility = Visibility.Hidden;
                Arrow3.Content = "\xF0AF";
                DeleteButton.IsEnabled = false;
                ShowButton.IsEnabled = false;
                PrintButton.IsEnabled = false;
                ExportButton.IsEnabled = false;

                NameInput.Text = "";
                CommentInput.Text = "";
                DateInput.Text = "";
                TransferDateInput.Text = "";
                DataInput.Text = "";
            }
            UpdateSums();
            UpdateSaveButton();
        }

        private void UpdateSaveButton() {
            SaveButton.IsEnabled = PaymentVariantList.SelectedItem != null &&
                ((DataChanged && DataValid) || NameChanged || CommentChanged ||
                (TransferDateChanged && TransferDateValid));
        }

        private void UpdateSums() {
            if (PaymentVariantList.SelectedItem is PaymentVar v) {
                var sym = v.Season.Currency.Symbol;
                ModifierSum.Text = $"{v.DeliveryPartPayments.Sum(p => p.Amount - p.NetAmount):N2} {sym}";
                TotalSum.Text = $"{v.MemberPayments.Sum(p => p.Amount):N2} {sym}";
                if (v.Credits.Count == 0) {
                    VatSum.Text = $"- {sym}";
                    DeductionSum.Text = $"- {sym}";
                    PaymentSum.Text = $"- {sym}";
                } else {
                    VatSum.Text = $"{v.Credits.Sum(c => c.VatAmount):N2} {sym}";
                    DeductionSum.Text = $"{-v.Credits.Sum(c => c.Modifiers ?? 0):N2} {sym}";
                    PaymentSum.Text = $"{v.Credits.Sum(c => c.Amount):N2} {sym}";
                }
            } else {
                ModifierSum.Text = "-";
                TotalSum.Text = "-";
                VatSum.Text = "-";
                DeductionSum.Text = "-";
                PaymentSum.Text = "-";
            }
        }

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

        private async void AddButton_Click(object sender, RoutedEventArgs evt) {
            try {
                PaymentVar v = Context.CreateProxy<PaymentVar>();

                v.Year = Year;
                v.AvNr = await Context.NextAvNr(Year);
                v.Name = "Neue Auszahlungsvariante";
                v.TestVariant = true;
                v.DateString = $"{DateTime.Today:yyyy-MM-dd}";
                v.Data = "{\"mode\": \"elwig\", \"version\": 1, \"payment\": 1.0, \"curves\": []}";

                await Context.AddAsync(v);
                await Context.SaveChangesAsync();
                await App.HintContextChange();

                ControlUtils.SelectListBoxItem(PaymentVariantList, v, v => (v as PaymentVar)?.AvNr);
            } 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);
            }
        }

        private async void CopyButton_Click(object sender, RoutedEventArgs evt) {
            if (PaymentVariantList.SelectedItem is not PaymentVar orig) return;
            try {
                PaymentVar n = Context.CreateProxy<PaymentVar>();

                n.Year = orig.Year;
                n.AvNr = await Context.NextAvNr(Year);
                n.Name = $"{orig.Name} (Kopie)";
                n.TestVariant = true;
                n.DateString = $"{DateTime.Today:yyyy-MM-dd}";
                n.Data = orig.Data;

                await Context.AddAsync(n);
                await Context.SaveChangesAsync();
                await App.HintContextChange();

                ControlUtils.SelectListBoxItem(PaymentVariantList, n, v => (v as PaymentVar)?.AvNr);
            } 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);
            }
        }

        private async void DeleteButton_Click(object sender, RoutedEventArgs evt) {
            if (PaymentVariantList.SelectedItem is not PaymentVar v || !v.TestVariant) return;
            try {
                Context.Remove(v);
                await Context.SaveChangesAsync();
                await 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);
            }
        }

        private async void CalculateButton_Click(object sender, RoutedEventArgs evt) {
            if (PaymentVariantList.SelectedValue is not PaymentVar v)
                return;
            CalculateButton.IsEnabled = false;
            Mouse.OverrideCursor = Cursors.AppStarting;
            try {
                var b = new BillingVariant(v.Year, v.AvNr);
                await b.Calculate();
            } catch (Exception exc) {
                MessageBox.Show(exc.Message, "Berechnungsfehler", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            await App.HintContextChange();
            Mouse.OverrideCursor = null;
            CalculateButton.IsEnabled = true;
        }

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

        private async void ShowButton_Click(object sender, RoutedEventArgs evt) {
            await Generate(1);
        }

        private async void PrintButton_Click(object sender, RoutedEventArgs evt) {
            await Generate(2);
        }

        private async void CommitButton_Click(object sender, RoutedEventArgs evt) {
            if (PaymentVariantList.SelectedValue is not PaymentVar v)
                return;
            CommitButton.IsEnabled = false;
            Mouse.OverrideCursor = Cursors.AppStarting;
            var b = new BillingVariant(v.Year, v.AvNr);
            await b.Commit();
            Mouse.OverrideCursor = null;
            RevertButton.IsEnabled = true;
            await App.HintContextChange();
        }

        private async void RevertButton_Click(object sender, RoutedEventArgs evt) {
            if (PaymentVariantList.SelectedValue 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;
            RevertButton.IsEnabled = false;
            Mouse.OverrideCursor = Cursors.AppStarting;
            var b = new BillingVariant(v.Year, v.AvNr);
            await b.Revert();
            Mouse.OverrideCursor = null;
            CommitButton.IsEnabled = true;
            await App.HintContextChange();
        }

        private async void ExportButton_Click(object sender, RoutedEventArgs evt) {
            if (PaymentVariantList.SelectedValue 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;
            }
            var d = new SaveFileDialog() {
                FileName = $"{App.Client.NameToken}-Überweisungsdaten-{v.Year}-{v.Name.Trim().Replace(' ', '-')}.{Ebics.FileExtension}",
                DefaultExt = Ebics.FileExtension,
                Filter = "EBICS-Datei (*.xml)|*.xml",
                Title = $"Überweisungsdaten speichern unter - Elwig",
            };
            if (d.ShowDialog() == true) {
                ExportButton.IsEnabled = false;
                Mouse.OverrideCursor = Cursors.AppStarting;
                using var e = new Ebics(v, d.FileName);
                await e.ExportAsync(Transaction.FromPaymentVariant(v));
                Mouse.OverrideCursor = null;
                ExportButton.IsEnabled = true;
            }
        }

        private async void SaveButton_Click(object sender, RoutedEventArgs evt) {
            if (PaymentVariantList.SelectedItem is not PaymentVar v) return;
            try {
                v.Name = NameInput.Text;
                v.Comment = (CommentInput.Text != "") ? CommentInput.Text : null;
                v.TransferDateString = (TransferDateInput.Text != "") ? string.Join("-", TransferDateInput.Text.Split(".").Reverse()) : null;
                if (App.Config.Debug) v.Data = JsonSerializer.Serialize(BillingData.ParseJson(DataInput.Text));
                Context.Update(v);
                await Context.SaveChangesAsync();
                await 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);
            }
        }

        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;
            }
            UpdateSaveButton();
        }

        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;
            }
            UpdateSaveButton();
        }

        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;
            }
            UpdateSaveButton();
        }

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

        private async Task Generate(int mode) {
            if (PaymentVariantList.SelectedItem is not PaymentVar v)
                return;

            Mouse.OverrideCursor = Cursors.AppStarting;

            var members = Context.Members.FromSqlRaw($"""
                SELECT m.*
                FROM member m
                    INNER JOIN delivery d ON d.mgnr = m.mgnr
                WHERE d.year = {Year}
                GROUP BY m.mgnr
                """);
            members = members.OrderBy(m => m.MgNr);

            IEnumerable<Member> list = await members.ToListAsync();
            var data = await CreditNoteData.ForPaymentVariant(Context.CreditNoteRows, Context.Seasons, v.Year, v.AvNr);
            var payments = await Context.MemberPayments.Where(p => p.Year == v.Year && p.AvNr == v.AvNr).ToDictionaryAsync(c => c.MgNr);
            await Context.GetMemberAreaCommitmentBuckets(Year, 0);
            using var doc = Document.Merge(list.Select(m =>
                new CreditNote(Context, payments[m.MgNr], data[m.MgNr], Context.GetMemberUnderDelivery(Year, m.MgNr).GetAwaiter().GetResult())
            ));
            await doc.Generate(new Progress<double>(v => {
                ProgressBar.Value = v;
            }));
            Mouse.OverrideCursor = null;

            if (mode < 2) {
                doc.Show();
                return;
            }
            if (App.Config.Debug) {
                doc.Show();
            } else {
                await doc.Print();
            }
        }
    }
}