using Elwig.Helpers;
using Elwig.Models;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

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

        private Dictionary<string, string?>? Modifiers = null;
        private Dictionary<Modifier, string>? ModifierIds = null;
        private ObservableCollection<Modifier>? ModifierList = null;
        private bool ModifiersChanged = false;
        private bool ModifierUpdate = false;

        public BaseDataWindow() {
            InitializeComponent();
            RequiredInputs = new Control[] {
                ClientNameInput, ClientNameTypeInput, ClientNameTokenInput, ClientNameShortInput,
                ClientAddressInput, ClientPlzInput, ClientOrtInput,
            };
            ExemptInputs = new Control[] {
                ClientNameFull,
                SeasonModifierIdInput, SeasonModifierNameInput, SeasonModifierRelInput, SeasonModifierAbsInput,
            };
        }

        new protected void LockInputs() {
            base.LockInputs();
            SeasonModifierIdInput.IsReadOnly = true;
            SeasonModifierNameInput.IsReadOnly = true;
            SeasonModifierRelInput.IsReadOnly = true;
            SeasonModifierAbsInput.IsReadOnly = true;
        }

        new protected void UnlockInputs() {
            base.UnlockInputs();
            SeasonModifierIdInput.IsReadOnly = false;
            SeasonModifierNameInput.IsReadOnly = false;
            SeasonModifierRelInput.IsReadOnly = false;
            SeasonModifierAbsInput.IsReadOnly = false;
        }

        private void Window_Loaded(object sender, RoutedEventArgs evt) {
            LockInputs();
            FillInputs(App.Client);
        }

        protected override async Task RenewContext() {
            await base.RenewContext();
            ControlUtils.RenewItemsSource(SeasonList, await Context.Seasons.OrderByDescending(s => s.Year).ToListAsync(), s => (s as Season)?.Year, null, ControlUtils.RenewSourceDefault.First);
            var year = (SeasonList.SelectedItem as Season)?.Year;
            ControlUtils.RenewItemsSource(SeasonModifierList, await Context.Modifiers.Where(m => m.Year == year).OrderBy(m => m.Ordering).ToListAsync(), m => (m as Modifier)?.ModId);
        }

        protected override void UpdateButtons() {
            if (!IsEditing && !IsCreating) return;
            bool ch = ModifiersChanged || HasChanged, v = IsValid;
            CancelButton.IsEnabled = true;
            ResetButton.IsEnabled = ch;
            SaveButton.IsEnabled = ch && v;
            SeasonModifierUpButton.IsEnabled = SeasonModifierList.SelectedIndex >= 1;
            SeasonModifierDownButton.IsEnabled = SeasonModifierList.SelectedIndex != -1 && SeasonModifierList.SelectedIndex < (ModifierList?.Count - 1 ?? 0);
            SeasonModifierAddButton.IsEnabled = true;
            SeasonModifierDeleteButton.IsEnabled = SeasonModifierList.SelectedIndex != -1;
        }

        private void ModifiersInitEditing() {
            var year = (SeasonList.SelectedItem as Season)?.Year;
            Context.ChangeTracker.Clear();
            ModifierList = new(Context.Modifiers.Where(m => m.Year == year).OrderBy(m => m.Ordering).ToList());
            Modifiers = ModifierList.ToDictionary(m => m.ModId, m => m.ModId);
            ModifierIds = ModifierList.ToDictionary(m => m, m => m.ModId);
            ControlUtils.RenewItemsSource(SeasonModifierList, ModifierList, m => (m as Modifier)?.ModId);
        }

        private void ModifiersFinishEditing() {
            var year = (SeasonList.SelectedItem as Season)?.Year;
            ControlUtils.RenewItemsSource(SeasonModifierList, Context.Modifiers.Where(m => m.Year == year).OrderBy(m => m.Ordering).ToList(), m => (m as Modifier)?.ModId);
            ModifierList = null;
            Modifiers = null;
            ModifierIds = null;
            ModifiersChanged = false;

            SeasonModifierUpButton.IsEnabled = false;
            SeasonModifierDownButton.IsEnabled = false;
            SeasonModifierAddButton.IsEnabled = false;
            SeasonModifierDeleteButton.IsEnabled = false;
        }

        private async Task ModifiersSave() {
            if (!ModifiersChanged || ModifierList == null || Modifiers == null || ModifierIds == null) return;
            int i = 0;
            foreach (var mod in ModifierList) mod.Ordering = ++i;

            var year = (SeasonList.SelectedItem as Season)?.Year;
            foreach (var (modid, _) in Modifiers.Where(m => m.Value == null)) {
                Context.Remove(Context.Modifiers.Find(new object?[] { year, modid }));
            }
            foreach (var (mod, old) in ModifierIds) {
                mod.ModId = old;
            }
            foreach (var (old, modid) in Modifiers.Where(m => m.Value != null)) {
                Context.Update(Context.Modifiers.Find(new object?[] { year, old }));
            }
            await Context.SaveChangesAsync();

            foreach (var (old, modid) in Modifiers.Where(m => m.Value != null)) {
                await Context.Database.ExecuteSqlAsync($"UPDATE modifier SET modid = {modid} WHERE (year, modid) = ({year}, {old})");
            }
            await Context.SaveChangesAsync();

            foreach (var mod in ModifierList.Where(m => !ModifierIds.ContainsKey(m))) {
                await Context.AddAsync(mod);
            }
            await Context.SaveChangesAsync();
        }

        private void EditButton_Click(object sender, RoutedEventArgs evt) {
            IsEditing = true;
            EditButton.Visibility = Visibility.Hidden;
            ResetButton.Visibility = Visibility.Visible;

            ModifiersInitEditing();
            UnlockInputs();
            UpdateButtons();
        }

        private void CancelButton_Click(object sender, RoutedEventArgs evt) {
            IsEditing = false;
            IsCreating = false;
            EditButton.Visibility = Visibility.Visible;
            ResetButton.Visibility = Visibility.Hidden;
            CancelButton.IsEnabled = false;
            SaveButton.IsEnabled = false;
            ResetButton.IsEnabled = false;
            ModifiersFinishEditing();

            ClearInputStates();
            FillInputs(App.Client);
            LockInputs();
        }

        private void ResetButton_Click(object sender, RoutedEventArgs evt) {
            ModifiersChanged = false;
            ModifiersInitEditing();
            ClearInputStates();
            FillInputs(App.Client);
            UpdateButtons();
        }

        private async void SaveButton_Click(object sender, RoutedEventArgs evt) {
            try {
                await UpdateClientParameters(App.Client);
                await ModifiersSave();
            } 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, "Stammdaten aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error);
            }

            IsEditing = false;
            IsCreating = false;
            EditButton.Visibility = Visibility.Visible;
            ResetButton.Visibility = Visibility.Hidden;
            CancelButton.IsEnabled = false;
            SaveButton.IsEnabled = false;
            ResetButton.IsEnabled = false;
            ModifiersFinishEditing();

            ClearInputStates();
            FillInputs(App.Client);
            LockInputs();
        }

        private void FillInputs(ClientParameters p) {
            ClearOriginalValues();
            ClearDefaultValues();

            ClientNameInput.Text = p.Name;
            ClientNameSuffixInput.Text = p.NameSuffix;
            ClientNameTypeInput.Text = p.NameType;
            ClientNameTokenInput.Text = p.NameToken;
            ClientNameShortInput.Text = p.NameShort;
            ClientAddressInput.Text = p.Address;
            ClientPlzInput.Text = p.Plz.ToString();
            ClientOrtInput.Text = p.Ort;
            ClientIbanInput.Text = p.Iban;
            ClientBicInput.Text = p.Bic;
            ClientUstIdNrInput.Text = p.UstIdNr;
            ClientLfbisNrInput.Text = p.LfbisNr;
            ClientPhoneNrInput.Text = p.PhoneNr;
            ClientFaxNrInput.Text = p.FaxNr;
            ClientEmailAddressInput.Text = p.EmailAddress;
            ClientWebsiteInput.Text = p.Website;

            FinishInputFilling();
        }

        private async Task UpdateClientParameters(ClientParameters p) {
            p.Name = ClientNameInput.Text;
            p.NameSuffix = ClientNameSuffixInput.Text.Length > 0 ? ClientNameSuffixInput.Text : null;
            p.NameType = ClientNameTypeInput.Text;
            p.NameToken = ClientNameTokenInput.Text;
            p.NameShort = ClientNameShortInput.Text;
            p.Address = ClientAddressInput.Text;
            p.Plz = int.Parse(ClientPlzInput.Text);
            p.Ort = ClientOrtInput.Text;
            p.Iban = ClientIbanInput.Text.Length > 0 ? ClientIbanInput.Text : null;
            p.Bic = ClientBicInput.Text.Length > 0 ? ClientBicInput.Text : null;
            p.UstIdNr = ClientUstIdNrInput.Text.Length > 0 ? ClientUstIdNrInput.Text : null;
            p.LfbisNr = ClientLfbisNrInput.Text.Length > 0 ? ClientLfbisNrInput.Text : null;
            p.PhoneNr = ClientPhoneNrInput.Text.Length > 0 ? ClientPhoneNrInput.Text : null;
            p.FaxNr = ClientFaxNrInput.Text.Length > 0 ? ClientFaxNrInput.Text : null;
            p.EmailAddress = ClientEmailAddressInput.Text.Length > 0 ? ClientEmailAddressInput.Text : null;
            p.Website = ClientWebsiteInput.Text.Length > 0 ? ClientWebsiteInput.Text : null;

            await p.UpdateValues();
        }

        private void SeasonModifierUpButton_Click(object sender, RoutedEventArgs evt) {
            if (ModifierList == null) return;
            ModifiersChanged = true;
            var idx = SeasonModifierList.SelectedIndex;
            var item = ModifierList[idx];
            ModifierList.RemoveAt(idx);
            idx--;
            ModifierList.Insert(idx, item);
            SeasonModifierList.SelectedIndex = idx;
            UpdateButtons();
        }

        private void SeasonModifierDownButton_Click(object sender, RoutedEventArgs evt) {
            if (ModifierList == null) return;
            ModifiersChanged = true;
            var idx = SeasonModifierList.SelectedIndex;
            var item = ModifierList[idx];
            ModifierList.RemoveAt(idx);
            idx++;
            ModifierList.Insert(idx, item);
            SeasonModifierList.SelectedIndex = idx;
            UpdateButtons();
        }

        private void SeasonModifierAddButton_Click(object sender, RoutedEventArgs evt) {
            if (ModifierList == null || SeasonList.SelectedItem is not Season s) return;
            ModifiersChanged = true;
            var idx = (SeasonModifierList.SelectedIndex != -1) ? SeasonModifierList.SelectedIndex + 1 : ModifierList.Count;
            var item = Context.CreateProxy<Modifier>();
            item.Year = s.Year;
            ModifierList.Insert(idx, item);
            SeasonModifierList.SelectedIndex = idx;
            UpdateButtons();
        }

        private void SeasonModifierDeleteButton_Click(object sender, RoutedEventArgs evt) {
            if (ModifierList == null || Modifiers == null) return;
            ModifiersChanged = true;
            var idx = SeasonModifierList.SelectedIndex;
            var item = ModifierList[idx];
            Modifiers[item.ModId] = null;
            ModifierList.RemoveAt(idx);
            SeasonModifierList.SelectedIndex = idx < ModifierList.Count ? idx : idx - 1;
            UpdateButtons();
        }


        private void ClientNames_TextChanged(object sender, TextChangedEventArgs evt) {
            var suffix = ClientNameSuffixInput.Text.Length > 0 ? ClientNameSuffixInput.Text : null;
            ClientNameFull.Text = $"{ClientNameInput.Text}{(suffix != null ? $", {suffix}," : "")} {ClientNameTypeInput.Text}";
            TextBox_TextChanged(sender, evt);
        }

        private async void SeasonList_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
            var year = (SeasonList.SelectedItem as Season)?.Year;
            SeasonModifierList.ItemsSource = await Context.Modifiers.Where(m => m.Year == year).OrderBy(m => m.Ordering).ToListAsync();
        }

        private void SeasonModifierList_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
            UpdateButtons();
            ModifierUpdate = true;
            if (SeasonModifierList.SelectedItem is not Modifier mod) {
                SeasonModifierIdInput.Text = "";
                SeasonModifierNameInput.Text = "";
                SeasonModifierRelInput.Text = "";
                SeasonModifierAbsInput.Text = "";
            } else {
                SeasonModifierIdInput.Text = mod.ModId;
                SeasonModifierNameInput.Text = mod.Name;
                SeasonModifierRelInput.Text = (mod.Rel * 100)?.ToString();
                SeasonModifierAbsInput.Text = mod.Abs?.ToString();
            }
            ModifierUpdate = false;
        }

        private void SeasonModifierIdInput_TextChanged(object sender, TextChangedEventArgs evt) {
            if ((!IsEditing && !IsCreating) || SeasonModifierList.SelectedItem is not Modifier mod || Modifiers == null || ModifierIds == null) return;
            ModifiersChanged = ModifiersChanged || (SeasonModifierIdInput.Text ?? "") != ((SeasonModifierList.SelectedItem as Modifier)?.ModId ?? "");
            if (ModifierUpdate) return;
            var old = ModifierIds.GetValueOrDefault(mod);
            var id = SeasonModifierIdInput.Text ?? "";
            if (old != null) Modifiers[old] = id;
            mod.ModId = id;
            CollectionViewSource.GetDefaultView(ModifierList).Refresh();
            UpdateButtons();
        }

        private void SeasonModifierNameInput_TextChanged(object sender, TextChangedEventArgs evt) {
            if ((!IsEditing && !IsCreating) || SeasonModifierList.SelectedItem is not Modifier mod) return;
            ModifiersChanged = ModifiersChanged || (SeasonModifierNameInput.Text ?? "") != ((SeasonModifierList.SelectedItem as Modifier)?.Name ?? "");
            if (ModifierUpdate) return;
            mod.Name = SeasonModifierNameInput.Text ?? "";
            CollectionViewSource.GetDefaultView(ModifierList).Refresh();
            UpdateButtons();
        }

        private void SeasonModifierRelInput_TextChanged(object sender, TextChangedEventArgs evt) {
            if ((!IsEditing && !IsCreating) || SeasonModifierList.SelectedItem is not Modifier mod) return;
            ModifiersChanged = ModifiersChanged || (SeasonModifierRelInput.Text ?? "") != ((SeasonModifierList.SelectedItem as Modifier)?.Rel?.ToString() ?? "");
            if (ModifierUpdate) return;
            mod.Rel = decimal.TryParse(SeasonModifierRelInput.Text, out var v) ? v / 100 : null;
            if (mod.Rel != null) SeasonModifierAbsInput.Text = "";
            CollectionViewSource.GetDefaultView(ModifierList).Refresh();
            UpdateButtons();
        }

        private void SeasonModifierAbsInput_TextChanged(object sender, TextChangedEventArgs evt) {
            if ((!IsEditing && !IsCreating) || SeasonModifierList.SelectedItem is not Modifier mod) return;
            ModifiersChanged = ModifiersChanged || (SeasonModifierAbsInput.Text ?? "") != ((SeasonModifierList.SelectedItem as Modifier)?.Abs?.ToString() ?? "");
            if (ModifierUpdate) return;
            mod.Abs = decimal.TryParse(SeasonModifierAbsInput.Text, out var v) ? v : null;
            if (mod.Abs != null) SeasonModifierRelInput.Text = "";
            CollectionViewSource.GetDefaultView(ModifierList).Refresh();
            UpdateButtons();
        }
    }
}