using Valg.EVA.Skanning.Contracts;
using Valg.EVA.Skanning.Contracts.CommonEnums;
using Valg.EVA.Skanning.Entity.ReadModel;
using Valg.EVA.Skanning.Entity.SqlConnection;
using Valg.EVA.Skanning.Entity.WriteModel;
using Valg.EVA.Skanning.Repositories.BallotPaper;
using Valg.EVA.Skanning.Repositories.Counting;
using Valg.EVA.Skanning.ServicesModel.Internal;
using System;
using System.Linq;
using Valg.EVA.Skanning.Diagnostics;

namespace Valg.EVA.Skanning.Commands.SaveScannedBallotPaper
{
    public class SaveScannedBallotPaperCommandHandler : IHandleCommandsWithReturnValue<SaveScannedBallotPaperCommand, SaveScannedBallotPaperCommandResult>
    {
        private readonly IBallotPaperWritableRepository ballotPaperWritableRepository;
        private readonly ICountingRepository countingRepository;
        private readonly TransactionScopeFactory transactionScopeFactory;
        private readonly CountingUniqueIdCreator countingUniqueIdCreator;

        public SaveScannedBallotPaperCommandHandler(IBallotPaperWritableRepository ballotPaperWritableRepository,
            ICountingRepository countingRepository, TransactionScopeFactory transactionScopeFactory, CountingUniqueIdCreator countingUniqueIdCreator)
        {
            this.ballotPaperWritableRepository = ballotPaperWritableRepository;
            this.countingRepository = countingRepository;
            this.transactionScopeFactory = transactionScopeFactory;
            this.countingUniqueIdCreator = countingUniqueIdCreator;
        }

        public void HandleCommand(SaveScannedBallotPaperCommand command)
        {
            CommandResult = new SaveScannedBallotPaperCommandResult();
            try
            {
                var activeCounting = GetActiveCounting(command);
                if (activeCounting == null)
                {
                    CommandResult.BoxCountingDoesNotExist = true;
                }
                else
                {
                    var boxCounting = countingRepository.GetBoxCounting(command.BarcodeLong, command.BarcodeShort, activeCounting.Id);

                    if (boxCounting == null || boxCounting.Id != command.BoxCountingId)
                    {
                        CommandResult.BoxCountingDoesNotExist = true;
                    }
                    else
                    {
                        var acceptingScans = boxCounting.State == BoxCountingState.Scanning
                                             && boxCounting.Modified == command.BoxCountingConcurrencyToken;

                        CommandResult.BoxCountingIsNotAcceptingScans = !acceptingScans;

                        if (acceptingScans)
                        {
                            UpdateDatabase(command, boxCounting);
                        }
                    }
                }
            }
            catch (Exception e)
            {
                EvaSkanningEventSource.Log.SaveScannedBallotFailed(e.ToString());
                CommandResult.HasDatabaseErrors = true;
                CommandResult.DatabaseError = e;
            }
        }

        private void UpdateDatabase(SaveScannedBallotPaperCommand command, BoxCounting boxCounting)
        {
            using (var scope = transactionScopeFactory.GetTransactionScope())
            {
                var isValid = command.ScannedFields.All(f => !f.NeedsVerification);
                if (!command.ScannedFields.Any())
                {
                    isValid = false;
                }

                var ballotPaperInsert = new BallotPaperInsert
                {
                    BallotPaperType = command.BallotPaperType,
                    BoxCountingId = boxCounting.Id,
                    IsValid = isValid,
                    NeededVerification = !isValid,
                    CreatedBy = command.CreatedBy,
                    ValueSourceType = ValueSourceType.Scan,
                    ValidBallotId = command.ValidBallotId,
                    ScannedOrder =  command.ScannedOrder
                };

                var ballotPaperId = ballotPaperWritableRepository.InsertBallotPaper(ballotPaperInsert);

                foreach (var scannedField in command.ScannedFields)
                {
                    if (scannedField.IsBallotIdField())
                    {
                        throw new InvalidOperationException("ScannedField of type BallotId should not be saved to the Field/FieldValue tables");
                    }

                    var ballotPaperFieldInsert = new BallotPaperFieldInsert
                    {
                        BallotPaperId = ballotPaperId,
                        FieldIndex = scannedField.FieldIndex,
                        FieldType = scannedField.FieldType,
                        ValueSourceTypeId = ValueSourceType.Scan,
                        Value = scannedField.Value,
                        CreatedBy = command.CreatedBy,
                        NeedsVerification = scannedField.NeedsVerification
                    };

                    ballotPaperWritableRepository.InsertBallotPaperField(ballotPaperFieldInsert);
                }

                ballotPaperWritableRepository.InsertBallotPaperImages(new BallotPaperImageInsert
                {
                    BallotPaperId = ballotPaperId,
                    ImageBack = command.ScannedImages.Back,
                    ImageFront = command.ScannedImages.Front,
                    RotationBack = command.ScannedImages.BackRotationDegrees,
                    RotationFront = command.ScannedImages.FrontRotationDegrees
                });

                CommandResult.BallotPaperId = ballotPaperId;

                scope.Complete();
            }
        }

        private ActiveCounting GetActiveCounting(SaveScannedBallotPaperCommand command)
        {
            var uniqueId = countingUniqueIdCreator.Create(command.BarcodeLong, command.BarcodeShort);

            return countingRepository.GetActiveCounting(
                uniqueId,
                command.CountingEmlAreaContextId,
                command.CountingIdBoxWasScannedFor);
        }

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