using System.Linq;

using Unity.Interception.Utilities;
using Valg.EVA.Skanning.ServicesModel.ElectionConfig.DataProvider;
using Valg.EVA.Skanning.ServicesModel.ElectionConfig.Model;
using Valg.EVA.Skanning.Verify.Startup.VerifyBallotPaper.BallotNumber;
using Valg.EVA.Skanning.Verify.Startup.VerifyBallotPaper.Messaging.Events;
using Valg.EVA.Skanning.Verify.Startup.VerifyBallotPaper.RenumberStrikeout;
using Valg.EVA.Skanning.Verify.Startup.VerifyBallotPaper.VerifiableViewModelsCollection;

namespace Valg.EVA.Skanning.Verify.Startup.VerifyBallotPaper
{
    public class AutoVerifyHandler
    {
        private readonly IElectionConfigDataProvider electionConfigDataProvider;
        private readonly ICandidateListDataProvider candidateListDataProvider;

        public AutoVerifyHandler(IElectionConfigDataProvider electionConfigDataProvider, ICandidateListDataProvider candidateListDataProvider)
        {
            this.electionConfigDataProvider = electionConfigDataProvider;
            this.candidateListDataProvider = candidateListDataProvider;
        }

        public bool AutoVerifyFieldsIfApplicable(BallotNumberUpdate ballotNumberUpdate,
            IVerifiableViewModelsCollectionVisitor verifiableViewModelsCollectionVisitor)
        {
            bool autoVerifyHasBeenPerformed = false;
            if (AutoVerifyIsApplicable(ballotNumberUpdate))
            {
                var viewModelsTargetedForAutoVerify = verifiableViewModelsCollectionVisitor.GetTargetedViewModelsForAutoVerify();
                var affiliation = candidateListDataProvider.GetAffiliation(ballotNumberUpdate.ElectionEventId, ballotNumberUpdate.ContestId, ballotNumberUpdate.AffiliationShortCode);

                var renumbersAppearingTwiceOrMore = viewModelsTargetedForAutoVerify
                    .Where(v => v.Renumber != null)
                    .Select(v => v.Renumber.Value)
                    .GroupBy(renumberValue => renumberValue)
                    .Where(g => g.Count() > 1)
                    .Select(g => g.Key)
                    .ToList();

                var isTargetedForRemoval = viewModelsTargetedForAutoVerify.Where(v =>
                    v.Renumber == null
                    || renumbersAppearingTwiceOrMore.Contains(v.Renumber.Value)
                    || ProposedRenumberIsOutsideMaxCandidateBounds(v, affiliation)
                    || ProposedStrikeoutIsInConflict(v)
                    || ProposedRenumberIsEqualToCandidateRank(v));

                viewModelsTargetedForAutoVerify
                    .Except(isTargetedForRemoval)
                    .ForEach(v => ApplyAutoVerification(v,verifiableViewModelsCollectionVisitor));
                if (viewModelsTargetedForAutoVerify.Length > 0)
                {
                    autoVerifyHasBeenPerformed = true;
                }

            }
            return autoVerifyHasBeenPerformed;
            
        }

        private bool AutoVerifyIsApplicable(BallotNumberUpdate ballotNumberUpdate)
        {
            if (!ballotNumberUpdate.IgnoreCandidates
                && !ballotNumberUpdate.IsBlank)
            {
                var countMetrics = electionConfigDataProvider.GetCountMetricsForContest(ballotNumberUpdate.ElectionEventId, ballotNumberUpdate.ContestId);
                if (countMetrics.HasRenumbering.GetValueOrDefault())
                {
                    return true;
                }
            }

            return false;
        }

        private bool ProposedRenumberIsEqualToCandidateRank(RenumberStrikeoutViewModel renumberStrikeoutViewModel)
        {
            if (renumberStrikeoutViewModel.Renumber.HasValue)
            {
                return renumberStrikeoutViewModel.Renumber.Value == renumberStrikeoutViewModel.CandidateRank;
            }

            return false;
        }

        private bool ProposedRenumberIsOutsideMaxCandidateBounds(RenumberStrikeoutViewModel renumberStrikeoutViewModel, Affiliation affiliaton)
        {
            return renumberStrikeoutViewModel.Renumber < 1 
                || renumberStrikeoutViewModel.Renumber > affiliaton.Candidates.Count;
        }

        private bool ProposedStrikeoutIsInConflict(RenumberStrikeoutViewModel renumberStrikeoutViewModel)
        {
            return renumberStrikeoutViewModel.Renumber.HasValue && renumberStrikeoutViewModel.HasCandidateBeenStrikedOut;
        }

        private void ApplyAutoVerification(RenumberStrikeoutViewModel viewModel, IVerifiableViewModelsCollectionVisitor verifiableViewModelsCollectionVisitor)
        {
            verifiableViewModelsCollectionVisitor.SuspendFocusMove();
            viewModel.RaiseRenumberSelectedEvent();
            verifiableViewModelsCollectionVisitor.ResumeFocusMove();
        }
    }
}
