using System.Collections.Concurrent;
using qdr.fnd.core.pqueue.Enums;
using qdr.fnd.core.pqueue.Exceptions;
using Quadarax.Foundation.Core.Data;
using Quadarax.Foundation.Core.Logging;

namespace qdr.fnd.core.pqueue.Storages
{
    public class MemoryStorageProvider : BaseStorageProvider
    {

        private ConcurrentDictionary<string, IPsQueueItem> _items = new ConcurrentDictionary<string, IPsQueueItem>();

        /// <inheritdoc />
        public MemoryStorageProvider(ILogger? logger) : base(logger)
        {
        }

        #region Overrides of BaseStorageProvider

        #endregion

        #region Overrides of DisposableObject

        /// <inheritdoc />
        protected override void OnDisposing()
        {
            _items.Clear();
            _items = null!;
        }

        #endregion

        #region Overrides of BaseStorageProvider

        /// <inheritdoc />
        protected override string OnCreateItem(string id, string owner, string ownerRef, string providerClass, int initialAttempts,
            DateTime created, string s)
        {
            var item = new PsQueueItem(id, owner, ownerRef, providerClass, created)
            {
                AttemptsLeft = initialAttempts
            };
            if(!_items.TryAdd(id, item))
                Log?.Log(LogSeverityEnum.Warn, $"Item id={id} already exists in storage (id conflict)");
            return id;
        }

        /// <inheritdoc />
        protected override void OnDeleteItem(string itemId)
        {
            if(!_items.TryRemove(itemId, out _))
                Log?.Log(LogSeverityEnum.Warn, $"Item id={itemId} wasn't delete from storage (not exists)");
        }

        /// <inheritdoc />
        protected override void OnUpdateItemPriority(string itemId, int priority)
        {
            var item = (PsQueueItem)GetItem(itemId);
            if (item == null)
                throw new PSStorageException(PSStorageException.ErrorCodes.PsStorageItemNotFound, itemId);
            item.Priority = priority;
        }

        /// <inheritdoc />
        protected override void OnUpdateItemValidity(string itemId, DateTime validForm, DateTime? validTo)
        {
            var item = (PsQueueItem)GetItem(itemId);
            if (item == null)
                throw new PSStorageException(PSStorageException.ErrorCodes.PsStorageItemNotFound, itemId);
            item.ValidFrom = validForm;
            item.ValidTo = validTo;
        }

        /// <inheritdoc />
        protected override void OnUpdateItemStatus(string itemId, PsStatusEnum? status, bool isTransient)
        {
            var item = (PsQueueItem)GetItem(itemId);
            if (item == null)
                throw new PSStorageException(PSStorageException.ErrorCodes.PsStorageItemNotFound, itemId);
            if (status!= null)
                item.Status = status.Value;
            item.IsStatusTransient = isTransient;
        }

        /// <inheritdoc />
        protected override void OnUpdateItemAttempts(string itemId, int attemptsLeft)
        {
            var item = (PsQueueItem)GetItem(itemId);
            if (item == null)
                throw new PSStorageException(PSStorageException.ErrorCodes.PsStorageItemNotFound, itemId);
            
            item.AttemptsLeft = attemptsLeft;
        }

        /// <inheritdoc />
        protected override void OnUpdateItemStarted(string itemId, DateTime? started)
        {
            var item = (PsQueueItem)GetItem(itemId);
            if (item == null)
                throw new PSStorageException(PSStorageException.ErrorCodes.PsStorageItemNotFound, itemId);
            
            item.Started = started;
        }

        /// <inheritdoc />
        protected override void OnUpdateItemFinished(string itemId, DateTime? finished)
        {
            var item = (PsQueueItem)GetItem(itemId);
            if (item == null)
                throw new PSStorageException(PSStorageException.ErrorCodes.PsStorageItemNotFound, itemId);
            
            item.Finished = finished;
        }

        /// <inheritdoc />
        protected override void OnCreateItemAttribute(string itemId, string name, string? value, string valueTypeClass, bool isOutput)
        {
            var item = (PsQueueItem)GetItem(itemId);
            if (item == null)
                throw new PSStorageException(PSStorageException.ErrorCodes.PsStorageItemNotFound, itemId);
            var type = Type.GetType(valueTypeClass);

            ((List<ITypedArgument>)item.Arguments).Add(new TypedArgument(name, value, type,isOutput));
        }

        /// <inheritdoc />
        protected override void OnUpdateItemAttribute(string itemId, string name, string value)
        {
            var item = (PsQueueItem)GetItem(itemId);
            if (item == null)
                throw new PSStorageException(PSStorageException.ErrorCodes.PsStorageItemNotFound, itemId);
            
            var attr = item.Arguments.FirstOrDefault(a => string.Equals(a.Name , name,StringComparison.CurrentCultureIgnoreCase));
            if(attr== null)
                throw new PSStorageException(PSStorageException.ErrorCodes.PsStorageItemArgumentNotFound, itemId, name);
            ((TypedArgument)attr).Value = value; 
            
        }

        /// <inheritdoc />
        protected override void OnDeleteItemAttribute(string itemId, string name)
        {
            var item = (PsQueueItem)GetItem(itemId);
            if (item == null)
                throw new PSStorageException(PSStorageException.ErrorCodes.PsStorageItemNotFound, itemId);
            
            var attr = item.Arguments.FirstOrDefault(a => string.Equals(a.Name , name,StringComparison.CurrentCultureIgnoreCase));
            if(attr== null)
                throw new PSStorageException(PSStorageException.ErrorCodes.PsStorageItemArgumentNotFound, itemId, name);
            ((List<ITypedArgument>)item.Arguments).Remove(attr);
        }

        /// <inheritdoc />
        protected override void OnAppendLog(string itemId, string message, int code, ErrorLevelEnum messageType)
        {
            var item = (PsQueueItem)GetItem(itemId);
            if (item == null)
                throw new PSStorageException(PSStorageException.ErrorCodes.PsStorageItemNotFound, itemId);

            ((List<IError>)item.Log).Add(new Error(message, code, messageType, DateTime.Now));
        }

        /// <inheritdoc />
        protected override void OnAppendStatusJournal(string itemId, PsStatusEnum status, bool isTransient, string message)
        {
            var item = (PsQueueItem)GetItem(itemId);
            if (item == null)
                throw new PSStorageException(PSStorageException.ErrorCodes.PsStorageItemNotFound, itemId);

            ((List<PsStatusHistory>)item.StatusJournal).Add(new PsStatusHistory(status, isTransient,message));
        }

        /// <inheritdoc />
        protected override IQueryable<IPsQueueItem> OnQuery()
        {
            return _items.Values.AsQueryable();
        }

        /// <inheritdoc />
        protected override IPsQueueItem OnGetItem(string itemId)
        {
            _items.TryGetValue(itemId,out var item);
            if (item== null)
                throw new PSStorageException(PSStorageException.ErrorCodes.PsStorageItemNotFound, itemId);
            return item;
        }

        /// <inheritdoc />
        public override bool ContainsItem(string itemId)
        {
            return _items.ContainsKey(itemId);
        }

        #endregion
    }
}
