using System;
using System.Collections.Generic;
using System.Linq;
using iRIS.Framework.Data;
using iRIS.InsuranceCore.Code.Implementations.Comparison;
using iRIS.InsuranceCore.Entities;
using iRIS.InsuranceCore.Entities.Custom;
using iRIS.KaskoLink2.Modification.Code.Constants;

namespace iRIS.KaskoLink2.Modification.Code.Implementations.ChangeValidations
{
    /// <summary>
    /// Валидатор допустимых для ВИ Link2 отличий.
    /// </summary>
    public class KaskoModificationChangesLink2Validator : BaseChangesValidator
    {
        private const int AllowedLevenshteinDistance = 2;

        private readonly string[] _driversFieldsAllowedForChanges = { ChangesPaths.DriverName, ChangesPaths.DriverMiddleName, ChangesPaths.DriverLastName };
        private readonly string[] _insurantFieldsAllowedForChanges = { ChangesPaths.InsurantName, ChangesPaths.InsurantMiddleName, ChangesPaths.InsurantLastName };
        private readonly string[] _ownerFieldsAllowedForChanges = { ChangesPaths.OwnerName, ChangesPaths.OwnerMiddleName, ChangesPaths.OwnerLastName };

        /// <summary>
        /// Initializes a new instance of the <see cref="KaskoModificationChangesLink2Validator"/> class.
        /// </summary>
        /// <param name="changes">Словарь с результатами сравнения корзин.</param>
        public KaskoModificationChangesLink2Validator(Dictionary<string, List<ObjectCompareResult>> changes)
            : base(changes)
        {
        }

        public ChangeValidationResult CheckAutoDeal()
        {
            var resultErrors = new List<string>();

            var autoDealChanges = GetChangedItems(DealPathConstants.AutoDeal);
            if (autoDealChanges.Count == 0 && resultErrors.Count == 0)
            {
                return ChangeValidationResult.Empty();
            }

            if (!CheckIsMultidriveChanged(autoDealChanges))
            {
                resultErrors.Add("Не допускается изменение признака мультидрайва.");
            }

            return new ChangeValidationResult(resultErrors);
        }

        private bool CheckIsMultidriveChanged(List<ObjectCompareResult> autoDealChanges) => !autoDealChanges.Any(dr => dr.ChangedFields.Any(f => f == ChangesPaths.AutoDealIsMultidrive));

        /// <summary>
        /// Выполняет проверку допустимости изменений данных страхователя.
        /// </summary>
        /// <returns>Результат проверки.</returns>
        public ChangeValidationResult CheckInsurant(bool? isInsurantGettingMarried)
        {
            var resultErrors = new List<string>();

            var insurantChanges = GetChangedItems(DealPathConstants.Insurant);
            if (insurantChanges.Count == 0)
            {
                return ChangeValidationResult.Empty();
            }

            if (!CheckLevenshteinDistanceForInsurantFullNameChanges(insurantChanges, isInsurantGettingMarried))
            {
                var errorMessage = isInsurantGettingMarried == true
                    ? "Не допускается изменение более " + AllowedLevenshteinDistance + " символов в поле имя и отчество страхователя."
                    : "Не допускается изменение более " + AllowedLevenshteinDistance + " символов в поле ФИО страхователя.";
                resultErrors.Add(errorMessage);
            }

            return new ChangeValidationResult(resultErrors);
        }

        /// <summary>
        /// Выполняет проверку допустимости изменений данных собственника.
        /// </summary>
        /// <returns>Результат проверки.</returns>
        public ChangeValidationResult CheckOwner(bool? isOwnerGettingMerried)
        {
            var resultErrors = new List<string>();

            var ownerChanges = GetChangedItems(DealPathConstants.Owner);
            if (ownerChanges.Count == 0)
            {
                return ChangeValidationResult.Empty();
            }

            // Если собственник полностью меняется, то не выводим ошибку по кол-ву измененных символов
            if (HasOwnerChangedCompletely(ownerChanges))
            {
                return ChangeValidationResult.Empty();
            }

            if (!CheckLevenshteinDistanceForOwnerFullNameChanges(ownerChanges, isOwnerGettingMerried))
            {
                var errorMessage = isOwnerGettingMerried == true
                    ? "Не допускается изменение более " + AllowedLevenshteinDistance + " символов в поле имя и отчество собственника."
                    : "Не допускается изменение более " + AllowedLevenshteinDistance + " символов в поле ФИО собственника.";
                resultErrors.Add(errorMessage);
            }

            return new ChangeValidationResult(resultErrors);
        }

        /// <summary>
        /// Проверяем полное изменение собственника или частичное.
        /// </summary>
        /// <param name="insurantChanges"></param>
        /// <returns></returns>
        private bool HasOwnerChangedCompletely(List<ObjectCompareResult> ownerChanges)
        {
            var fullNameChanges = ownerChanges.Any(change =>
                change.ChangedFields.Any(field => field == ChangesPaths.OwnerLastName || field == ChangesPaths.OwnerName || field == ChangesPaths.OwnerMiddleName));
            var dateOfBirthChange = ownerChanges.Any(change => change.ChangedFields.Any(field => field == ChangesPaths.OwnerDateOfBirth));

            var identityDocumentChanges = ownerChanges.Any(change => change.ChangedFields.Any(field => field == ChangesPaths.OwnerIdentityDocumentSeries || field == ChangesPaths.OwnerIdentityDocumentNumber));
            return fullNameChanges && dateOfBirthChange && identityDocumentChanges;
        }

        /// <summary>
        /// Выполняет проверку допустимости изменений данных водителей.
        /// </summary>
        /// <returns>Результат проверки.</returns>
        public ChangeValidationResult CheckDrivers(bool? isDriverGettingMerried)
        {
            var resultErrors = new List<string>();

            var driverChanges = GetChangedItems(DealPathConstants.Drivers);
            if (driverChanges.Count == 0)
            {
                return ChangeValidationResult.Empty();
            }

            if (!CheckLevenshteinDistanceForDriverFullNameChanges(driverChanges, isDriverGettingMerried))
            {
                var errorMessage = isDriverGettingMerried == true
                    ? "Не допускается изменение более " + AllowedLevenshteinDistance + " символов в поле имя и отчество водителя."
                    : "Не допускается изменение более " + AllowedLevenshteinDistance + " символов в поле ФИО водителя.";
                resultErrors.Add(errorMessage);
            }

            return new ChangeValidationResult(resultErrors);
        }

        /// <summary>
        /// Выполняет проверку допустимости изменений данных авто.
        /// </summary>
        /// <param name="isPts">Признак документа ПТС.</param>
        /// <returns>Результат проверки.</returns>
        public ChangeValidationResult CheckAuto()
        {
            var resultErrors = new List<string>();

            var autoChanges = GetChangedItems(DealPathConstants.Auto);

            if (autoChanges.Count == 0)
            {
                return ChangeValidationResult.Empty();
            }

            if (!CheckHasAutostartChangesAndKeysAmount(autoChanges))
            {
                resultErrors.Add($"Не допускается изменение признака автозапуска и количества ключей.");
            }

            return new ChangeValidationResult(resultErrors);
        }

        private bool CheckHasAutostartChangesAndKeysAmount(List<ObjectCompareResult> autoChanges)
        {
            return !autoChanges.Any(dr => dr.ChangedFields.Any(f => f == ChangesPaths.AutoHasAutostart) 
                && dr.ChangedFields.Any(f => f == ChangesPaths.AutoOriginalKeysAmount));
        }

        private bool CheckLevenshteinDistanceForDriverFullNameChanges(List<ObjectCompareResult> driversChanges, bool? isDriverGettingMerried)
        {
            var allowedDrivesChanges = driversChanges
                .Where(dr => dr.ChangedFields.Any(f => _driversFieldsAllowedForChanges.Contains(f))).ToList();

            var fieldsValues = new Dictionary<string, Func<Driver, string>>
            {
                [ChangesPaths.DriverName] = d => d.Subject.IndividualEntity.Person.Name,
                [ChangesPaths.DriverMiddleName] = d => d.Subject.IndividualEntity.Person.MiddleName,
            };

            if (isDriverGettingMerried != true)
            {
                fieldsValues[ChangesPaths.DriverLastName] = d => d.Subject.IndividualEntity.Person.LastName;
            }

            var checkLevenshtein = CheckLevenshteinDistanceFor(
                allowedDrivesChanges,
                fieldsValues);
            return checkLevenshtein;
        }

        private bool CheckLevenshteinDistanceForInsurantFullNameChanges(List<ObjectCompareResult> insurantChanges, bool? isInsurantGettingMarried)
        {
            var allowedInsurantChanges = insurantChanges
                .Where(dr => dr.ChangedFields.Any(f => _insurantFieldsAllowedForChanges.Contains(f))).ToList();

            var fieldsValues = new Dictionary<string, Func<Insurant, string>>
            {
                [ChangesPaths.InsurantName] = i => i.Subject.IndividualEntity.Person.Name,
                [ChangesPaths.InsurantMiddleName] = i => i.Subject.IndividualEntity.Person.MiddleName
            };

            if (isInsurantGettingMarried != true)
            {
                fieldsValues[ChangesPaths.InsurantLastName] = i => i.Subject.IndividualEntity.Person.LastName;
            }

            var checkLevenshtein = CheckLevenshteinDistanceFor(
                allowedInsurantChanges,
                fieldsValues);

            return checkLevenshtein;
        }

        private bool CheckLevenshteinDistanceForOwnerFullNameChanges(List<ObjectCompareResult> ownerChanges, bool? isOwnerGettingMerried)
        {
            var allowedOwnerChanges = ownerChanges
                .Where(dr => dr.ChangedFields.Any(f => _ownerFieldsAllowedForChanges.Contains(f))).ToList();

            var fieldsValues = new Dictionary<string, Func<Owner, string>>
            {
                [ChangesPaths.OwnerName] = i => i.Subject.IndividualEntity.Person.Name,
                [ChangesPaths.OwnerMiddleName] = i => i.Subject.IndividualEntity.Person.MiddleName,
            };

            if (isOwnerGettingMerried != true)
            {
                fieldsValues[ChangesPaths.OwnerLastName] = i => i.Subject.IndividualEntity.Person.LastName;
            }

            var checkLevenshtein = CheckLevenshteinDistanceFor(
                allowedOwnerChanges,
                fieldsValues);
            return checkLevenshtein;
        }

        private bool CheckLevenshteinDistanceFor<T>(
            List<ObjectCompareResult> changes,
            Dictionary<string, Func<T, string>> fieldValues)
            where T : BaseEntity
        {
            return CheckLevenshteinDistanceFor(changes, fieldValues, AllowedLevenshteinDistance);
        }
    }
}

Мне надо поменять логику определения изменений для Ф.И.О. или дата рождения Страхователя, Собственника, ЛДУ.
Старая логика:
Если изменение произошло по причине вступления / расторжения брака, т.е. передан признак isGettingMarried :true, то можно полностью изменить фамилию.
Новая логика:
Если в хотя бы из одном полей ФИО и ДР изменили более 2х символов, но при этом не изменили паспортные данные (блок identityDocument не передан), то считаем, что это корректировка данных существующего участника.
Если изменили менее 2х символов в полях ФИО и ДР, но при этом также изменили паспортные данные (блок identityDocument передан), то считаем, что это корректировка данных существующего участника.
Если для объекта owner/insurant в поле фамилии изменили более 2х символов, а в имени, отчестве и др 2 или менее символов и изменили паспортные данные (блок identityDocument передан), а также передан признак isGettingMarried = true, то считаем, что это корректировка данных существующего участника.
Если для объекта owner хотя бы в одном из полей ФИО и ДР изменили более 2х символов, и изменили паспортные данные (блок identityDocument передан) и признак isGettingMarried не передан, то считаем, что это изменение участника договора, т.е. изменение 43.
Если для объекта insurant хотя бы в одном из полей ФИО и ДР изменили более 2х символов, и изменили паспортные данные (блок identityDocument передан) и признак isGettingMarried не передан, то выводить error 510105 "Недопустимые изменения: не допускается изменение Страхователя на другое физическое лицо" 
По водителям также убрать ограничение на изменение 2х символов. Для определения, произошла ли корректировка данных или добавление нового водителя, сравнивать серию и номер ВУ из запроса и в БД.
Не добавляй новых методов, все дописывай в существующих и пиши названия классов куда внести изменения.