using Microsoft.EntityFrameworkCore;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using Elwig.Helpers;
using Elwig.Models;
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Xceed.Wpf.Toolkit.Primitives;

namespace Elwig.Windows {
    public partial class AreaComAdminWindow : AdministrationWindow {
        private readonly Member Member;

        public AreaComAdminWindow(int mgnr) {
            InitializeComponent();
            Member = Context.Members.Find(mgnr) ?? throw new ArgumentException("MgNr argument has invalid value");
            Title = $"Flächenbindungen - {Member.AdministrativeName} - Elwig";
            ExemptInputs = new Control[] {
                MgNrInput, AreaCommitmentList, NewAreaCommitmentButton, 
                EditAreaCommitmentButton, DeleteAreaCommitmentButton, AreaCommitmentSaveButton,
                AreaCommitmentResetButton, AreaCommitmentCancelButton
            };
            RequiredInputs = new Control[] {
                FbNrInput, YearFromInput, KgInput,
                GstNrInput, AreaInput, WineVarietyInput, WineCultivationInput
            };
        }

        private async void Window_Loaded(object sender, RoutedEventArgs e) {
            await RenewContext();
        }

        private async Task RefreshAreaCommitmentList() {
            await Context.AreaCommitments.LoadAsync();
            await RefreshAreaCommitmentListQuery();
        }

        private async Task RefreshAreaCommitmentListQuery() {
            List<AreaCom> areaComs = await Context.AreaCommitments.Where(a => a.MgNr == Member.MgNr).OrderBy(a => a.FbNr).ToListAsync();

            Utils.RenewItemsSource(AreaCommitmentList, areaComs, i => (i as AreaCom)?.FbNr);
            if (areaComs.Count == 1)
                AreaCommitmentList.SelectedIndex = 0;

            RefreshInputs();
        }

        private void RefreshInputs(bool validate = false) {
            ClearInputStates();
            AreaCom? a = (AreaCom)AreaCommitmentList.SelectedItem;

            if (a != null) {
                EditAreaCommitmentButton.IsEnabled = true;
                DeleteAreaCommitmentButton.IsEnabled = true;
                FillInputs(a);
            } else {
                EditAreaCommitmentButton.IsEnabled = false;
                DeleteAreaCommitmentButton.IsEnabled = false;
                ClearOriginalValues();
                ClearInputs();
                AttributesInput.UnSelectAll();
                MgNrInput.Text = "";
            }
            if (!validate) ClearInputStates();
            GC.Collect();
        }

        private void FillInputs(AreaCom a) {
            ClearOriginalValues();

            FbNrInput.Text = a.FbNr.ToString();
            MgNrInput.Text = a.MgNr.ToString();
            YearFromInput.Text = a.YearFrom.ToString();
            YearToInput.Text = a.YearTo.ToString();

            KgInput.SelectedItem = a.Kg.AtKg;
            RdInput.SelectedItem = a.Rd;
            GstNrInput.Text = a.GstNr;
            AreaInput.Text = a.Area.ToString();

            WineVarietyInput.SelectedItem = a.WineVar;

            AttributesInput.UnSelectAll();
            foreach (var attr in a.Attributes) {
                AttributesInput.SelectedItems.Add(attr);
            }

            WineCultivationInput.SelectedItem = a.WineCult;

            FillOriginalValues();
        }

        private async void InitInputs() {
            FbNrInput.Text = (await Context.NextFbNr()).ToString();
            MgNrInput.Text = Member.MgNr.ToString();
            FillOriginalValues();
            ValidateRequiredInputs();
        }

        protected override async Task RenewContext() {
            await base.RenewContext();
            Utils.RenewItemsSource(KgInput, await Context.WbKgs.Select(k => k.AtKg).OrderBy(k => k.Name).ToListAsync(), i => (i as AT_Kg)?.KgNr);
            Utils.RenewItemsSource(WineVarietyInput, await Context.WineVarieties.OrderBy(v => v.Name).ToListAsync(), i => (i as WineVar)?.SortId);
            Utils.RenewItemsSource(AttributesInput, await Context.WineAttributes.OrderBy(a => a.Name).ToListAsync(), i => (i as WineAttr)?.AttrId);
            Utils.RenewItemsSource(WineCultivationInput, await Context.WineCultivations.OrderBy(c => c.Name).ToListAsync(), i => (i as WineCult)?.CultId);
            await RefreshAreaCommitmentList();
        }

        private void NewAreaCommitmentButton_Click(object sender, RoutedEventArgs evt) {
            IsCreating = true;
            AreaCommitmentList.IsEnabled = false;
            AreaCommitmentList.SelectedItem = null;
            HideAreaCommitmentNewEditDeleteButtons();
            ShowAreaCommitmentSaveResetCancelButtons();
            UnlockInputs();
            AttributesInput.IsEnabled = true;
            InitInputs();
        }

        private void EditAreaCommitmentButton_Click(object sender, RoutedEventArgs evt) {
            if (AreaCommitmentList.SelectedItem == null)
                return;

            IsEditing = true;
            AreaCommitmentList.IsEnabled = false;

            HideAreaCommitmentNewEditDeleteButtons();
            ShowAreaCommitmentSaveResetCancelButtons();
            UnlockInputs();
            AttributesInput.IsEnabled = true;
        }

        private async void DeleteAreaCommitmentButton_Click(object sender, RoutedEventArgs evt) {
            AreaCom a = (AreaCom)AreaCommitmentList.SelectedItem;
            if (a == null) return;

            var r = MessageBox.Show(
                $"Soll die Flächenbindung {a.GstNr} ({a.Area} m²) wirklich unwiderruflich gelöscht werden?",
                "Parzelle löschen", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
            if (r == MessageBoxResult.Yes) {
                Context.Remove(a);
                Context.SaveChanges();
                await RefreshAreaCommitmentList();
            }
        }

        private async Task<AreaCom> UpdateAreaCom(AreaCom a) {
            int newFbNr = int.Parse(FbNrInput.Text);
            a.MgNr = int.Parse(MgNrInput.Text);
            a.YearFrom = int.Parse(YearFromInput.Text);
            a.YearTo = (YearToInput.Text == "") ? null : int.Parse(YearToInput.Text);
            a.KgNr = ((AT_Kg)KgInput.SelectedItem).KgNr;
            a.RdNr = RdInput.SelectedItem.GetType() == typeof(NullItem) ? null : ((WbRd)RdInput.SelectedItem).RdNr;
            a.GstNr = GstNrInput.Text;
            a.Area = int.Parse(AreaInput.Text);
            a.SortId = ((WineVar)WineVarietyInput.SelectedItem).SortId;
            a.CultId = ((WineCult)WineCultivationInput.SelectedItem).CultId;

            EntityEntry<AreaCom>? tr = null;
            try {
                if (IsEditing) {
                    tr = Context.Update(a);
                } else if (IsCreating) {
                    a.FbNr = newFbNr;
                    tr = (await Context.AddAsync(a));
                }


                foreach (WineAttr attr in a.Attributes) {
                    if (!AttributesInput.SelectedItems.Contains(attr)) {
                        var aca = a.AttributeEntries.FirstOrDefault(h => h.AttrId == attr.AttrId);
                        Context.Remove(aca);
                    }
                }

                foreach (WineAttr attr in AttributesInput.SelectedItems) {
                    if (!a.Attributes.Contains(attr)) {
                        AreaComAttr aca = Context.CreateProxy<AreaComAttr>();
                        aca.FbNr = a.FbNr;
                        aca.AttrId = attr.AttrId;
                        await Context.AddAsync(aca);
                    }
                }

                await Context.SaveChangesAsync();

                if (newFbNr != a.FbNr) {
                    await Context.Database.ExecuteSqlAsync($"UPDATE area_commitment SET fbnr = {newFbNr} WHERE fbnr = {a.FbNr}");
                    await Context.Members.LoadAsync();
                    a = await Context.AreaCommitments.FindAsync(newFbNr);
                }

            } catch (Exception exc) {
                if (tr != null) await tr.ReloadAsync();
                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, "Flächenbindung aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error);
            }

            return a;
        }

        private async void AreaCommitmentSaveButton_Click(object sender, RoutedEventArgs evt) {
            AreaCom a = await UpdateAreaCom(IsEditing ? (AreaCom)AreaCommitmentList.SelectedItem : Context.CreateProxy<AreaCom>());
            IsEditing = false;
            IsCreating = false;
            AreaCommitmentList.IsEnabled = true;
            HideAreaCommitmentSaveResetCancelButtons();
            ShowAreaCommitmentNewEditDeleteButtons();
            LockInputs();
            AttributesInput.IsEnabled = false;
            await RefreshAreaCommitmentList();
            AreaCommitmentList.SelectedItem = a;
        }

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

        private void AreaCommitmentCancelButton_Click(object sender, RoutedEventArgs evt) {
            IsEditing = false;
            IsCreating = false;
            AreaCommitmentList.IsEnabled = true;
            HideAreaCommitmentSaveResetCancelButtons();
            ShowAreaCommitmentNewEditDeleteButtons();
            RefreshInputs();
            ClearInputStates();
            LockInputs();
            AttributesInput.IsEnabled = false;
        }

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

        private void DisableAreaCommitmentNewEditDeleteButtons() {
            NewAreaCommitmentButton.IsEnabled = false;
            EditAreaCommitmentButton.IsEnabled = false;
            DeleteAreaCommitmentButton.IsEnabled = false;
        }

        private void EnableAreaCommitmentNewEditDeleteButtons() {
            NewAreaCommitmentButton.IsEnabled = true;
            EditAreaCommitmentButton.IsEnabled = AreaCommitmentList.SelectedItem != null;
            DeleteAreaCommitmentButton.IsEnabled = AreaCommitmentList.SelectedItem != null;
        }

        private void ShowAreaCommitmentSaveResetCancelButtons() {
            AreaCommitmentSaveButton.IsEnabled = false;
            AreaCommitmentResetButton.IsEnabled = false;
            AreaCommitmentCancelButton.IsEnabled = true;
            AreaCommitmentSaveButton.Visibility = Visibility.Visible;
            AreaCommitmentResetButton.Visibility = Visibility.Visible;
            AreaCommitmentCancelButton.Visibility = Visibility.Visible;
        }

        private void HideAreaCommitmentSaveResetCancelButtons() {
            AreaCommitmentSaveButton.IsEnabled = false;
            AreaCommitmentResetButton.IsEnabled = false;
            AreaCommitmentCancelButton.IsEnabled = false;
            AreaCommitmentSaveButton.Visibility = Visibility.Hidden;
            AreaCommitmentResetButton.Visibility = Visibility.Hidden;
            AreaCommitmentCancelButton.Visibility = Visibility.Hidden;
        }

        private void ShowAreaCommitmentNewEditDeleteButtons() {
            EnableAreaCommitmentNewEditDeleteButtons();
            NewAreaCommitmentButton.Visibility = Visibility.Visible;
            EditAreaCommitmentButton.Visibility = Visibility.Visible;
            DeleteAreaCommitmentButton.Visibility = Visibility.Visible;
        }

        private void HideAreaCommitmentNewEditDeleteButtons() {
            DisableAreaCommitmentNewEditDeleteButtons();
            NewAreaCommitmentButton.Visibility = Visibility.Hidden;
            EditAreaCommitmentButton.Visibility = Visibility.Hidden;
            DeleteAreaCommitmentButton.Visibility = Visibility.Hidden;
        }

        private void AreaCommitmentList_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
            RefreshInputs();
        }

        private void AttributesInput_SelectionChanged(object sender, ItemSelectionChangedEventArgs evt) {

        }

        private async void KgInput_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
            AT_Kg? curr_kg = (AT_Kg)KgInput.SelectedItem;
            if (curr_kg != null) {
                var rdList = await Context.WbRde.Where(r => r.KgNr == curr_kg.KgNr).OrderBy(r => r.Name).Cast<object>().ToListAsync();
                rdList.Insert(0, new NullItem());
                Utils.RenewItemsSource(RdInput, rdList, i => (i as WbRd)?.RdNr);
            }
            ComboBox_SelectionChanged(sender, evt);
        }

        protected void InputTextChanged(TextBox input, Func<TextBox, bool, AppDbContext, AreaCom?, ValidationResult> checker) {
            InputTextChanged(input, checker(input, SenderIsRequired(input), Context, (AreaCom)AreaCommitmentList.SelectedItem));
        }

        protected void InputLostFocus(TextBox input, Func<TextBox, bool, AppDbContext, AreaCom?, ValidationResult> checker, string? msg = null) {
            InputLostFocus(input, checker(input, SenderIsRequired(input), Context, (AreaCom)AreaCommitmentList.SelectedItem), msg);
        }

        private void FbNrInput_TextChanged(object sender, RoutedEventArgs evt) {
            InputTextChanged((TextBox)sender, Validator.CheckFbNr);
        }

        private void FbNrInput_LostFocus(object sender, RoutedEventArgs evt) {
            InputLostFocus((TextBox)sender, Validator.CheckFbNr);
        }
        
        private void GstNrInput_TextChanged(object sender, RoutedEventArgs evt) {
            InputTextChanged((TextBox)sender, Validator.CheckGstNr);
        }

        private void GstNrInput_LostFocus(object sender, RoutedEventArgs evt) {
            InputLostFocus((TextBox)sender, Validator.CheckGstNr);
        }
    }
}