using System;
using System.Linq;
using System.Threading.Tasks;
using ChannelAdam.ServiceModel;
using Valg.EVA.Skanning.Commands;
using Valg.EVA.Skanning.Commands.UpdateTransferState;
using Valg.EVA.Skanning.Configuration;
using Valg.EVA.Skanning.Contracts;
using Valg.EVA.Skanning.Contracts.CommonEnums;
using Valg.EVA.Skanning.Diagnostics;
using Valg.EVA.Skanning.Entity.ReadModel;
using Valg.EVA.Skanning.Repositories.Transfers;
using Valg.EVA.Skanning.ServicesModel.Internal.UploadWebService;

namespace Valg.EVA.Skanning.BackgroundServices.CheckTransferStatus
{
    public class CheckTransferStatusService
    {
        private readonly ITransferRepository transferRepository;
        private readonly IUploadWebServiceFactory uploadWebServiceFactory;
        private readonly ICommandDispatcher commandDispatcher;
        private readonly IExternalEndpointsConfiguration externalEndpointsConfiguration;

        public CheckTransferStatusService(ITransferRepository transferRepository,
            IUploadWebServiceFactory uploadWebServiceFactory,
            ICommandDispatcher commandDispatcher,
            IExternalEndpointsConfiguration externalEndpointsConfiguration)
        {
            this.transferRepository = transferRepository;
            this.uploadWebServiceFactory = uploadWebServiceFactory;
            this.commandDispatcher = commandDispatcher;
            this.externalEndpointsConfiguration = externalEndpointsConfiguration;
        }

        public async Task ExecuteAsync()
        {
            var statusCheckExpirationInMinutes = externalEndpointsConfiguration.StatusCheckExpirationInMinutes;
            var transfersEligibleForCheck = await transferRepository.GetTransfersEligibleForStatusCheckAsync(statusCheckExpirationInMinutes).ConfigureAwait(false);

            var tasks = transfersEligibleForCheck.Select(t => PerformStatusCheckAndUpdateTransferAsync(t));
            await Task.WhenAll(tasks.ToArray()).ConfigureAwait(false);
        }

        private async Task PerformStatusCheckAndUpdateTransferAsync(TransferToCheckForStatus transferToCheckForStatus)
        {
            IServiceConsumer<GenericFileUploadWS> service = null;
            checkStatusResponse response = null;
            var checkStatusSuccess = false;
            try
            {
                service = uploadWebServiceFactory.CreateCheckStatusClient();
                
                response = await service.Operations.checkStatusAsync(new checkStatus(transferToCheckForStatus.FileId)).ConfigureAwait(false);
                checkStatusSuccess = true;
            }
            catch (Exception ex)
            {
                
                
                var transferInfo = $"transfer id = {transferToCheckForStatus.TransferId}, currentState = {transferToCheckForStatus.TransferState.Name}, file/batchId = {transferToCheckForStatus.FileId}, lastUpdated = {transferToCheckForStatus.Updated}";
                EvaSkanningEventSource.Log.TransferStatusCheckFailed(transferInfo, ex.ToString(), externalEndpointsConfiguration.TransferWebServiceUrl);
            }
            finally
            {
                service?.Dispose();
            }

            if (checkStatusSuccess)
            {
                var mostRecentTransferState = TransferState.GetTransferStateFromUploadStatusCode(response.status);
                if (mostRecentTransferState == null)
                {
                    EvaSkanningEventSource.Log.UnknownResponseStatusFromTransferStatusCheck(transferToCheckForStatus.TransferId, response.status, 
                        externalEndpointsConfiguration.TransferWebServiceUrl);
                } 
                else if (!mostRecentTransferState.Equals(transferToCheckForStatus.TransferState)
                    && IsAForwardGoingTransferState(mostRecentTransferState, transferToCheckForStatus.TransferState)) 
                {
                    var command = new UpdateTransferStateCommand(mostRecentTransferState, transferToCheckForStatus.Updated, transferToCheckForStatus.TransferState,
                        transferToCheckForStatus.TransferId, "CheckTransferStatusService");
                    await commandDispatcher.DispatchCommandAsync(command);
                }
            }
        }

        private static bool IsAForwardGoingTransferState(TransferState mostRecentTransferState, TransferState previousTransferState)
        {
            var isAValidStateFromServer = mostRecentTransferState.Equals(TransferState.QueuedOnServer)
                                               || mostRecentTransferState.Equals(TransferState.StartedOnServer)
                                               || mostRecentTransferState.Equals(TransferState.FinishedOnServer)
                                               || mostRecentTransferState.Equals(TransferState.ErrorOnServer);
            return isAValidStateFromServer
            && (mostRecentTransferState.UploadStatusCode.GetValueOrDefault() > previousTransferState.UploadStatusCode.GetValueOrDefault());
        }
    }
}
