using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using Elwig.Helpers;
using Elwig.Models;
using System.Runtime.CompilerServices;
using System;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.ChangeTracking;

namespace Elwig.Windows {
    public partial class AreaCommAdminWindow : AdministrationWindow {
        private readonly Member member;
        private bool IsEditing = false;
        private bool IsCreating = false;

        public AreaCommAdminWindow(Member member) {
            InitializeComponent();
            this.member = member;
            Title = $"Flächenbindungen - {member.FamilyName} {member.GivenName} - Elwig";
            ExemptInputs = new Control[] {
                MgNrInput, ContractList, AreaCommitmentList, NewContractButton, DeleteContractButton,
                ContractSaveButton, ContractCancelButton, NewAreaCommitmentButton, 
                EditAreaCommitmentButton, DeleteAreaCommitmentButton, AreaCommitmentSaveButton,
                AreaCommitmentResetButton, AreaCommitmentCancelButton
            };
            RequiredInputs = new Control[] {
                VNrInput, YearFromInput, KgInput,
                GstNrInput, AreaInput, SortInput, CultInput
            };
        }

        private async void Window_Loaded(object sender, RoutedEventArgs e) {
            await RefreshContractList();
            KgInput.ItemsSource = Context.WbKgs.Select(k => k.Kg).OrderBy(k => k.Name).ToList();
            SortInput.ItemsSource = Context.WineVarieties.OrderBy(s => s.Name).ToList();
            AttrInput.ItemsSource = Context.WineAttributes.OrderBy(a => a.Name).ToList();
            CultInput.ItemsSource = Context.WineCultivations.OrderBy(c => c.Name).ToList();
        }

        private async Task RefreshContractList() {
            await Context.Contracts.LoadAsync();
            await RefreshContractListQuery();
        }

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

        private async Task RefreshContractListQuery() {
            List<Contract> contracts = await Context.Contracts.Where(c => c.MgNr == member.MgNr).ToListAsync();

            ContractList.ItemsSource = contracts;
            if (contracts.Count == 1)
                ContractList.SelectedIndex = 0;

            //TODO notwendig?
            await RefreshAreaCommitmentList();
            RefreshInputs();
        }

        private async Task RefreshAreaCommitmentListQuery() {
            Contract? contract = (Contract)ContractList.SelectedItem;
            
            if (contract == null) {
                AreaCommitmentList.ItemsSource = null;
                return;
            }

            List<AreaCommitment> commitments = await Context.AreaCommitments.Where(a => a.VNr == contract.VNr).ToListAsync();

            AreaCommitmentList.ItemsSource = commitments;
            if (commitments.Count == 1)
                AreaCommitmentList.SelectedIndex = 0;
            RefreshInputs();
        }

        private void RefreshInputs(bool validate = false) {
            ClearInputStates();
            Contract? c = (Contract)ContractList.SelectedItem;
            AreaCommitment? a = (AreaCommitment)AreaCommitmentList.SelectedItem;

            if (c != null) {
                DeleteContractButton.IsEnabled = true;
                NewAreaCommitmentButton.IsEnabled = true;
            } else {
                DeleteContractButton.IsEnabled = false;
                NewAreaCommitmentButton.IsEnabled = false;
            }

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

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

            VNrInput.Text = a.VNr.ToString();
            MgNrInput.Text = a.Contract.MgNr.ToString();
            YearFromInput.Text = a.Contract.YearFrom.ToString();
            YearToInput.Text = a.Contract.YearTo.ToString();

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

            SortInput.SelectedItem = a.WineVar;
            AttrInput.SelectedItem = a.WineAttr;
            CultInput.SelectedItem = a.WineCult;

            FillOriginalValues();
        }

        private void NewAreaCommitmentButton_Click(object sender, RoutedEventArgs evt) {
            IsCreating = true;
            AreaCommitmentList.IsEnabled = false;
            AreaCommitmentList.SelectedItem = null;
            ContractList.IsEnabled = false;
            HideAreaCommitmentNewEditDeleteButtons();
            ShowAreaCommitmentSaveResetCancelButtons();
            DisableContractNewDeleteButtons();
            UnlockInputs();
            VNrInput.IsReadOnly = true;
            YearFromInput.IsReadOnly = true;
            YearToInput.IsReadOnly = true;
            InitInputs();
        }

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

            IsEditing = true;
            AreaCommitmentList.IsEnabled = false;
            ContractList.IsEnabled = false;

            HideAreaCommitmentNewEditDeleteButtons();
            ShowAreaCommitmentSaveResetCancelButtons();
            DisableContractNewDeleteButtons();
            UnlockInputs();
        }

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

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

        private void NewContractButton_Click(object sender, RoutedEventArgs evt) {
            IsCreating = true;
            ContractList.IsEnabled = false;
            ContractList.SelectedItem = null;
            AreaCommitmentList.IsEnabled = false;
            AreaCommitmentList.SelectedItem = null;
            HideContractNewDeleteButtons();
            ShowContractSaveCancelButtons();
            DisableAreaCommitmentNewEditDeleteButtons();
            UnlockInputs();
            InitInputs();
        }

        private async void DeleteContractButton_Click(object sender, RoutedEventArgs evt) {
            Contract c = (Contract)ContractList.SelectedItem;
            if (c == null) return;

            var r = MessageBox.Show(
                $"Soll der Vertrag \"{c.VNr} und alle enthaltenen Flächenbindungen wirklich unwiderruflich gelöscht werden?",
                "Vertrag löschen", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
            if (r == MessageBoxResult.Yes) {
                Context.Remove(c);
                Context.SaveChanges();
                await RefreshContractList();
            }
        }

        private async Task<Contract> UpdateContract(Contract c) {
            int newVNr = int.Parse(VNrInput.Text);
            c.MgNr = int.Parse(MgNrInput.Text);
            c.YearFrom = int.Parse(YearFromInput.Text);
            c.YearTo = (YearToInput.Text == "") ? null : int.Parse(YearToInput.Text);

            EntityEntry<Contract>? tr = null;
            try {
                if (IsEditing) {
                    tr = Context.Update(c);
                } else if (IsCreating) {
                    c.VNr = newVNr;
                    tr = (await Context.AddAsync(c));
                }
                await Context.SaveChangesAsync();

                if (newVNr != c.VNr) {
                    await Context.Database.ExecuteSqlAsync($"UPDATE contract SET vnr = {newVNr} WHERE mgnr = {c.VNr}");
                    await Context.Contracts.LoadAsync();
                    c = await Context.Contracts.FindAsync(newVNr);
                }
            } 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, "Vertrag aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error);
            }

            return c;
        }

        private async Task<AreaCommitment> UpdateaAreaCommitment(AreaCommitment a) {
            a.VNr = int.Parse(VNrInput.Text);
            a.KgNr = ((AT_Kg)KgInput.SelectedItem).KgNr;
            a.RdNr = ((WbRd)RdInput.SelectedItem).RdNr;
            a.GstNr = GstNrInput.Text;
            a.Area = int.Parse(AreaInput.Text);
            a.SortId = ((WineVar)SortInput.SelectedItem).SortId;
            a.AttrId = ((WineAttr)AttrInput.SelectedItem).AttrId;
            a.CultId = ((WineCult)CultInput.SelectedItem).CultId;

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

            } 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) {
            Contract c = await UpdateContract((Contract)ContractList.SelectedItem);
            AreaCommitment a = await UpdateaAreaCommitment(IsEditing ? (AreaCommitment)AreaCommitmentList.SelectedItem : Context.CreateProxy<AreaCommitment>());

            IsEditing = false;
            IsCreating = false;
            AreaCommitmentList.IsEnabled = true;
            ContractList.IsEnabled = true;
            HideAreaCommitmentSaveResetCancelButtons();
            ShowAreaCommitmentNewEditDeleteButtons();
            EnableContractNewDeleteButtons();
            LockInputs();
            await RefreshContractList();
            await RefreshAreaCommitmentList();
            ContractList.SelectedItem = c;
            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;
            ContractList.IsEnabled = true;
            HideAreaCommitmentSaveResetCancelButtons();
            ShowAreaCommitmentNewEditDeleteButtons();
            EnableContractNewDeleteButtons();
            RefreshInputs();
            ClearInputStates();
            LockInputs();
        }

        private async void ContractSaveButton_Click(object sender, RoutedEventArgs evt) {
            Contract c = await UpdateContract(Context.CreateProxy<Contract>());
            AreaCommitment a = await UpdateaAreaCommitment(Context.CreateProxy<AreaCommitment>());

            IsEditing = false;
            IsCreating = false;
            AreaCommitmentList.IsEnabled = true;
            ContractList.IsEnabled = true;
            HideContractSaveCancelButtons();
            ShowContractNewDeleteButtons();
            EnableAreaCommitmentNewEditDeleteButtons();
            LockInputs();
            await RefreshContractList();
            await RefreshAreaCommitmentList();
            ContractList.SelectedItem = c;
            AreaCommitmentList.SelectedItem = a;
        }

        private void ContractCancelButton_Click(object sender, RoutedEventArgs evt) {
            IsEditing = false;
            IsCreating = false;
            ContractList.IsEnabled = true;
            AreaCommitmentList.IsEnabled = true;
            HideContractSaveCancelButtons();
            ShowContractNewDeleteButtons();
            EnableAreaCommitmentNewEditDeleteButtons();
            RefreshInputs();
            ClearInputStates();
            LockInputs();
        }

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

        private async void InitInputs() {
            VNrInput.Text = ContractList.SelectedItem == null ? (await Utils.NextVNr(Context)).ToString() : ((Contract)ContractList.SelectedItem).VNr.ToString();
            MgNrInput.Text = member.MgNr.ToString();
            FillOriginalValues();
            ValidateRequiredInputs();
        }

        private void EnableContractNewDeleteButtons() {
            NewContractButton.IsEnabled = true;
            DeleteContractButton.IsEnabled = ContractList.SelectedItem != null;
        }

        private void DisableContractNewDeleteButtons() {
            NewContractButton.IsEnabled = false;
            DeleteContractButton.IsEnabled = false;
        }

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

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

        private void ShowContractSaveCancelButtons() {
            ContractSaveButton.IsEnabled = false;
            ContractCancelButton.IsEnabled = true;
            ContractSaveButton.Visibility = Visibility.Visible;
            ContractCancelButton.Visibility = Visibility.Visible;
        }

        private void HideContractSaveCancelButtons() {
            ContractSaveButton.IsEnabled = false;
            ContractCancelButton.IsEnabled = false;
            ContractSaveButton.Visibility = Visibility.Hidden;
            ContractCancelButton.Visibility = Visibility.Hidden;
        }

        private void ShowContractNewDeleteButtons() {
            EnableContractNewDeleteButtons();
            NewContractButton.Visibility = Visibility.Visible;
            DeleteContractButton.Visibility = Visibility.Visible;
        }

        private void HideContractNewDeleteButtons() {
            DisableContractNewDeleteButtons();
            NewContractButton.Visibility = Visibility.Hidden;
            DeleteContractButton.Visibility = Visibility.Hidden;
        }

        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 async void ContractList_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
            //await RefreshContractListQuery();
            await RefreshAreaCommitmentList();
        }

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

        private void KgInput_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
            AT_Kg? curr_kg = (AT_Kg)KgInput.SelectedItem;
            if (curr_kg != null) {
                RdInput.ItemsSource = Context.WbRde.Where(r => r.KgNr == curr_kg.KgNr).OrderBy(r => r.Name).ToList();
            }
            ComboBox_SelectionChanged(sender, evt);
        }

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

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

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

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

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

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

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

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

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