using System;
using System.Data;
using Valg.EVA.Skanning.Contracts.CommonEnums;
using Valg.EVA.Skanning.Diagnostics;
using Valg.EVA.Skanning.Entity.SqlConnection;
using Valg.EVA.Skanning.Repositories.BallotPaper;

namespace Valg.EVA.Skanning.Commands.UpdateBallotPaperSeenStatus
{
    public class UpdateBallotPaperSeenStatusCommandHandler : IHandleCommandsWithReturnValue<UpdateBallotPaperSeenStatusCommand, UpdateBallotPaperSeenStatusCommandResult>
    {
        private readonly IBallotPaperRepository repository;
        private readonly IBallotPaperValuesUpdater ballotPaperValuesUpdater;
        private readonly TransactionScopeFactory transactionScopeFactory;

        public UpdateBallotPaperSeenStatusCommandHandler(IBallotPaperRepository repository, IBallotPaperValuesUpdater ballotPaperValuesUpdater, TransactionScopeFactory transactionScopeFactory)
        {
            this.repository = repository;
            this.ballotPaperValuesUpdater = ballotPaperValuesUpdater;
            this.transactionScopeFactory = transactionScopeFactory;
        }

        public void HandleCommand(UpdateBallotPaperSeenStatusCommand command)
        {
            CommandResult = new UpdateBallotPaperSeenStatusCommandResult();

            var currentValues = repository.GetCurrentBallotPaperValues(command.BallotPaperId);
            if (command.RevokeSeenStatus && currentValues.IsValid)
            {
                CommandResult.BallotWasAlreadyValidAndNotRevoked = true;
                return;
            }

            using (var scope = transactionScopeFactory.GetTransactionScope())
            {
                var required = new RequiredInputBallotPaperValues
                {
                    BallotPaperConcurrencyToken = command.BallotPaperConcurrencyToken,
                    BallotPaperId = command.BallotPaperId,
                    ChangedBy = command.ChangedBy,
                    ValueSourceType = command.ValueSource ?? ValueSourceType.Verify
                };

                DateTime? newSeenValue = null;
                if (!command.RevokeSeenStatus)
                {
                    newSeenValue = DateTime.Now;
                }

                var optional = new CopiedUnlessSpecifiedValues
                {
                    SeenIsSpecified = true,
                    Seen = newSeenValue
                };

                var commit = true;
                try
                {
                    ballotPaperValuesUpdater.UpdateBallotPaperValues(required, optional);

                    CommandResult.BallotWasRevoked = command.RevokeSeenStatus;
                }
                catch (DBConcurrencyException ex)
                {
                    EvaSkanningEventSource.Log.ConcurrencyError(GetType().ToString(), ex.ToString());

                    if (!command.RevokeSeenStatus)
                    {
                        throw;
                    }
                    
                    commit = false;
                }

                if (commit)
                {
                    scope.Complete();
                }
            }
        }

        public UpdateBallotPaperSeenStatusCommandResult CommandResult { get; private set; }
    }
}
