using C4IT.FASD.Base;
using C4IT.MultiLanguage;
using F4SD_AdaptableIcon;
using F4SD_AdaptableIcon.Enums;
using FasdDesktopUi.Basics.CustomEvents;
using FasdDesktopUi.Basics.Models;
using FasdDesktopUi.Basics.UiActions;
using FasdDesktopUi.Basics.UserControls.SearchResult;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using static C4IT.Logging.cLogManager;

namespace FasdDesktopUi.Basics.UserControls
{
    public partial class CustomSearchResultCollection : UserControl
    {
        private Dictionary<enumFasdInformationClass, SearchResultCategory> _searchCategories;

        #region IndexOfSelectedResultItem

        private int _indexOfSelectedResultItem = int.MinValue;

        public int IndexOfSelectedResultItem
        {
            get { return _indexOfSelectedResultItem; }
            set
            {
                if (_indexOfSelectedResultItem == value)
                    return;

                if (value < -1)
                    _indexOfSelectedResultItem = int.MinValue;
                else if (value == -1)
                    _indexOfSelectedResultItem = GetSearchResultCount() - 1;
                else if (value == GetSearchResultCount())
                    _indexOfSelectedResultItem = 0;
                else
                    _indexOfSelectedResultItem = value;

                HandleIndexOfSelectedResultItemChanged();
            }
        }

        #endregion

        private cMenuDataBase _lastSelectedMenuData = null;

        public bool ShowSearchResultDetails
        {
            get { return cFasdCockpitConfig.Instance.ShowSearchResultDetails; }
            set
            {
                cFasdCockpitConfig.Instance.ShowSearchResultDetails = value;
                cFasdCockpitConfig.Instance.Save("ShowSearchResultDetails");
            }
        }

        public CustomSearchResultCollection()
        {
            InitializeComponent();
        }

        protected override void OnInitialized(EventArgs e)
        {
            base.OnInitialized(e);
            AddHandler(CustomEventManager.IndexChangedEvent, new CustomEventManager.IndexChangedHandlerDelegate(UpdateIndexOfSelectedResultItem));
            AddHandler(CustomEventManager.MenuDataChangedEvent, new CustomEventManager.MenuDataChangedHandlerDelegate((_, args) => _lastSelectedMenuData = args.MenuData));
        }

        private void UpdateIndexOfSelectedResultItem(object sender, IndexEventArgs args)
        {
            try
            {
                if (!(args.OriginalSource is SearchResultCategory category))
                    return;

                var indexOfSender = _searchCategories.Values.ToList().IndexOf(category);

                int updatedIndex = args.NewValue;
                for (int i = 0; i < indexOfSender; i++)
                {
                    updatedIndex += _searchCategories.Values.ToList()[i].GetItemCount();
                }

                IndexOfSelectedResultItem = updatedIndex;
            }
            catch (Exception ex)
            {
                LogException(ex);
            }

        }

        public bool IsEmpty() => MainStackPanel.Children.Count == 0;

        internal int GetSearchResultCount()
        {
            int itemCount = 0;

            foreach (var resultCategory in _searchCategories.Values)
            {
                itemCount += resultCategory.GetItemCount();
            }

            return itemCount;
        }

        private void HandleIndexOfSelectedResultItemChanged()
        {
            try
            {
                int totalResultItemCount = 0;

                foreach (var category in _searchCategories.Values)
                {
                    int categoryItemCount = category.GetItemCount();

                    if (IndexOfSelectedResultItem > totalResultItemCount)
                        category.HiglightItemAt(-1);

                    if (IndexOfSelectedResultItem < totalResultItemCount + categoryItemCount)
                        category.HiglightItemAt(IndexOfSelectedResultItem - totalResultItemCount);

                    totalResultItemCount += categoryItemCount;
                }
            }
            catch (Exception E)
            {
                LogException(E);
            }
        }

        public void SetHeaderText(string headerText, bool hideDetailsCheckbox = false)
        {
            try
            {
                HeaderTextBlock.Text = headerText;
                HeaderTextBlock.Visibility = string.IsNullOrEmpty(headerText) ? Visibility.Collapsed : Visibility.Visible;
                ShowDetailsCheckBox.Visibility = string.IsNullOrEmpty(headerText) || hideDetailsCheckbox ? Visibility.Collapsed : Visibility.Visible;
            }
            catch (Exception E)
            {
                LogException(E);
            }
        }

        private void ProcessNewItems()
        {
            UpdateCornerRadius();
            UpdateSearchResultDetailsShown(ShowSearchResultDetails);
        }

        public void ShowLoadingTextItem(string itemText)
        {
            try
            {
                ClearControl();

                var menuItem = new CustomMenuItem(false) { MenuData = new cMenuDataLoading(itemText) };
                MainStackPanel.Children.Add(menuItem);

                ProcessNewItems();
            }
            catch (Exception E)
            {
                LogException(E);
            }
        }

        public void ShowSearchResults(cFilteredResults SearchResults, string headerText, ISearchUiProvider searchUiProvider)
        {
            try
            {
                ClearControl();
                SetHeaderText(headerText);

                if (SearchResults?.Results is null || SearchResults.Results.Count == 0)
                    ProcessNoSearchResults();
                else
                    ProcessSearchResults();

                ProcessNewItems();
            }
            catch (Exception E)
            {
                LogException(E);
            }

            void ProcessNoSearchResults()
            {
                var resultItemControl = new CustomMenuItem(false)
                {
                    MenuData = new cMenuDataBase()
                    {
                        MenuText = cMultiLanguageSupport.GetItem("SearchBar.NoResults"),
                        MenuIcon = new IconData(enumInternIcons.menuBar_search_noResults)
                    }
                };
                MainStackPanel.Children.Add(resultItemControl);
            }

            void ProcessSearchResults()
            {
                if (!_searchCategories.TryGetValue(enumFasdInformationClass.Main, out var mainCategory))
                    return;

                List<cMenuDataBase> menuDataList = new List<cMenuDataBase>();
                mainCategory.UpdateSearchResults(SearchResults.Results, searchUiProvider);
                UpdatePendingInformationClasses(new HashSet<enumFasdInformationClass>());
            }
        }

        public void ShowSearchHistory()
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                ClearControl();
                SetHeaderText(cMultiLanguageSupport.GetItem("Searchbar.History.Title"), true);

                if (cSearchManager.Instance.HistoryList.Count <= 0)
                {
                    this.Visibility = Visibility.Collapsed;
                    return;
                }

                this.Visibility = Visibility.Visible;

                foreach (var historyEntry in cSearchManager.Instance.HistoryList)
                {
                    var menuData = new cMenuDataBase() { MenuText = historyEntry.DisplayText, UiAction = new cUiProcessSearchHistoryEntry(historyEntry) };
                    if (historyEntry.isSeen)
                    {
                        menuData.MenuIcon = new IconData(MaterialIcons.MaterialIconType.ic_visibility);
                        menuData.MenuIconSize = 0.75;
                    }

                    MainStackPanel.Children.Add(new CustomMenuItem(true) { MenuData = menuData });
                }

                ProcessNewItems();
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                LogMethodEnd(CM);
            }
        }

        public void UpdateSearchRelations(ILookup<enumFasdInformationClass, cMenuDataBase> relations)
        {
            try
            {
                if (_searchCategories is null)
                    return;

                int indexOfMenuData = -1;
                int indexOfMenuDataCategory = -1;
                foreach (var relation in relations)
                {
                    if (!_searchCategories.TryGetValue(relation.Key, out var category))
                        continue;

                    category.UpdateSearchRelations(relation);

                    if (indexOfMenuData != -1)
                        continue;

                    indexOfMenuData = GetIndexOf(relation.Select(value => value.Data), _lastSelectedMenuData?.Data);

                    if (indexOfMenuData != -1)
                        indexOfMenuDataCategory = GetIndexOf(_searchCategories.Values, category);
                }

                ProcessNewItems();

                if (indexOfMenuData == -1)
                    return;

                IndexOfSelectedResultItem = indexOfMenuData + _searchCategories.Take(indexOfMenuDataCategory).Sum(category => category.Value.GetItemCount());
            }
            catch (Exception ex)
            {
                LogException(ex);
            }

            // even if not ideal, seems to be the most performant solution for retrieving the index of IEnumerable
            int GetIndexOf(IEnumerable<object> data, object value)
            {
                int index = 0;

                foreach (var item in data)
                {
                    if (item.Equals(value))
                        return index;
                    index++;
                }

                return -1;
            }
        }

        private void ClearControl()
        {
            MainStackPanel.Children.Clear();

            var mainCategory = new SearchResultCategory() { IsPending = false };
            _searchCategories = new Dictionary<enumFasdInformationClass, SearchResultCategory>() { [enumFasdInformationClass.Main] = mainCategory };
            MainStackPanel.Children.Add(mainCategory);

            _lastSelectedMenuData = null;
            IndexOfSelectedResultItem = int.MinValue;
            HandleIndexOfSelectedResultItemChanged();
        }

        public void SelectCurrentResultItem()
        {
            try
            {
                int totalResultItemCount = 0;

                foreach (var category in _searchCategories.Values)
                {
                    int categoryItemCount = category.GetItemCount();

                    if (IndexOfSelectedResultItem < totalResultItemCount + categoryItemCount)
                    {
                        category.SelectItemAt(IndexOfSelectedResultItem - totalResultItemCount);
                        return;
                    }

                    totalResultItemCount += categoryItemCount;
                }
            }
            catch (Exception E)
            {
                LogException(E);
            }
        }

        private void UpdateCornerRadius()
        {
            try
            {
                const double cornerRadius = 10;

                if (MainStackPanel.Children.Count <= 0)
                    return;

                if (MainStackPanel.Children[0] is CustomMenuItem resultItemFirst)
                {
                    var tempCornerRadius = resultItemFirst.MenuItemBorder.CornerRadius;
                    tempCornerRadius.TopLeft = cornerRadius;
                    tempCornerRadius.TopRight = cornerRadius;
                    resultItemFirst.MenuItemBorder.CornerRadius = tempCornerRadius;
                }

                if (MainStackPanel.Children[MainStackPanel.Children.Count - 1] is CustomMenuItem resultItemLast)
                {
                    var tempCornerRadiusLast = resultItemLast.MenuItemBorder.CornerRadius;
                    tempCornerRadiusLast.BottomLeft = cornerRadius;
                    tempCornerRadiusLast.BottomRight = cornerRadius;
                    resultItemLast.MenuItemBorder.CornerRadius = tempCornerRadiusLast;
                }
            }
            catch (Exception E)
            {
                LogException(E);
            }
        }

        private void ShowDetailsCheckBox_Checked(object sender, RoutedEventArgs e)
        {
            if (!(sender is CheckBox checkBox))
                return;

            cFasdCockpitConfig.Instance.ShowSearchResultDetails = checkBox.IsChecked == true;
            cFasdCockpitConfig.Instance.Save("ShowSearchResultDetails");
            UpdateSearchResultDetailsShown(checkBox.IsChecked);
        }

        private void UpdateSearchResultDetailsShown(bool? showDetailHeadings)
        {
            try
            {
                if (showDetailHeadings is null)
                    return;

                if (MainStackPanel?.Children is null || MainStackPanel.Children.Count <= 0)
                    return;

                foreach (var searchResult in MainStackPanel.Children.OfType<CustomMenuItem>())
                {
                    searchResult.ShowDetailHeadings(showDetailHeadings.Value);
                }

                foreach (var searchResult in MainStackPanel.Children.OfType<SearchResultCategory>())
                {
                    searchResult.ShowDetailHeadings(showDetailHeadings.Value);
                }
            }
            catch (Exception ex)
            {
                LogException(ex);
            }
        }

        public void SetPendingInformationClasses(HashSet<enumFasdInformationClass> informationClasses)
        {
            try
            {
                MainStackPanel.Children.Clear();
                _searchCategories = new Dictionary<enumFasdInformationClass, SearchResultCategory>();

                foreach (var informationClass in informationClasses.Reverse())
                {
                    var searchResultCategory = new SearchResultCategory()
                    {
                        Title = string.Format(cMultiLanguageSupport.GetItem("Searchbar.Relations.SearchFor"), informationClass),
                        IsPending = true,
                        TitleIcon = GetIconFrom(informationClass),
                        Margin = new Thickness(0, 7.5, 0, 0)
                    };

                    _searchCategories.Add(informationClass, searchResultCategory);
                    MainStackPanel.Children.Add(searchResultCategory);
                }
            }
            catch (Exception ex)
            {
                LogException(ex);
            }

            IconData GetIconFrom(enumFasdInformationClass informationClass)
            {
                switch (informationClass)
                {
                    case enumFasdInformationClass.Computer:
                        return new IconData(enumInternIcons.misc_computer);
                    case enumFasdInformationClass.User:
                        return new IconData(enumInternIcons.misc_user);
                    case enumFasdInformationClass.Ticket:
                        return new IconData(enumInternIcons.menuBar_mail);
                    case enumFasdInformationClass.VirtualSession:
                        return new IconData(MaterialIcons.MaterialIconType.ic_cloud_queue);
                    case enumFasdInformationClass.MobileDevice:
                        return new IconData(MaterialIcons.MaterialIconType.ic_smartphone);
                    default:
                        return new IconData(enumInternIcons.f4sd);
                }
            }
        }

        public void UpdatePendingInformationClasses(HashSet<enumFasdInformationClass> informationClasses)
        {
            try
            {
                if (_searchCategories is null)
                    return;

                var finishedInformationClasses = _searchCategories.Where(category => !informationClasses.Contains(category.Key)).Select(category => category.Value);
                foreach (var pendingCategory in finishedInformationClasses)
                {
                    pendingCategory.IsPending = false;

                    if (pendingCategory.GetItemCount() == 0)
                        pendingCategory.Margin = new Thickness(0);
                    else
                        pendingCategory.Margin = new Thickness(0, 7.5, 0, 0);

                }

                if (IndexOfSelectedResultItem != int.MinValue)
                    return;

                if (_searchCategories.Values.Any(c => c.IsPending))
                    return;

                IndexOfSelectedResultItem = _searchCategories.Values.Sum(c => c.GetItemCount()) - 1;

            }
            catch (Exception ex)
            {
                LogException(ex);
            }
        }
    }
}
