LocalGovIMS_localgov-ims/src/BusinessLogic/Validators/Payment/CheckDigitStrategies/WeightedSumStrategy.cs
using BusinessLogic.Extensions;
using BusinessLogic.Interfaces.Validators;

namespace BusinessLogic.Validators.Payment
{
    public class WeightedSumStrategy : ICheckDigitStrategy
    {
        private int[] _weightings;
        private string _referenceToCheck;
        private int _weightedSum = 0;
        private int _remainder = 0;
        private string _calculatedCheckDigit;

        public WeightedSumStrategy()
        {
        }

        public void Validate(CheckDigitStrategyArgs args)
        {
            ValidateArgs(args);

            Initialise(args);

            CalculateCheckDigit(args);

            ValidateCheckDigit(args);
        }

        private void ValidateArgs(CheckDigitStrategyArgs args)
        {
            if (string.IsNullOrEmpty(args.CheckDigitConfiguration.Weightings))
                throw new CheckDigitValidationException("No check digit weightings specified");
        }

        private void Initialise(CheckDigitStrategyArgs args)
        {
            ExtractWeightings(args);
            PrepareReferenceToCheck(args);
        }

        private void ExtractWeightings(CheckDigitStrategyArgs args)
        {
            _weightings = args.CheckDigitConfiguration.WeightingsArray();
        }

        private void PrepareReferenceToCheck(CheckDigitStrategyArgs args)
        {
            _referenceToCheck = args.Reference;

            ApplySourceSubstitutions(args);
        }

        private void ApplySourceSubstitutions(CheckDigitStrategyArgs args)
        {
            if (string.IsNullOrEmpty(args.CheckDigitConfiguration.SourceSubstitutions))
                return;

            // TODO: This could be done in an extension method
            _referenceToCheck = args.CheckDigitConfiguration.SourceSubstitutions + args.Reference.Substring(args.CheckDigitConfiguration.SourceSubstitutions.Length);
        }

        private void CalculateCheckDigit(CheckDigitStrategyArgs args)
        {
            GenerateWeightedSum();
            CalculateRemainder(args);
            ApplySubtraction(args);
            ApplyResultSubstitutions(args);
        }

        private void GenerateWeightedSum()
        {
            var charArray = _referenceToCheck.ToCharArray();

            for (var i = 0; i < charArray.Length; i++)
            {
                var characterToCheck = charArray[i];

                if (char.IsDigit(characterToCheck))
                {
                    _weightedSum += int.Parse(characterToCheck.ToString()) * _weightings[i];
                }
            }
        }

        private void CalculateRemainder(CheckDigitStrategyArgs args)
        {
            _remainder = _weightedSum % args.CheckDigitConfiguration.Modulus;
        }

        private void ApplySubtraction(CheckDigitStrategyArgs args)
        {
            if (!args.CheckDigitConfiguration.ApplySubtraction)
                return;

            _remainder = args.CheckDigitConfiguration.Modulus - _remainder;
        }

        private void ApplyResultSubstitutions(CheckDigitStrategyArgs args)
        {
            var conversions = args.CheckDigitConfiguration.ResultSubstitutionsDictionary();

            _calculatedCheckDigit = conversions.ContainsKey(_remainder.ToString())
                    ? conversions[_remainder.ToString()]
                    : _remainder.ToString();
        }

        private void ValidateCheckDigit(CheckDigitStrategyArgs args)
        {
            if (!Equals(args.CheckDigit.ToString(), _calculatedCheckDigit))
                throw new CheckDigitValidationException();
        }
    }
}
