using Elwig.Helpers;
using Elwig.Models.Entities;
using Microsoft.EntityFrameworkCore;
using System;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;

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

        protected AppDbContext? EditContext;

        public BaseDataWindow() {
            InitializeComponent();
            RequiredInputs = [
                ClientNameInput, ClientNameTypeInput, ClientNameTokenInput, ClientNameShortInput,
                ClientAddressInput, ClientPlzInput, ClientOrtInput,
            ];
            ExemptInputs = [
                BranchList, AreaCommitmentTypeList, WineAttributeList, WineCultivationList, SeasonList, SeasonModifierList,
                ClientNameFull,
                BranchIdInput, BranchNameInput, BranchPlzInput, BranchOrtInput,
                BranchAddressInput, BranchPhoneNrInput, BranchFaxNrInput, BranchMobileNrInput,
                WineAttributeIdInput, WineAttributeNameInput, WineAttributeActiveInput,
                WineAttributeMaxKgPerHaInput, WineAttributeStrictInput, WineAttributeFillLowerInput,
                WineCultivationIdInput, WineCultivationNameInput, WineCultivationDescriptionInput,
                AreaCommitmentTypeIdInput, AreaCommitmentTypeWineVariantInput, AreaCommitmentTypeWineAttributeInput,
                AreaCommitmentTypeMinKgPerHaInput, AreaCommitmentTypePenaltyPerKgInput,
                AreaCommitmentTypePenaltyInput, AreaCommitmentTypePenaltyNoneInput,
                SeasonMaxKgPerHaInput, SeasonVatNormalInput, SeasonVatFlatrateInput, SeasonStartInput, SeasonEndInput,
                SeasonMinKgPerBsInput, SeasonMaxKgPerBsInput, SeasonBsValueInput,
                SeasonPenaltyPerKgInput, SeasonPenaltyInput, SeasonPenaltyNoneInput,
                SeasonPenaltyPerBsInput, SeasonPenaltyPerBsNoneInput,
                SeasonModifierIdInput, SeasonModifierNameInput, SeasonModifierRelInput, SeasonModifierAbsInput,
            ];
            WineAttributeFillLowerInput.Visibility = Visibility.Hidden;
            WineAttributeFillLowerLabel.Visibility = Visibility.Hidden;
            ParameterAreaComGroup.Header = ParameterAreaComGroup.Header.ToString()!.Split('(')[0] + $"({Utils.CurrentLastSeason})";
        }

        protected override void ShortcutNew() { }

        protected override void ShortcutDelete() { }

        new protected void LockInputs() {
            base.LockInputs();

            BranchIdInput.IsReadOnly = true;
            BranchNameInput.IsReadOnly = true;
            BranchPlzInput.IsReadOnly = true;
            BranchOrtInput.IsEnabled = false;
            BranchAddressInput.IsReadOnly = true;
            BranchPhoneNrInput.IsReadOnly = true;
            BranchFaxNrInput.IsReadOnly = true;
            BranchMobileNrInput.IsReadOnly = true;

            WineAttributeIdInput.IsReadOnly = true;
            WineAttributeNameInput.IsReadOnly = true;
            WineAttributeActiveInput.IsEnabled = false;
            WineAttributeMaxKgPerHaInput.IsReadOnly = true;
            WineAttributeStrictInput.IsEnabled = false;
            WineAttributeFillLowerInput.IsEnabled = false;

            WineCultivationIdInput.IsReadOnly = true;
            WineCultivationNameInput.IsReadOnly = true;
            WineCultivationDescriptionInput.IsReadOnly = true;

            AreaCommitmentTypeWineVariantInput.IsEnabled = false;
            AreaCommitmentTypeWineAttributeInput.IsEnabled = false;
            AreaCommitmentTypeMinKgPerHaInput.IsReadOnly = true;
            AreaCommitmentTypePenaltyPerKgInput.IsReadOnly = true;
            AreaCommitmentTypePenaltyInput.IsReadOnly = true;
            AreaCommitmentTypePenaltyNoneInput.IsReadOnly = true;

            SeasonMaxKgPerHaInput.IsReadOnly = true;
            SeasonVatNormalInput.IsReadOnly = true;
            SeasonVatFlatrateInput.IsReadOnly = true;
            SeasonMinKgPerBsInput.IsReadOnly = true;
            SeasonMaxKgPerBsInput.IsReadOnly = true;
            SeasonPenaltyPerKgInput.IsReadOnly = true;
            SeasonPenaltyInput.IsReadOnly = true;
            SeasonPenaltyNoneInput.IsReadOnly = true;
            SeasonPenaltyPerBsInput.IsReadOnly = true;
            SeasonPenaltyPerBsNoneInput.IsReadOnly = true;
            SeasonBsValueInput.IsReadOnly = true;

            SeasonModifierIdInput.IsReadOnly = true;
            SeasonModifierNameInput.IsReadOnly = true;
            SeasonModifierRelInput.IsReadOnly = true;
            SeasonModifierAbsInput.IsReadOnly = true;

            ParameterAllowAttrIntoLowerInput.IsEnabled = false;
            ParameterAvoidUnderDeliveriesInput.IsEnabled = false;
            ParameterHonorGebundenInput.IsEnabled = false;
            ParameterExportEbicsVersion.IsEnabled = false;
            ParameterExportEbicsAddress.IsEnabled = false;
        }

        new protected void UnlockInputs() {
            base.UnlockInputs();

            BranchIdInput.IsReadOnly = false;
            BranchNameInput.IsReadOnly = false;
            BranchPlzInput.IsReadOnly = false;
            BranchOrtInput.IsEnabled = true;
            BranchAddressInput.IsReadOnly = false;
            BranchPhoneNrInput.IsReadOnly = false;
            BranchFaxNrInput.IsReadOnly = false;
            BranchMobileNrInput.IsReadOnly = false;

            WineAttributeIdInput.IsReadOnly = false;
            WineAttributeNameInput.IsReadOnly = false;
            WineAttributeActiveInput.IsEnabled = true;
            WineAttributeMaxKgPerHaInput.IsReadOnly = false;
            WineAttributeStrictInput.IsEnabled = true;
            WineAttributeFillLowerInput.IsEnabled = true;

            WineCultivationIdInput.IsReadOnly = false;
            WineCultivationNameInput.IsReadOnly = false;
            WineCultivationDescriptionInput.IsReadOnly = false;

            AreaCommitmentTypeWineVariantInput.IsEnabled = true;
            AreaCommitmentTypeWineAttributeInput.IsEnabled = true;
            AreaCommitmentTypeMinKgPerHaInput.IsReadOnly = false;
            AreaCommitmentTypePenaltyPerKgInput.IsReadOnly = false;
            AreaCommitmentTypePenaltyInput.IsReadOnly = false;
            AreaCommitmentTypePenaltyNoneInput.IsReadOnly = false;

            SeasonMaxKgPerHaInput.IsReadOnly = false;
            SeasonVatNormalInput.IsReadOnly = false;
            SeasonVatFlatrateInput.IsReadOnly = false;
            SeasonMinKgPerBsInput.IsReadOnly = false;
            SeasonMaxKgPerBsInput.IsReadOnly = false;
            SeasonPenaltyPerKgInput.IsReadOnly = false;
            SeasonPenaltyInput.IsReadOnly = false;
            SeasonPenaltyNoneInput.IsReadOnly = false;
            SeasonPenaltyPerBsInput.IsReadOnly = false;
            SeasonPenaltyPerBsNoneInput.IsReadOnly = false;
            SeasonBsValueInput.IsReadOnly = false;

            SeasonModifierIdInput.IsReadOnly = false;
            SeasonModifierNameInput.IsReadOnly = false;
            SeasonModifierRelInput.IsReadOnly = false;
            SeasonModifierAbsInput.IsReadOnly = false;

            ParameterAllowAttrIntoLowerInput.IsEnabled = true;
            ParameterAvoidUnderDeliveriesInput.IsEnabled = true;
            ParameterHonorGebundenInput.IsEnabled = true;
            ParameterExportEbicsVersion.IsEnabled = true;
            ParameterExportEbicsAddress.IsEnabled = true;
        }

        private void Window_Loaded(object sender, RoutedEventArgs evt) {
            LockInputs();
        }

        protected override async Task OnRenewContext(AppDbContext ctx) {
            await base.OnRenewContext(ctx);
            FillInputs(App.Client, await ctx.Seasons.FindAsync(Utils.CurrentLastSeason));
            ControlUtils.RenewItemsSource(SeasonList, await ctx.Seasons
                .OrderByDescending(s => s.Year)
                .Include(s => s.Modifiers)
                .Include(s => s.Currency)
                .ToListAsync(), null, ControlUtils.RenewSourceDefault.First);
            var year = (SeasonList.SelectedItem as Season)?.Year;
            ControlUtils.RenewItemsSource(BranchList, await ctx.Branches
                .OrderBy(b => b.Name)
                .Include(b => b.PostalDest!.AtPlz)
                .ToListAsync(), null, ControlUtils.RenewSourceDefault.First);
            ControlUtils.RenewItemsSource(WineAttributeList, await ctx.WineAttributes
                .OrderBy(a => a.Name)
                .ToListAsync(), null, ControlUtils.RenewSourceDefault.First);
            ControlUtils.RenewItemsSource(AreaCommitmentTypeWineVariantInput, await ctx.WineVarieties
                .OrderBy(s => s.Name)
                .ToListAsync());
            var attrList = await ctx.WineAttributes.OrderBy(a => a.Name).Cast<object>().ToListAsync();
            attrList.Insert(0, new NullItem(""));
            ControlUtils.RenewItemsSource(AreaCommitmentTypeWineAttributeInput, attrList);
            ControlUtils.RenewItemsSource(AreaCommitmentTypeList, await ctx.AreaCommitmentTypes
                .OrderBy(t => t.VtrgId)
                .Include(t => t.WineVar)
                .Include(t => t.WineAttr)
                .ToListAsync(), null, ControlUtils.RenewSourceDefault.First);
            ControlUtils.RenewItemsSource(WineCultivationList, await ctx.WineCultivations
                .OrderBy(c => c.Name)
                .ToListAsync(), null, ControlUtils.RenewSourceDefault.First);
            ControlUtils.RenewItemsSource(SeasonModifierList, await ctx.Modifiers
                .Where(m => m.Year == year)
                .OrderBy(m => m.Ordering)
                .ToListAsync(), null, ControlUtils.RenewSourceDefault.First);
        }

        protected override void UpdateButtons() {
            if (!IsEditing && !IsCreating) return;
            bool ch = _branchChanged || _attrChanged || _cultChanged || _actChanged ||
                      _seasonChanged || _modChanged || HasChanged,
                v = IsValid;
            CancelButton.IsEnabled = true;
            ResetButton.IsEnabled = ch;
            SaveButton.IsEnabled = ch && v;

            BranchAddButton.IsEnabled = true;
            BranchDeleteButton.IsEnabled = BranchList.SelectedIndex != -1;
            WineAttributeAddButton.IsEnabled = true;
            WineAttributeDeleteButton.IsEnabled = WineAttributeList.SelectedIndex != -1;
            WineCultivationAddButton.IsEnabled = true;
            WineCultivationDeleteButton.IsEnabled = WineCultivationList.SelectedIndex != -1;
            AreaCommitmentTypeAddButton.IsEnabled = true;
            AreaCommitmentTypeDeleteButton.IsEnabled = AreaCommitmentTypeList.SelectedIndex != -1;
            SeasonModifierUpButton.IsEnabled = SeasonModifierList.SelectedIndex >= 1;
            SeasonModifierDownButton.IsEnabled = SeasonModifierList.SelectedIndex != -1 && SeasonModifierList.SelectedIndex < (_modList?.Count - 1 ?? 0);
            SeasonModifierAddButton.IsEnabled = true;
            SeasonModifierDeleteButton.IsEnabled = SeasonModifierList.SelectedIndex != -1;
        }

        private void OriginButton_Click(object sender, RoutedEventArgs evt) {
            App.FocusOriginHierarchy();
        }

        private async Task InitEditing() {
            EditContext = new AppDbContext();
            await BranchesInitEditing(EditContext);
            await WineAttributesInitEditing(EditContext);
            await WineCultivationsInitEditing(EditContext);
            await AreaCommitmentTypesInitEditing(EditContext);
            await SeasonsInitEditing(EditContext);
            await ModifiersInitEditing(EditContext);
        }

        private async Task Save() {
            await UpdateClientParameters(App.Client);
            await UpdateParameters(Utils.CurrentLastSeason);
            using var tx = await EditContext!.Database.BeginTransactionAsync();
            await BranchesSave(EditContext!);
            await WineAttributesSave(EditContext!);
            await WineCultivationsSave(EditContext!);
            await AreaCommitmentTypesSave(EditContext!);
            await SeasonsSave(EditContext!);
            await ModifiersSave(EditContext!);
            await tx.CommitAsync();
        }

        private async Task FinishEditing() {
            EditContext?.Dispose();
            EditContext = null;
            using var ctx = new AppDbContext();
            await BranchesFinishEditing(ctx);
            await WineAttributesFinishEditing(ctx);
            await WineCultivationsFinishEditing(ctx);
            await AreaCommitmentTypesFinishEditing(ctx);
            await SeasonsFinishEditing(ctx);
            await ModifiersFinishEditing(ctx);
        }


        protected override void ShortcutEdit() {
            if (!EditButton.IsEnabled || EditButton.Visibility != Visibility.Visible)
                return;
            EditButton_Click(null, null);
        }

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

            await InitEditing();

            UnlockInputs();
            UpdateButtons();
        }

        private async 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;

            await FinishEditing();

            using var ctx = new AppDbContext();
            ClearInputStates();
            FillInputs(App.Client, await ctx.Seasons.FindAsync(Utils.CurrentLastSeason));
            LockInputs();
        }

        protected override void ShortcutReset() {
            if (!ResetButton.IsEnabled || ResetButton.Visibility != Visibility.Visible)
                return;
            ResetButton_Click(null, null);
        }

        private async void ResetButton_Click(object? sender, RoutedEventArgs? evt) {
            _branchChanged = false;
            _attrChanged = false;
            _cultChanged = false;
            _modChanged = false;

            await InitEditing();

            using var ctx = new AppDbContext();
            ClearInputStates();
            FillInputs(App.Client, await ctx.Seasons.FindAsync(Utils.CurrentLastSeason));
            UpdateButtons();
        }

        protected override void ShortcutSave() {
            if (!SaveButton.IsEnabled || SaveButton.Visibility != Visibility.Visible)
                return;
            SaveButton_Click(null, null);
        }

        private async void SaveButton_Click(object? sender, RoutedEventArgs? evt) {
            try {
                await Save();
            } 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;

            await FinishEditing();

            using (var ctx = new AppDbContext()) {
                ClearInputStates();
                FillInputs(App.Client, await ctx.Seasons.FindAsync(Utils.CurrentLastSeason));
                LockInputs();
            }

            await App.HintContextChange();
        }

        private void FillInputs(ClientParameters p, Season? s) {
            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;

            TextElementDeliveryNote.Text = p.TextDeliveryNote;
            switch (p.ModeDeliveryNoteStats) {
                case 0: ModeDeliveryNoteNone.IsChecked = true; break;
                case 1: ModeDeliveryNoteGaOnly.IsChecked = true; break;
                case 2: ModeDeliveryNoteShort.IsChecked = true; break;
                case 3: ModeDeliveryNoteFull.IsChecked = true; break;
            }
            TextElementDeliveryConfirmation.Text = p.TextDeliveryConfirmation;
            TextElementCreditNote.Text = p.TextCreditNote;

            ParameterAllowAttrIntoLowerInput.IsChecked = s?.Billing_AllowAttrsIntoLower ?? false;
            ParameterAvoidUnderDeliveriesInput.IsChecked = s?.Billing_AvoidUnderDeliveries ?? false;
            ParameterHonorGebundenInput.IsChecked = s?.Billing_HonorGebunden ?? false;
            ParameterExportEbicsVersion.SelectedIndex = p.ExportEbicsVersion - 3;
            ParameterExportEbicsAddress.SelectedIndex = p.ExportEbicsAddress;

            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;

            p.TextDeliveryNote = TextElementDeliveryNote.Text.Length > 0 ? TextElementDeliveryNote.Text : null;
            p.ModeDeliveryNoteStats = (ModeDeliveryNoteNone.IsChecked == true) ? 0 : (ModeDeliveryNoteGaOnly.IsChecked == true) ? 1 : (ModeDeliveryNoteShort.IsChecked == true) ? 2 : (ModeDeliveryNoteFull.IsChecked == true) ? 3 : 2;
            p.TextDeliveryConfirmation = TextElementDeliveryConfirmation.Text.Length > 0 ? TextElementDeliveryConfirmation.Text : null;
            p.TextCreditNote = TextElementCreditNote.Text.Length > 0 ? TextElementCreditNote.Text : null;

            p.ExportEbicsVersion = ParameterExportEbicsVersion.SelectedIndex + 3;
            p.ExportEbicsAddress = ParameterExportEbicsAddress.SelectedIndex;

            await p.UpdateValues();
        }

        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 Task UpdateParameters(int year) {
            try {
                using var ctx = new AppDbContext();
                if (await ctx.Seasons.FindAsync(year) is not Season s)
                    return;

                s.Billing_AllowAttrsIntoLower = ParameterAllowAttrIntoLowerInput.IsChecked ?? false;
                s.Billing_AvoidUnderDeliveries = ParameterAvoidUnderDeliveriesInput.IsChecked ?? false;
                s.Billing_HonorGebunden = ParameterHonorGebundenInput.IsChecked ?? false;
                ctx.Update(s);
                await ctx.SaveChangesAsync();
            } catch { }
        }
    }
}