using System;
using System.Collections.Generic;
using CareFusion.Lib.StorageSystem.Wwks2;
using CareFusion.Lib.StorageSystem.Wwks2.Messages;
using CareFusion.Lib.StorageSystem.Wwks2.Messages.Stock;
using CareFusion.Lib.StorageSystem.Wwks2.Types;
using StorageSystem.Stock;

namespace CareFusion.Lib.StorageSystem.Stock
{
    public sealed class StockUpdate : IStockUpdate
    {
        private readonly List<Criteria> _criteria = new List<Criteria>();

        private readonly Pack _pack = new Pack();
        private bool _isRunning = false;
        private readonly object _lock = new object();
        private readonly MessageDispatcher _messageDispatcher;
        private readonly int _defaultMessageTimeout;
        private readonly int _destinationId;
        private readonly int _source;


        public StockUpdate(MessageDispatcher messageDispatcher, int defaultMessageTimeout, int destinationId, int source)
        {
            _messageDispatcher = messageDispatcher ?? throw new ArgumentNullException(nameof(messageDispatcher));
            _defaultMessageTimeout = defaultMessageTimeout;
            _destinationId = destinationId;
            _source = source;
        }

        public void AddCriteria(string articleId = null, string batchNumber = null, string externalId = null, ulong? packId = null,
            string stockLocationId = null, string machineLocation = null, string serialNumber = null)
        {
            lock (_lock)
            {
                if (_isRunning)
                {
                    throw new ApplicationException("Adding criteria to an already running update is not supported.");
                }
            }
            _criteria.Add(Criteria.Create(articleId, batchNumber, externalId, packId, stockLocationId, machineLocation,
                serialNumber));
        }

        public FieldUpdate<string>? BatchNumber { get; set; }
    

        public StockUpdateResult Execute()
        {
            lock (_lock)
            {
                if (_isRunning)
                {
                    return StockUpdateResult.Failure("Already running");
                }
                _isRunning = true;
            }

            // Nothing to update
            if (BatchNumber is null)
            {
                return StockUpdateResult.Failure("Nothing to update");
            }

            _pack.BatchNumber = BatchNumber.Value.IsSet() ?
                BatchNumber.Value.Get() : string.Empty;



            var envelope = new StockUpdateRequestEnvelope()
            {
                StockUpdateRequest = new StockUpdateRequest
                {
                    Id = MessageId.Next,
                    Destination = _destinationId,
                    Source = _source,
                    Criteria = _criteria,
                    Pack = _pack,
                }
            };

            var message = (StockUpdateResponse)_messageDispatcher.SendAndWaitForResponse(envelope,
                envelope.StockUpdateRequest.Id,
                typeof(StockUpdateResponse), _defaultMessageTimeout);

            if (message is null)
            {
                throw new ApplicationException("Waiting for the message 'StockUpdateResponse' failed");
            }

            if (Enum.TryParse(message.Details.Status, out StockUpdateStatus status))
            {
                return status == StockUpdateStatus.Accepted ?
                    StockUpdateResult.Success() : StockUpdateResult.Failure(message.Details.Reason);
            }

            return StockUpdateResult.Failure("Failed to parse response");

        }


    }
}