using System.Collections.Generic;
using System.Linq;
using Valg.EVA.Skanning.Contracts;
using Valg.EVA.Skanning.Contracts.CommonEnums;
using Valg.EVA.Skanning.Entity.ReadModel;
using Newtonsoft.Json;
using Valg.EVA.Skanning.ServicesModel.ElectionConfig.DataProvider;
using Valg.EVA.Skanning.ServicesModel.ElectionConfig.Model;
using Valg.EVA.Skanning.ServicesModel.Internal.BallotNumber;
using Valg.EVA.Skanning.ServicesModel.Internal.Interpreter.Modifications;
using Valg.EVA.Skanning.ServicesModel.Internal.SystemSettings;

namespace Valg.EVA.Skanning.ServicesModel.Internal.Interpreter
{
    public class GeneralBallotInterpreter
    {
        private readonly ISystemSettingsProvider systemSettingsProvider;
        private readonly ICandidateListDataProvider candidateListDataProvider;
        private List<GeneralBallotAffiliation> generalBallotAffiliations;

        public GeneralBallotInterpreter(ISystemSettingsProvider systemSettingsProvider, ICandidateListDataProvider candidateListDataProvider)
        {
            this.systemSettingsProvider = systemSettingsProvider;
            this.candidateListDataProvider = candidateListDataProvider;
        }

        public void CheckIntegrity(BallotPaperInBoxCounting ballotPaperInBoxCounting, IReadOnlyCollection<BallotPaperFieldValue> ballotPaperFieldValues, InterpreterModificationBuffer interpreterModificationBuffer, string boxBarcodeLong, BallotPaperType ballotPaperType)
        {
            if (ballotPaperType.IsGeneralBallotPaperType())
            {
                var ballotNumberField = ballotPaperFieldValues.GetBallotNumberField();
                var partySelectionFields = ballotPaperFieldValues.Where(p => p.IsPartySelectionField()).ToArray();

                if (!ballotNumberField.NeedsVerification)
                {
                    if (!BallotAndCountingElectionEventMatch(boxBarcodeLong, ballotNumberField.Value))
                    {
                        interpreterModificationBuffer.MarkFieldToNeedVerification(ballotNumberField);
                    }
                    else
                    {
                        var possibleAffiliations = GetGeneralBallotAffiliations(ballotPaperType);

                        if (partySelectionFields.Count() == 1)
                        {
                            var selectedParty = partySelectionFields.First();
                            var positionOnBallot = selectedParty.FieldIndex;

                            if (!selectedParty.NeedsVerification && AffiliationExistsInList(positionOnBallot, possibleAffiliations))
                            {
                                var affiliationId = possibleAffiliations.First(a => a.PositionOnBallot == positionOnBallot).AffiliationId;
                                if (AffilationExistInContest(boxBarcodeLong, affiliationId))
                                {
                                    var generatedBallotNumber = BallotNumberGenerator.GenerateBallotNumber(
                                        boxBarcodeLong.GetElectionEventIdFromBarcodeLong(),
                                        boxBarcodeLong.GetElectionGroupIdFromBarcodeLong(),
                                        boxBarcodeLong.GetElectionIdFromBarcodeLong(),
                                        boxBarcodeLong.GetContestIdFromBarcodeLong(),
                                        affiliationId);
                                    interpreterModificationBuffer.GenerateValidBallotNumber(ballotNumberField, generatedBallotNumber);
                                }
                                else
                                {
                                    interpreterModificationBuffer.MarkFieldToNeedVerification(ballotNumberField);
                                }
                            }
                            else
                            {
                                interpreterModificationBuffer.MarkFieldToNeedVerification(ballotNumberField);
                            }
                        }
                        else if (!partySelectionFields.Any())
                        {
                            var generateBlankBallotNumber = BallotNumberGenerator.GenerateBallotNumber(
                                boxBarcodeLong.GetElectionEventIdFromBarcodeLong(),
                                boxBarcodeLong.GetElectionGroupIdFromBarcodeLong(),
                                boxBarcodeLong.GetElectionIdFromBarcodeLong(),
                                boxBarcodeLong.GetContestIdFromBarcodeLong(),
                                Affiliation.Blank.AffiliationShortCode);
                            interpreterModificationBuffer.GenerateValidBallotNumber(ballotNumberField, generateBlankBallotNumber);
                        }
                        else
                        {
                            interpreterModificationBuffer.MarkFieldToNeedVerification(ballotNumberField);
                        }
                    }
                }

                    interpreterModificationBuffer.SetBallotPaperToBeMarkedAsIgnoreCandidates(ballotPaperInBoxCounting);
                    MarkAffiliationFieldsToBeIgnored(interpreterModificationBuffer, partySelectionFields); 

            }
        }

        private bool BallotAndCountingElectionEventMatch(string boxBarcodeLong, string ballotNumber)
        {
            return boxBarcodeLong.Substring(0,10)
                .Equals(ballotNumber.Substring(0,10));
        }

        private bool AffilationExistInContest(string barcodeLong, string affiliationId)
        {
            var affiliation = candidateListDataProvider.GetAffiliations(barcodeLong.GetElectionEventIdFromBarcodeLong(), barcodeLong.GetContestIdFromBarcodeLong()).Exists(a => a.AffiliationShortCode == affiliationId);
            return affiliation;
        }

        private bool AffiliationExistsInList(int? value, IEnumerable<GeneralBallotAffiliation> possibleAffiliations)
        {
            return possibleAffiliations.ToList().Exists(a => a.PositionOnBallot == value);
        }

        private IEnumerable<GeneralBallotAffiliation> GetGeneralBallotAffiliations(BallotPaperType generalBallotType)
        {
            if (generalBallotAffiliations == null)
            {
                var json = string.Empty;

                switch (generalBallotType)
                {
                    case BallotPaperType.MunicipalityGeneral:
                        json = systemSettingsProvider.GetGeneralMunicipalityAffiliationList();
                        break;

                    case BallotPaperType.CountyGeneral:
                        json = systemSettingsProvider.GetGeneralCountyAffiliationList();
                        break;

                    case BallotPaperType.OsloDistrictGeneral:
                        json = systemSettingsProvider.GetGeneralOsloDistrictAffiliationList();
                        break;

                    case BallotPaperType.ParliamentGeneral:
                        json = systemSettingsProvider.GetGeneralParliamentAffiliationList();
                        break;
                }

                generalBallotAffiliations = JsonConvert.DeserializeObject<List<GeneralBallotAffiliation>>(json);
            }
            return generalBallotAffiliations;
        }

        private void MarkAffiliationFieldsToBeIgnored(InterpreterModificationBuffer interpreterModificationBuffer, BallotPaperFieldValue[] affiliationFields)
        {
            foreach (var affiliationField in affiliationFields)
            {
                affiliationField.NeedsVerification = false;
                interpreterModificationBuffer.AddModifiedField(affiliationField);
            }
        }

        public struct GeneralBallotAffiliation
        {
            public int PositionOnBallot { get; set; }

            public string AffiliationId { get; set; }

            public string AffiliationName { get; set; }
        }
    }
}
