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

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

        private bool IsUpdatingGradation = false;
        private readonly DispatcherTimer Timer;

        public DeliveryAdminWindow() {
            InitializeComponent();
            RequiredInputs = new Control[] {
                MgNrInput, MemberInput,
                SortIdInput, WineVarietyInput,
                GradationOeInput, GradationKmwInput,
                WineQualityLevelInput,
                WineOriginInput, WineKgInput
            };
            ExemptInputs = new Control[] {
                LsNrInput, DateInput, TimeInput, BranchInput,
                MemberAddressField
            };

            Timer = new DispatcherTimer();
            Timer.Tick += new EventHandler(OnSecondPassed);
            Timer.Interval = new TimeSpan(0, 0, 1);
        }

        private void Window_Loaded(object sender, RoutedEventArgs evt) {
            OnSecondPassed(null, null);
            Timer.Start();
        }

        private void OnSecondPassed(object? sender, EventArgs? evt) {
            var now = DateTime.Now;
            TimeInput.Text = now.ToString("HH:mm");
            DateInput.Text = now.ToString("dd.MM.yyyy");
        }

        protected override void UpdateButtons() {

        }

        protected override async Task RenewContext() {
            await base.RenewContext();
            Utils.RenewItemsSource(MemberInput, await Context.Members.OrderBy(m => m.FamilyName).ThenBy(m => m.GivenName).ToListAsync(), i => (i as Member)?.MgNr);
            Utils.RenewItemsSource(BranchInput, await Context.Branches.OrderBy(b => b.Name).ToListAsync(), i => (i as Branch)?.ZwstId);
            BranchInput.SelectedItem = BranchInput.ItemsSource.Cast<Branch>().First(b => b.ZwstId == App.ZwstId);
            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(WineQualityLevelInput, await Context.WineQualityLevels.ToListAsync(), i => (i as WineQualLevel)?.QualId);
            Utils.RenewItemsSource(ModifiersInput, await Context.Modifiers.Where(m => m.Season.Year == 2022).OrderBy(m => m.Name).ToListAsync(), i => (i as Modifier)?.ModId);
            Utils.RenewItemsSource(WineOriginInput, (await Context.WineOrigins.ToListAsync()).OrderByDescending(o => o.SortKey).ThenBy(o => o.HkId), i => (i as WineOrigin)?.HkId);
            var kgList = await Context.WbKgs.Select(k => k.AtKg).OrderBy(k => k.Name).Cast<object>().ToListAsync();
            kgList.Insert(0, new NullItem());
            Utils.RenewItemsSource(WineKgInput, kgList, i => (i as AT_Kg)?.KgNr);
            if (WineKgInput.SelectedItem == null) WineKgInput.SelectedIndex = 0;
            UpdateWineQualityLevels();
            UpdateRdInput();
            await UpdateLsNr();
        }

        private void MgNrInput_TextChanged(object sender, TextChangedEventArgs evt) {
            var valid = InputTextChanged((TextBox)sender, Validator.CheckMgNr);
            MemberInput.SelectedItem = valid ? Context.Members.Find(int.Parse(MgNrInput.Text)) : null;
        }

        private void MgNrInput_LostFocus(object sender, RoutedEventArgs evt) {
            var valid = InputLostFocus((TextBox)sender, Validator.CheckMgNr);
            MemberInput.SelectedItem = valid ? Context.Members.Find(int.Parse(MgNrInput.Text)) : null;
        }

        private void MemberInput_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
            var m = MemberInput.SelectedItem as Member;
            if (m != null) MgNrInput.Text = m.MgNr.ToString();
            MemberAddressField.Text = m?.FullAddress;
            if (m == null) {
                UnsetOriginalValue(WineKgInput);
                WineKgInput.SelectedIndex = 0;
            } else {
                SetOriginalValue(WineKgInput, m.DefaultKg);
                WineKgInput.SelectedItem = m.DefaultKg;
            }
        }

        private async Task UpdateLsNr() {
            var branch = (Branch)BranchInput.SelectedItem;
            var date = DateOnly.ParseExact(DateInput.Text, "dd.MM.yyyy");
            var lnr = await Context.NextLNr(date);
            LsNrInput.Text = $"{date:yyyyMMdd}{branch.ZwstId}{lnr:000}";
        }

        private void DateInput_TextChanged(object sender, TextChangedEventArgs evt) {
            UpdateLsNr().GetAwaiter().GetResult();
        }

        private void UpdateWineVariety(bool valid) {
            if (valid) {
                var text = SortIdInput.Text;
                WineVarietyInput.SelectedItem = Context.WineVarieties.Find(text[0..2]);
                if (text.Length >= 3) {
                    AttributesInput.UnSelectAll();
                    AttributesInput.SelectedItems.Add(Context.WineAttributes.Find(text[2..]));
                    SortIdInput.Text = text[0..2];
                }
            } else {
                WineVarietyInput.SelectedItem = null;
                AttributesInput.UnSelectAll();
            }
        }

        private void SortIdInput_TextChanged(object sender, TextChangedEventArgs evt) {
            UpdateWineVariety(InputTextChanged((TextBox)sender, Validator.CheckSortId));
        }

        private void SortIdInput_LostFocus(object sender, RoutedEventArgs evt) {
            UpdateWineVariety(InputLostFocus((TextBox)sender, Validator.CheckSortId));
        }

        private void WineVarietyInput_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
            var s = WineVarietyInput.SelectedItem as WineVar;
            if (s != null) SortIdInput.Text = s.SortId;
        }

        private void UpdateWineQualityLevels() {
            if (!GetInputValid(GradationKmwInput)) {
                UnsetOriginalValue(WineQualityLevelInput);
                WineQualityLevelInput.ItemsSource = null;
                return;
            }
            var kmw = double.Parse(GradationKmwInput.Text);
            WineQualityLevelInput.ItemsSource = Context.WineQualityLevels.Where(q => q.MinKmw == null || q.MinKmw <= kmw).ToList();
            var qual = Context.WineQualityLevels.Where(q => !q.IsPredicate && (q.MinKmw == null || q.MinKmw <= kmw)).OrderBy(q => q.MinKmw).LastOrDefault();
            SetOriginalValue(WineQualityLevelInput, qual);
            if (WineQualityLevelInput.SelectedItem == null || (WineQualityLevelInput.SelectedItem is WineQualLevel selected && !selected.IsPredicate)) {
                WineQualityLevelInput.SelectedItem = qual;
            }
        }

        private void UpdateGradationKmw() {
            IsUpdatingGradation = true;
            var caret = GradationKmwInput.CaretIndex;
            GradationKmwInput.Text = $"{Utils.OeToKmw(double.Parse(GradationOeInput.Text)):#.0}";
            GradationKmwInput.CaretIndex = caret;
            IsUpdatingGradation = false;
        }

        private void UpdateGradationOe() {
            IsUpdatingGradation = true;
            var caret = GradationOeInput.CaretIndex;
            GradationOeInput.Text = $"{Utils.KmwToOe(double.Parse(GradationKmwInput.Text)):#}";
            GradationOeInput.CaretIndex = caret;
            IsUpdatingGradation = false;
        }

        private void GradationOeInput_TextChanged(object sender, TextChangedEventArgs evt) {
            var valid = InputTextChanged((TextBox)sender, Validator.CheckGradatoinOe);
            if (!IsUpdatingGradation) {
                if (valid) UpdateGradationKmw();
                else if (GradationOeInput.Text.Length == 0) GradationKmwInput.Text = "";
                if (valid || GradationOeInput.Text.Length == 0) UpdateWineQualityLevels();
            }
        }

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

        private void GradationKmwInput_TextChanged(object sender, TextChangedEventArgs evt) {
            var valid = InputTextChanged((TextBox)sender, Validator.CheckGradationKmw);
            if (!IsUpdatingGradation) {
                if (valid) UpdateGradationOe();
                else if (GradationKmwInput.Text.Length == 0) GradationOeInput.Text = "";
                if (valid || GradationKmwInput.Text.Length == 0) UpdateWineQualityLevels();
            }
        }

        private void GradationKmwInput_LostFocus(object sender, RoutedEventArgs evt) {
            if (GradationKmwInput.Text.EndsWith(",")) GradationKmwInput.Text += "0";
            InputLostFocus((TextBox)sender, Validator.CheckGradationKmw);
            if (GradationKmwInput.Text.Length != 0 && !GradationKmwInput.Text.Contains(','))
                GradationKmwInput.Text += ",0";
        }

        private void AttributesInput_SelectionChanged(object sender, ItemSelectionChangedEventArgs evt) {

        }

        private void ModifiersInput_SelectionChanged(object sender, ItemSelectionChangedEventArgs evt) {

        }

        private void UpdateWineOrigin() {
            var qual = WineQualityLevelInput.SelectedItem as WineQualLevel;
            var kg = (WineKgInput.SelectedItem as AT_Kg)?.WbKg;
            if (qual == null || kg == null) {
                WineOriginInput.IsEnabled = true;
                return;
            }
            WineOriginInput.IsEnabled = false;
            var o = kg.Origin;
            while (o != null && o.Level > qual.OriginLevel) o = o.Parent;
            WineOriginInput.SelectedItem = o;
        }

        private void WineQualityLevelInput_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
            UpdateWineOrigin();
        }

        private void UpdateRdInput() {
            if (WineKgInput.SelectedItem is AT_Kg kg) {
                var list = Context.WbRde.Where(r => r.KgNr == kg.KgNr).OrderBy(r => r.Name).Cast<object>().ToList();
                list.Insert(0, new NullItem());
                Utils.RenewItemsSource(WineRdInput, list, i => ((i as WbRd)?.KgNr, (i as WbRd)?.RdNr));
                if (WineRdInput.SelectedItem == null) WineRdInput.SelectedIndex = 0;
                WineRdInput.IsEnabled = list.Count > 1;
            } else {
                WineRdInput.ItemsSource = null;
                WineRdInput.IsEnabled = false;
            }
        }

        private void WineKgInput_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
            UpdateWineOrigin();
            UpdateRdInput();
        }
    }
}