using C4IT.FASD.Base;
using C4IT.FASD.Cockpit.Communication;
using FasdDesktopUi.Basics.CustomEvents;
using FasdDesktopUi.Basics.Models;
using FasdDesktopUi.Pages.DetailsPage.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using static C4IT.Logging.cLogManager;

namespace FasdDesktopUi.Basics.Services.SupportCase.Controllers
{
    /// <summary>
    /// Used to manage the <see cref="ISupportCase"/> for the UI via the <see cref="SupportCaseProcessor"/>
    /// </summary>
    public class SupportCaseController
    {
        private SupportCaseProcessor _supportCaseProcessor;
        private cF4sdApiSearchResultRelation _focusedRelation;
        private readonly Dictionary<enumFasdInformationClass, cF4sdApiSearchResultRelation> _selectedRelations = new Dictionary<enumFasdInformationClass, cF4sdApiSearchResultRelation>();
        private cHealthCard _selectedHealthcard = null;
        private bool _hasDirectionConnection = false;
        public cSupportCaseDataProvider SupportCaseDataProviderArtifact { get => _supportCaseProcessor?.SupportCaseDataProviderArtifact; }

        internal void SetSupportCaseProcessor(SupportCaseProcessor supportCaseProcessor, IEnumerable<cF4sdIdentityEntry> preselectedIdentities)
        {
            IEnumerable<cF4sdApiSearchResultRelation> preselectedRelations = GetPreselectedRelations(supportCaseProcessor.GetCaseRelations(), preselectedIdentities);

            ResetSelectedRelations(preselectedRelations);
            _hasDirectionConnection = false;

            if (_supportCaseProcessor != null)
            {
                _supportCaseProcessor.AvailableCaseRelationsAdded -= HandleAvailableCaseRelationsAdded;
                _supportCaseProcessor.CaseDataChanged -= HandleCaseDataChanged;
                _supportCaseProcessor.SupportCaseDataProviderArtifact.DirectConnectionHelper.DirectConnectionChanged -= HandleDirectConnectionChanged;
            }

            _supportCaseProcessor = supportCaseProcessor;

            _supportCaseProcessor.AvailableCaseRelationsAdded += HandleAvailableCaseRelationsAdded;
            _supportCaseProcessor.CaseDataChanged += HandleCaseDataChanged;
            _supportCaseProcessor.SupportCaseDataProviderArtifact.DirectConnectionHelper.DirectConnectionChanged += HandleDirectConnectionChanged;
        }

        private IEnumerable<cF4sdApiSearchResultRelation> GetPreselectedRelations
            (ILookup<enumFasdInformationClass, cF4sdApiSearchResultRelation> loadedRelations, IEnumerable<cF4sdIdentityEntry> preselectedIdentities)
        {
            List<cF4sdApiSearchResultRelation> preselectedRelations = new List<cF4sdApiSearchResultRelation>();

            foreach (var relationType in loadedRelations)
            {
                foreach (var relation in relationType)
                {
                    if (relation.Identities.All(ri => preselectedIdentities.Any(i => i.Id == ri.Id)))
                        preselectedRelations.Add(relation);
                }
            }

            return preselectedRelations;
        }

        private void ResetSelectedRelations(IEnumerable<cF4sdApiSearchResultRelation> preselectedRelations)
        {
            try
            {
                _selectedRelations.Clear();

                if (preselectedRelations is null)
                    return;

                foreach (var relation in preselectedRelations)
                {
                    _selectedRelations[cF4sdIdentityEntry.GetFromSearchResult(relation.Type)] = relation;
                }
            }
            catch (Exception ex)
            {
                LogException(ex);
            }
        }

        private void HandleAvailableCaseRelationsAdded(object sender, RelationEventArgs e)
            => AvailableCaseRelationsAdded?.Invoke(this, e);

        private void HandleCaseDataChanged(object sender, SupportCaseDataEventArgs e)
        {
            CaseDataChanged?.Invoke(this, e);

            _ = Task.Run(async () =>
            {
                await UpdateStatusOfSelectedRelations();

                if (!_hasDirectionConnection)
                    await SupportCaseDataProviderArtifact.DirectConnectionHelper.DirectConnectionStartAsync();
            });
        }

        private void HandleDirectConnectionChanged(object sender, EventArgs e)
        {
            _hasDirectionConnection = SupportCaseDataProviderArtifact.DirectConnectionHelper.IsDirectConnectionActive;
        }

        /// <summary>
        /// Updates the currently for a support case relevant and shown relation.
        /// </summary>
        /// <remarks>Raises <see cref="FocusedRelationsChanged"/></remarks>
        internal void UpdateFocusedCaseRelation(cF4sdApiSearchResultRelation relation)
        {
            try
            {
                if (_focusedRelation == relation)
                    return;

                _selectedHealthcard = _supportCaseProcessor.GetHealthcardFor(relation);
                HashSet<string> requiredTables = cHealthCard.GetRequiredTables(_selectedHealthcard);
                _ = Task.Run(async () => await _supportCaseProcessor.LoadSupportCaseDataAsync(relation, requiredTables));

                List<enumFasdInformationClass> requiredInformationClasses = relation.Identities
                    .Where(i => i.Class != enumFasdInformationClass.User)
                    .Select(i => i.Class)
                    .ToList();

                if (relation.Type == enumF4sdSearchResultClass.User)
                    requiredInformationClasses = new List<enumFasdInformationClass>() { enumFasdInformationClass.User };

                if (requiredInformationClasses.Count == 0)
                    requiredInformationClasses.Add(enumFasdInformationClass.User);

                SupportCaseDataProviderArtifact.HealthCardDataHelper.TrySetSelectedHealthcard(requiredInformationClasses);

                _focusedRelation = relation;

                if (!_selectedRelations.Values.Contains(relation) || relation.Type == enumF4sdSearchResultClass.Computer)
                {
                    _hasDirectionConnection = false;
                    SupportCaseDataProviderArtifact.DirectConnectionHelper.Reset();
                }

                _selectedRelations[cF4sdIdentityEntry.GetFromSearchResult(relation.Type)] = relation;
                _ = Task.Run(async () => await UpdateStatusOfSelectedRelations());

                var focusedRelations = new cF4sdApiSearchResultRelation[1] { _focusedRelation };
                FocusedRelationsChanged?.Invoke(this, new RelationEventArgs()
                {
                    Relations = focusedRelations.ToLookup(r => cF4sdIdentityEntry.GetFromSearchResult(r.Type), r => r)
                });
            }
            catch (Exception ex)
            {
                LogException(ex);
            }
        }

        private async Task UpdateStatusOfSelectedRelations()
        {
            const string StatusString = "Status";

            try
            {
                int? agentUserId = null;
                int? agentDeviceId = null;

                // todo these values should not be recieved from the NamedParameters, instead from the relation it self
                if (SupportCaseDataProviderArtifact.NamedParameterEntries.TryGetValue("AgentUserId", out var userNamedParameter))
                {
                    var value = userNamedParameter.GetValue();
                    if (!string.IsNullOrWhiteSpace(value))
                        agentUserId = int.Parse(value);
                }


                // todo these values should not be recieved from the NamedParameters, instead from the relation it self
                if (SupportCaseDataProviderArtifact.NamedParameterEntries.TryGetValue("AgentDeviceId", out var deviceNamedParameter))
                {
                    var value = deviceNamedParameter.GetValue();
                    if (!string.IsNullOrWhiteSpace(value))
                        agentDeviceId = int.Parse(deviceNamedParameter.GetValue());
                }

                foreach (var relationEntry in _selectedRelations)
                {
                    if (relationEntry.Value is null)
                        continue;

                    string statusValue = "Unknown";

                    switch (relationEntry.Key)
                    {
                        case enumFasdInformationClass.Computer:
                            if (!agentDeviceId.HasValue)
                                continue;

                            bool isDeviceOnline = await cFasdCockpitCommunicationBase.Instance.GetAgentOnlineStatus(agentDeviceId.Value);
                            statusValue = isDeviceOnline ? "Online" : "Offline";
                            break;
                        case enumFasdInformationClass.User:
                            if (!agentDeviceId.HasValue || !agentUserId.HasValue)
                                continue;

                            bool isUserOnline = await cFasdCockpitCommunicationBase.Instance.GetAgentOnlineStatus(agentDeviceId.Value, agentUserId.Value);
                            statusValue = isUserOnline ? "Online" : "Offline";
                            break;
                        case enumFasdInformationClass.Ticket:
                            if (relationEntry.Value.Infos != null && relationEntry.Value.Infos.TryGetValue("StatusId", out var statusIdValue))
                                if (Enum.TryParse<enumTicketStatus>(statusIdValue, out var ticketStatus))
                                    statusValue = ticketStatus.ToString();
                            break;

                        case enumFasdInformationClass.VirtualSession:
                        case enumFasdInformationClass.MobileDevice:
                        default:
                            continue;
                    }

                    if (relationEntry.Value.Infos is null)
                        relationEntry.Value.Infos = new Dictionary<string, string>();

                    relationEntry.Value.Infos[StatusString] = statusValue;
                }

                IEnumerable<cHeadingDataModel> newHeadingData = SupportCaseHeadingController.GetHeadingData(_selectedRelations);
                HeadingDataChanged?.Invoke(this, new HeadingDataEventArgs(newHeadingData));
            }
            catch (Exception ex)
            {
                LogException(ex);
            }
        }

        public async Task RefreshDataForCurrentlyFocusedRelationAsync()
        {
            SupportCaseDataProviderArtifact.HealthCardDataHelper.LoadingHelper.LastDataRequest = DateTime.Now;
            await _supportCaseProcessor.UpdateLatestCaseDataFor(_focusedRelation);
        }

        public List<List<cWidgetValueModel>> GetWidgetData()
            => _supportCaseProcessor.GetWidgetData(_focusedRelation);

        public cDetailsPageDataHistoryDataModel GetHistoryData()
            => _supportCaseProcessor.GetHistoryData(_focusedRelation);

        public List<cContainerCollectionData> GetContainerData()
            => _supportCaseProcessor.GetContainerData(_focusedRelation);

        public List<cMenuDataBase> GetMenuBarData()
            => _supportCaseProcessor.GetMenuBarData(_focusedRelation);

        /// <summary>
        /// Raised when the currently for a support case relevant and shown relations have been updated.
        /// </summary>
        public event EventHandler<RelationEventArgs> FocusedRelationsChanged;

        /// <summary>
        /// Raised when newly available relations for a support case were added.
        /// </summary>
        public event EventHandler<RelationEventArgs> AvailableCaseRelationsAdded;

        /// <summary>
        /// Raised when the data set of a support case has changed.
        /// </summary>
        public event EventHandler<SupportCaseDataEventArgs> CaseDataChanged;

        public event EventHandler<HeadingDataEventArgs> HeadingDataChanged;
    }
}
