using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using MoreLinq;
using Valg.EVA.Skanning.Entity.SerializedEntities.CountResultModel;
using Valg.EVA.Skanning.Identity;
using Valg.EVA.Skanning.Repositories.BallotPaper;
using Valg.EVA.Skanning.Repositories.Box;
using Valg.EVA.Skanning.Repositories.Counting;
using Valg.EVA.Skanning.ServicesModel.ElectionConfig.DataProvider;
using Valg.EVA.Skanning.ServicesModel.ElectionConfig.Model;
using Valg.EVA.Skanning.ServicesModel.Internal.CountResult;
using Valg.EVA.Skanning.UserInterfaceElements;


namespace Valg.EVA.Skanning.Sample.Startup.Box
{
    public class BoxControlViewModel : ViewModelBase
    {

        private readonly IBallotPaperRepository _ballotPaperRepository;
        private readonly ICountingRepository _countingRepository;
        private readonly CastVoteMapper _castVoteMapper;
        private readonly IIdentityProvider _identityProvider;
        private readonly ICandidateListDataProvider _candidateListDataProvider;
        private List<Affiliation> _affiliations;

        public BoxControlViewModel(
            NavigateBallotsViewModel navigateBallotsViewModel, IBoxRepository boxRepository, IBallotPaperRepository ballotPaperRepository, ICountingRepository countingRepository,
            CastVoteMapper castVoteMapper, IIdentityProvider identityProvider,
            ICandidateListDataProvider candidateListDataProvider)
        {
            NavigateBallotsViewModel = navigateBallotsViewModel;

            _ballotPaperRepository = ballotPaperRepository;
            _countingRepository = countingRepository;
            _castVoteMapper = castVoteMapper;
            _identityProvider = identityProvider;
            _candidateListDataProvider = candidateListDataProvider;
        }

        public NavigateBallotsViewModel NavigateBallotsViewModel { get; }
        public BoxOverview BoxOverview { get; private set; } = new BoxOverview();

        private void Reset()
        {
            BoxOverview = new BoxOverview();
        }

        private void GetBoxOverviewStatus()
        {
            _affiliations = _candidateListDataProvider.GetAffiliations(_identityProvider.CurrentSelectedRole.Area.ElectionEventId, _identityProvider.CurrentSelectedRole.ContestId);
            var counting = _countingRepository.GetCountingByBoxCountingId(BoxId);
            var mappedBallots = _castVoteMapper.MapToCastVotes(counting, _identityProvider.CurrentSelectedRole.Area.MunicipalityId)
                .Where(BallotIsInCurrentBox).ToList();

            BoxOverview.BallotsScanned = mappedBallots.Count;
            BoxOverview.ProposedRejected = mappedBallots.Count(p => p.ProposedRejection != CastVote.RejectionReasonCode.NotRejected);


            var validBallots = mappedBallots.Where(p => p.ProposedRejection == CastVote.RejectionReasonCode.NotRejected).ToList();
            var affiliations = validBallots.GroupBy(p => p.AffiliationIdentifier.AffiliationId, (s) => new { key = s.AffiliationIdentifier.AffiliationId, value = s });

            foreach (var affiliation in affiliations)
            {
                var personVoteCandidates = affiliation.SelectMany(p => p.value.PersonalVotes).DistinctBy(p => p.RankInAffiliation);
                foreach (var personVoteCandidate in personVoteCandidates)
                {
                    var candidateName = GetCandidateName(personVoteCandidate.RankInAffiliation, affiliation.Key);
                    var votes = affiliation.Sum(p => p.value.PersonalVotes.Count(x => x.RankInAffiliation == personVoteCandidate.RankInAffiliation));
                    BoxOverview.PersonVotesAggregated.Add(new PersonVoteAggregated() { AffiliationId = affiliation.Key, CandidateName = candidateName, Votes = votes });
                }

                BoxOverview.AffiliationVoteAggregated.Add(
                    new AffiliationVoteAggregated()
                    {
                        AffiliationId = affiliation.Key,
                        ValidVotes = validBallots.Count(p => p.AffiliationIdentifier.AffiliationId == affiliation.Key)
                    });

            }

            var allWriteIns = validBallots.SelectMany(ballot => ballot.WriteIns)
                .OrderBy(writeIn=>writeIn.RankInAffiliation)
                .GroupBy(writeInCandidate => $"{writeInCandidate.AffiliationIdentifier.AffiliationId}-{writeInCandidate.RankInAffiliation}");
            foreach (var group in allWriteIns)
            {
                var writeInCandidate = group.ElementAt(0);
                var candidateName = GetCandidateName(writeInCandidate.RankInAffiliation, writeInCandidate.AffiliationIdentifier.AffiliationId);
                var votes = group.Count();
                BoxOverview.WriteInVoteAggregated.Add(new WriteInVoteAggregated() { AffiliationId = writeInCandidate.AffiliationIdentifier.AffiliationId, CandidateName = candidateName, Votes = votes });
            }

            foreach (var ballot in mappedBallots)
            {
                var originalBallotPaperValues = _ballotPaperRepository.GetOriginalBallotPaperValues(ballot.BallotDbId);
                var currentBallotPaperValues = _ballotPaperRepository.GetCurrentBallotPaperValues(ballot.BallotDbId);

                if (!originalBallotPaperValues.NeededVerification && ballot.ProposedRejection == CastVote.RejectionReasonCode.NotRejected)
                {
                    BoxOverview.AutomaticVerifiedOk++;
                }

                if (originalBallotPaperValues.NeededVerification)
                {
                    BoxOverview.ManuallyVerified++;
                }
            }

            BoxOverview.RejectionsAggregated.AddRange(
                mappedBallots.GroupBy(p => p.ProposedRejection).Where(p => p.Key != CastVote.RejectionReasonCode.NotRejected)
                    .Select(group => new RejectionsAggregated() { RejectionReason = group.Key, Ballots = group.Count() }
                    ).OrderByDescending(p => p.Ballots));

            OnPropertyChanged(nameof(BoxOverview));
        }

        private bool BallotIsInCurrentBox(CastVote castVote)
        {
            var pos= _ballotPaperRepository.GetBallotPaperPositionInBox(castVote.BallotDbId, _boxId);
            if (pos != null) return pos.PlacementIndex>0;
            return false;
        }

        private string GetCandidateName(string rankInAffiliation, string affiliationId)
        {
            return _affiliations.FirstOrDefault(p => p.AffiliationId == affiliationId)?.Candidates.FirstOrDefault(p => p.RankInAffiliation == Convert.ToInt32(rankInAffiliation))?.NameLine;
        }

        private int _boxId;
        public int BoxId
        {
            get => _boxId;
            set
            {
                _boxId = value;
                NavigateBallotsViewModel.LoadBox(_boxId);
                Reset();
                GetBoxOverviewStatus();
            }
        }

    }
}

