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.Internal.Interpreter.Modifications;
using Valg.EVA.Skanning.ServicesModel.Internal.SystemSettings;

namespace Valg.EVA.Skanning.ServicesModel.Internal.Interpreter
{
    public class MultipleStampsInterpreter
    {
        private readonly ISystemSettingsProvider systemSettingsProvider;
        private List<MultipleStamp> multipleStamps;
        private readonly int stampFillPercentageMinimum;

        public MultipleStampsInterpreter(ISystemSettingsProvider systemSettingsProvider)
        {
            this.systemSettingsProvider = systemSettingsProvider;

            stampFillPercentageMinimum = systemSettingsProvider.GetStampFillPercentageMinimum();
        }

        public void CheckIntegrity(IReadOnlyCollection<BallotPaperFieldValue> ballotPaperFieldValues, InterpreterModificationBuffer interpreterModificationBuffer, string boxBarcodeLong, BallotPaperType ballotPaperType)
        {
            if (HasMultipleStamps(ballotPaperType))
            {
                var stampField = ballotPaperFieldValues.GetStampField();
                var bypassAllValidityChecks = (!stampField.NeedsVerification && stampFillPercentageMinimum == 0); 

                if (!bypassAllValidityChecks)
                {
                    ValidateMultipleStamps(ballotPaperFieldValues, interpreterModificationBuffer, boxBarcodeLong, stampField);
                }
            }
        }

        private void ValidateMultipleStamps(IReadOnlyCollection<BallotPaperFieldValue> ballotPaperFieldValues, InterpreterModificationBuffer interpreterModificationBuffer, string boxBarcodeLong,
            BallotPaperFieldValue stampField)
        {
            var possibleStamps = GetMultipleStamps();
            var multipleStampFields = ballotPaperFieldValues.Where(p => p.IsMultipleStampsField()).ToArray();

            if (multipleStampFields.Length == 1)
            {
                var multipleStampField = multipleStampFields.Single();
                var positionOnBallot = multipleStampField.FieldIndex;

                if (StampExistsInList(positionOnBallot, possibleStamps))
                {
                    var stampPositionContestId = possibleStamps.First(a => a.PositionOnBallot == positionOnBallot).ContestId;
                    if (stampPositionContestId != boxBarcodeLong.GetContestIdFromBarcodeLong())
                    {
                        interpreterModificationBuffer.MarkFieldToNeedVerification(stampField);
                    }
                    else
                    {
                        interpreterModificationBuffer.MarkStampFieldToNotNeedVerification(stampField, multipleStampField.Value);
                    }
                }
                else
                {
                    interpreterModificationBuffer.MarkFieldToNeedVerification(stampField);
                }
            }
            else
            {
                interpreterModificationBuffer.MarkFieldToNeedVerification(stampField);
            }
        }

        private static bool HasMultipleStamps(BallotPaperType ballotPaperType)
        {
            return (ballotPaperType == BallotPaperType.OsloDistrictGeneral || 
                    ballotPaperType == BallotPaperType.OsloDistrict || 
                    ballotPaperType == BallotPaperType.OsloDistrictBlank);
        }

        private bool StampExistsInList(int? value, IEnumerable<MultipleStamp> possibleStamps)
        {
            return possibleStamps.ToList().Exists(a => a.PositionOnBallot == value);
        }

        private IEnumerable<MultipleStamp> GetMultipleStamps()
        {
            if (multipleStamps == null)
            {
                var json = systemSettingsProvider.GetOsloDistrictList();
                multipleStamps = JsonConvert.DeserializeObject<List<MultipleStamp>>(json);
            }
            return multipleStamps;
        }

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

            public string ContestId { get; set; }

            public string ContestName { get; set; }
        }
    }
}
