using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Aurora.Proto.Events;

namespace Aurora.Services.EventManager
{
    public class EventManager : BaseService<EventManager>
    {
        #region Fields
        private Dictionary<string, List<EventType>> _subscriptionList;
        private Dictionary<string, Action<BaseEvent>> _actionList;

        #endregion Fields
        public EventManager()
        {
            _subscriptionList = new Dictionary<string, List<EventType>>();
            _actionList = new Dictionary<string, Action<BaseEvent>>();
        }

        #region Private Methods


        #endregion Private Methods

        #region Public Methods
        /// <summary>
        /// Get the list of event type subscriptions for a given session id.
        /// </summary>
        /// <param name="session">Session Id</param>
        /// <returns></returns>
        public List<EventType> GetSubscriptionList(string session)
        {
            List<EventType> eventList = new List<EventType>();
            if (_subscriptionList.ContainsKey(session))
            {
                _subscriptionList.TryGetValue(session, out eventList);
            }

            return eventList;
        }

        /// <summary>
        /// Get the number of event subscriptions for a given session
        /// </summary>
        /// <param name="session">Session Id</param>
        /// <returns></returns>
        public int GetSubscriptionCount(string session)
        {
            List<EventType> eventList = new List<EventType>();
            if (_subscriptionList.ContainsKey(session))
            {
                _subscriptionList.TryGetValue(session, out eventList);
            }

            return eventList.Count();
        }

        /// <summary>
        /// Add a new subscription 
        /// </summary>
        /// <param name="session"></param>
        /// <param name="type"></param>
        public bool AddSubscription(string session, EventType type)
        {
            bool success = false;
            lock (_subscriptionList)
            {
                if (!_subscriptionList.ContainsKey(session))
                {
                    //Add session to subscription list
                    List<EventType> eventList = new List<EventType>();
                    eventList.Add(type);
                    _subscriptionList.Add(session, eventList);
                    success = true;
                }
                else
                {
                    _subscriptionList.TryGetValue(session, out List<EventType> eventList);
                    if (eventList != null)
                    {
                        eventList.Add(type);
                        success = true;
                    }
                }
            }

            return success;
        }

        /// <summary>
        /// Add a list of subscriptions. This unsubscribes from unused events.
        /// </summary>
        /// <param name="session">The browser session id.</param>
        /// <param name="types">The list of event types to subscribe to.</param>
        public void AddSubscriptionList(string session, List<EventType> types)
        {
            RemoveAllSubscriptions(session);

            foreach (EventType e in types)
            {
                AddSubscription(session, e);
            }
        }

        /// <summary>
        /// Unsubscribe from a given event type.
        /// </summary>
        /// <param name="session">Session Id</param>
        /// <param name="type">Event Type to be removed</param>
        public void RemoveSubscription(string session, EventType type)
        {
            lock (_subscriptionList)
            {
                if (_subscriptionList.ContainsKey(session))
                {
                    List<EventType> eventTypeList;
                    _subscriptionList.TryGetValue(session, out eventTypeList);
                    if (eventTypeList != null && eventTypeList.Contains(type))
                    {
                        eventTypeList.Remove(type);
                        //base.LogInformation(string.Format("Subscription removed for event type {0} subscription on session {1}", type.ToString(), session));
                    }
                }
            }
        }

        public void RemoveSubscriptionList(string session, List<EventType> types)
        {
            foreach (EventType e in types)
            {
                RemoveSubscription(session, e);
            }
        }

        /// <summary>
        /// Remove all subscriptons for a given session.
        /// </summary>
        /// <param name="session">Session Id</param>
        public void RemoveAllSubscriptions(string session)
        {
            if (_subscriptionList.ContainsKey(session))
            {
                _subscriptionList.Remove(session);
            }
        }

        public void AddEventHandler(Action<BaseEvent> action, string sessionId)
        {
            lock (_actionList)
            {
                _actionList.Add(sessionId, action);
            }
        }

        public void RemoveEventHandler(string sessionId)
        {
            _actionList.Remove(sessionId);
        }

        public void FireEvent(BaseEvent bEvent)
        {
            Dictionary<string, Action<BaseEvent>> actionsCopy = new Dictionary<string, Action<BaseEvent>>();
            //Copy actions list
            lock (_actionList)
            {
                foreach (KeyValuePair<string, Action<BaseEvent>> pair in _actionList)
                {
                    actionsCopy.Add(pair.Key, pair.Value);
                }
            }

            lock (_subscriptionList)
            {
                foreach (KeyValuePair<string, List<EventType>> pair in _subscriptionList)
                {
                    Task.Delay(1000);
                    //If action list contains an action for id, invoke
                    if (actionsCopy.ContainsKey(pair.Key))
                    {
                        actionsCopy.TryGetValue(pair.Key, out Action<BaseEvent> action);
                        Task executionTask = new Task(() => action(bEvent));

                        //Execute task with exception handler
                        executionTask.ContinueWith((Task task) =>
                        {
                            var exception = executionTask.Exception;
                            System.Diagnostics.Debug.WriteLine(string.Format("SERVER --- Exception occurred firing event"));
                            this._actionList.Remove(pair.Key);
                        },
                            TaskContinuationOptions.OnlyOnFaulted);

                        executionTask.Start();
                    }
                }
            }

        }

        #endregion Public Methods

    }
}