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

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

        private bool IsUpdatingGradation = false;
        private bool IsRefreshingInputs = false;
        private readonly bool IsReceipt = false;
        private readonly Member? Member = null;
        private readonly DispatcherTimer Timer;
        private List<string> TextFilter = new();
        private readonly RoutedCommand CtrlF = new();

        public DeliveryAdminWindow() {
            InitializeComponent();
            CtrlF.InputGestures.Add(new KeyGesture(Key.F, ModifierKeys.Control));
            CommandBindings.Add(new CommandBinding(CtrlF, FocusSearchInput));
            RequiredInputs = new Control[] {
                MgNrInput, MemberInput,
                SortIdInput, WineVarietyInput,
                GradationOeInput, GradationKmwInput,
                WineQualityLevelInput,
                WineOriginInput, WineKgInput,
                WeightInput
            };
            ExemptInputs = new Control[] {
                SearchInput, TodayOnlyInput, SeasonOnlyInput,
                DeliveryList, DeliveryPartList,
                MemberAddressField,
                LsNrInput, DateInput, TimeInput, BranchInput,
            };

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

            InitializeDelayTimer(SearchInput, SearchInput_TextChanged);
            SearchInput.TextChanged -= SearchInput_TextChanged;
        }

        public DeliveryAdminWindow(bool receipt) : this() {
            IsReceipt = receipt;
            Title = "Übernahme - Elwig";
            TodayOnlyInput.IsChecked = true;
        }

        public DeliveryAdminWindow(int mgnr) : this() {
            Member = Context.Members.Find(mgnr) ?? throw new ArgumentException("MgNr argument has invalid value");
            Title = $"Lieferungen - {Member.AdministrativeName} - Elwig";
        }

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

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

        protected override void UpdateButtons() {

        }


        private async Task RefreshDeliveryList() {
            await RefreshDeliveryListQuery();
        }

        private async Task RefreshDeliveryListQuery(bool updateSort = false) {
            IQueryable<Delivery> deliveryQuery = Context.Deliveries;
            if (Member != null) {
                deliveryQuery = deliveryQuery.Where(d => d.MgNr == Member.MgNr);
            }
            if (TodayOnlyInput.IsChecked == true) {
                deliveryQuery = deliveryQuery
                    .Where(d => (d.DateString == Utils.Today.ToString("yyyy-MM-dd") && d.TimeString.CompareTo("03:00:00") > 0) || 
                                (d.DateString == Utils.Today.AddDays(1).ToString("yyyy-MM-dd") && d.TimeString.CompareTo("03:00:00") <= 0));
            } else if (SeasonOnlyInput.IsChecked == true) {
                deliveryQuery = deliveryQuery.Where(d => d.Year == Utils.CurrentLastSeason);
            }

            List<Delivery> deliveries = await deliveryQuery.OrderByDescending(d => d.DateString).ThenByDescending(d => d.TimeString).ToListAsync();
            if (TextFilter.Count > 0) {
                deliveries = deliveries
                    .ToDictionary(d => d, d => d.SearchScore(TextFilter))
                    .OrderByDescending(a => a.Value)
                    .ThenBy(a => a.Key.DateTime)
                    .Where(a => a.Value > 0)
                    .Select(a => a.Key)
                    .ToList();
            }

            Utils.RenewItemsSource(DeliveryList, deliveries, d => ((d as Delivery)?.Year, (d as Delivery)?.DId), !updateSort);
            if (deliveries.Count == 1)
                DeliveryList.SelectedIndex = 0;

            RefreshInputs();
        }

        private void RefreshDeliveryPartList(bool updateSort = false) {
            if (DeliveryList.SelectedItem is not Delivery d) {
                DeliveryPartList.ItemsSource = null;
                return;
            }

            Utils.RenewItemsSource(DeliveryPartList, d.Parts.OrderBy(p => p.DPNr), i => ((i as DeliveryPart)?.Year, (i as DeliveryPart)?.DId, (i as DeliveryPart)?.DPNr));
            if (d.Parts.Count == 1)
                DeliveryPartList.SelectedIndex = 0;

            RefreshInputs();
        }

        protected override async Task RenewContext() {
            await base.RenewContext();
            await RefreshDeliveryList();
            RefreshDeliveryPartList();
            var d = DeliveryList.SelectedItem as Delivery;
            var y = (d?.Year ?? Utils.CurrentLastSeason);
            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);
            Utils.SelectComboBoxItem(BranchInput, i => (i as Branch)?.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.Year == y).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 FocusSearchInput(object sender, RoutedEventArgs evt) {
            if (!IsEditing && !IsCreating) {
                SearchInput.Focus();
                SearchInput.SelectAll();
            }
        }

        private void RefreshInputs() {
            IsRefreshingInputs = true;
            ClearInputStates();
            if (DeliveryList.SelectedItem is Delivery d) {
                Utils.RenewItemsSource(ModifiersInput, Context.Modifiers.Where(m => m.Year == d.Year).OrderBy(m => m.Name).ToList(), i => (i as Modifier)?.ModId);
                FillInputs(d);
            } else {
                Utils.RenewItemsSource(ModifiersInput, Context.Modifiers.Where(m => m.Year == Utils.CurrentLastSeason).OrderBy(m => m.Name).ToList(), i => (i as Modifier)?.ModId);
                ClearOriginalValues();
                ClearInputs();
            }
            IsRefreshingInputs = false;
            GC.Collect();
        }

        private void FillInputs(Delivery d) {
            ClearOriginalValues();

            MgNrInput.Text = d.MgNr.ToString();
            LsNrInput.Text = d.LsNr;
            DateInput.Text = d.Date.ToString("dd.MM.yyyy");
            TimeInput.Text = d.Time?.ToString("HH:mm") ?? "";
            CommentInput.Text = d.Comment ?? "";
            Utils.RenewItemsSource(DeliveryPartList, d.Parts, i => ((i as DeliveryPart)?.Year, (i as DeliveryPart)?.DId, (i as DeliveryPart)?.DPNr));
            if (DeliveryPartList.SelectedItem == null && DeliveryPartList.ItemsSource != null) {
                DeliveryPartList.SelectedIndex = 0;
            }

            var p = DeliveryPartList.SelectedItem as DeliveryPart;
            SortIdInput.Text = p?.SortId ?? "";
            Utils.SelectCheckComboBoxItems(AttributesInput, p?.Attributes, i => (i as WineAttr)?.AttrId);
            GradationKmwInput.Text = (p != null) ? $"{p.Kmw:N1}" : "";
            Utils.SelectComboBoxItem(WineQualityLevelInput, q => (q as WineQualLevel)?.QualId, p?.QualId);
            Utils.SelectComboBoxItem(WineKgInput, k => (k as AT_Kg)?.KgNr, p?.KgNr);
            Utils.SelectComboBoxItem(WineRdInput, r => (r as WbRd)?.RdNr, p?.RdNr);
            Utils.SelectComboBoxItem(WineOriginInput, r => (r as WineOrigin)?.HkId, p?.HkId);
            WeightInput.Text = p?.Weight.ToString() ?? "";
            ManualWeighingInput.IsChecked = p?.ManualWeighing ?? false;
            GerebeltGewogenInput.IsChecked = p?.IsGerebelt ?? false;
            Utils.SelectCheckComboBoxItems(ModifiersInput, p?.Modifiers, i => (i as Modifier)?.ModId);
            PartCommentInput.Text = p?.Comment ?? "";
            TemperatureInput.Text = p?.Temperature?.ToString() ?? "";
            AcidInput.Text = p?.Acid?.ToString() ?? "";
            LesewagenInput.IsChecked = p?.IsLesewagen ?? false;
            HandPickedInput.IsChecked = p?.IsHandPicked;

            FillOriginalValues();
        }

        private async void SearchInput_TextChanged(object sender, RoutedEventArgs evt) {
            TextFilter = SearchInput.Text.ToLower().Split(" ").ToList().FindAll(e => e.Length > 0);
            await RefreshDeliveryListQuery(true);
        }

        private async void TodayOnlyInput_Changed(object sender, RoutedEventArgs evt) {
            if (TodayOnlyInput.IsChecked == true && SeasonOnlyInput.IsChecked == true) SeasonOnlyInput.IsChecked = false;
            await RefreshDeliveryListQuery();
        }

        private async void SeasonOnlyInput_Changed(object sender, RoutedEventArgs evt) {
            if (!IsInitialized) return;
            if (SeasonOnlyInput.IsChecked == true && TodayOnlyInput.IsChecked == true) TodayOnlyInput.IsChecked = false;
            await RefreshDeliveryListQuery();
        }

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

        private void DeliveryPartList_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
           if (!IsRefreshingInputs) RefreshInputs();
        }

        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.GetWineQualityLevel(kmw).GetAwaiter().GetResult();
            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 = (IsEditing || IsCreating);
                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();
            UpdateAbgewertet();
        }

        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 = (IsEditing || IsCreating) && list.Count > 1;
            } else {
                WineRdInput.ItemsSource = null;
                WineRdInput.IsEnabled = false;
            }
        }

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

        private void UpdateAbgewertet() {
            var qual = WineQualityLevelInput.SelectedItem as WineQualLevel;
            if (qual == null) {
                AbgewertetInput.IsChecked = false;
                return;
            }
            var defQual = Context.GetWineQualityLevel(double.Parse(GradationKmwInput.Text)).GetAwaiter().GetResult();
            AbgewertetInput.IsChecked = !qual.IsPredicate && defQual != qual;
        }
    }
}