using System;
using System.Collections.Generic;
using System.Linq;
using iRIS.Framework.Entity.Metadata.Manager.Interfaces;
using iRIS.InsuranceApp.Infrastructure.Extensions;
using iRIS.InsuranceCore.Code.Implementations.Comparison;
using iRIS.InsuranceCore.Code.Implementations.Comparison.DisplayedChanges;
using iRIS.InsuranceCore.Entities;
using iRIS.InsuranceCore.Entities.Custom;
using iRIS.InsuranceCore.Entities.EntityExtensions;
using iRIS.InsuranceCore.Infrastructure.Constants;
using iRIS.InsuranceCore.Infrastructure.Helpers;
using iRIS.KaskoLink2.Modification.Code.Constants;

namespace iRIS.KaskoLink2.Modification.Code.Implementations.Comparison
{
    public class KaskoDisplayedChangesFormatter : DisplayedChangesFormatter
    {
        public KaskoDisplayedChangesFormatter(IPathManager pathManager)
            : base(pathManager, 30)
        {
            // Periods
            AddRule(
                period => FormatPeriod(period as Period),
                "Period.DateStart",
                "Period.DateEnd");

            // Insurant
            AddRuleWithInfo(
                "ФИО страхователя",
                "Данные были изменены в профиле личного кабинета",
                insurant => FormatPersonName((insurant as Insurant)?.Subject),
                "Insurant.Subject.IndividualEntity.Person.LastName",
                "Insurant.Subject.IndividualEntity.Person.Name",
                "Insurant.Subject.IndividualEntity.Person.MiddleName");
            AddRuleWithInfo(
                "Дата рождения страхователя",
                "Данные были изменены в профиле личного кабинета",
                "Insurant.Subject.IndividualEntity.Person.DateOfBirth");
            AddRuleWithInfo(
                "Гражданство",
                "Данные были изменены в профиле личного кабинета",
                insurant => FormatCitizenship((insurant as Insurant)?.IdentityDocument?.Citizenship),
                "Insurant.IdentityDocument.CitizenshipCode",
                "Insurant.IdentityDocument.Citizenship.Code");
            AddRuleWithInfo(
                "Паспортные данные страхователя",
                "Данные были изменены в профиле личного кабинета",
                insurant => FormatPersonIdentityDocument((insurant as Insurant)?.IdentityDocument),
                "Insurant.IdentityDocument.IdentityDocumentTypeCode",
                "Insurant.IdentityDocument.IdentityDocumentType.Code",
                "Insurant.IdentityDocument.Series",
                "Insurant.IdentityDocument.Number",
                "Insurant.IdentityDocument.UnitCode",
                "Insurant.IdentityDocument.DateIssue",
                "Insurant.IdentityDocument.IssuedBy");
            AddRuleWithCustomCaption(
                insurant => GetAddressCaption((insurant as Insurant).GetPerson().Addresses[0], nameof(Insurant)),
                insurant => (insurant as Insurant).GetPerson()?.Addresses[0]?.EnteredAddress,
                "Insurant.Subject.IndividualEntity.Person.Addresses[0].EnteredAddress");
            AddRuleWithCustomCaption(
                insurant => GetAddressCaption((insurant as Insurant).GetPerson().Addresses[1], nameof(Insurant)),
                insurant => (insurant as Insurant).GetPerson()?.Addresses[1]?.EnteredAddress,
                "Insurant.Subject.IndividualEntity.Person.Addresses[1].EnteredAddress");
            AddRule(
                "Email страхователя",
                "Insurant.Subject.IndividualEntity.Person.Email1");
            AddRule(
                "Телефон страхователя",
                "Insurant.Subject.IndividualEntity.Person.Phone1");
            AddRule(
                "СНИЛС страхователя",
                "Insurant.Subject.IndividualEntity.Person.SnilsNumber");

            // Owner
            AddRule(
                "ФИО собственника",
                owner => FormatPersonName((owner as Owner)?.Subject),
                "Owner.Subject.IndividualEntity.Person.LastName",
                "Owner.Subject.IndividualEntity.Person.Name",
                "Owner.Subject.IndividualEntity.Person.MiddleName");
            AddRule(
                "Дата рождения собственника",
                "Owner.Subject.IndividualEntity.Person.DateOfBirth");
            AddRule(
                "Гражданство",
                owner => FormatCitizenship((owner as Owner)?.IdentityDocument?.Citizenship),
                "Owner.IdentityDocument.CitizenshipCode",
                "Owner.IdentityDocument.Citizenship.Code");
            AddRule(
                "Паспортные данные собственника",
                owner => FormatPersonIdentityDocument((owner as Owner)?.IdentityDocument),
                "Owner.IdentityDocument.IdentityDocumentTypeCode",
                "Owner.IdentityDocument.IdentityDocumentType.Code",
                "Owner.IdentityDocument.Series",
                "Owner.IdentityDocument.Number",
                "Owner.IdentityDocument.UnitCode",
                "Owner.IdentityDocument.DateIssue",
                "Owner.IdentityDocument.IssuedBy");
            AddRuleWithCustomCaption(
                owner => GetAddressCaption((owner as Owner).GetPerson().Addresses[0], nameof(Owner)),
                owner => (owner as Owner).GetPerson()?.Addresses[0]?.EnteredAddress,
                "Owner.Subject.IndividualEntity.Person.Addresses[0].EnteredAddress");
            AddRuleWithCustomCaption(
                owner => GetAddressCaption((owner as Owner).GetPerson().Addresses[1], nameof(Owner)),
                owner => (owner as Owner).GetPerson()?.Addresses[1]?.EnteredAddress,
                "Owner.Subject.IndividualEntity.Person.Addresses[1].EnteredAddress");
            AddRule(
                "СНИЛС собственника",
                "Owner.Subject.IndividualEntity.Person.SnilsNumber");
            AddRule(
                "Email собственника",
                "Owner.Subject.IndividualEntity.Person.Email1");

            // Auto
            AddRule("Год выпуска ТС", "Auto.AutoYearOfIssueCode");
            AddRule("VIN", "Auto.Vin");
            AddRule("Шасси (рама)", "Auto.ChassisNumber");
            AddRule("Кузов (прицеп)", "Auto.BodyNumber");
            AddRule("Смена рег. знака", "Auto.RegistrationNumber");
            AddRule("Мощность двигателя", "Auto.AutoEnginePowerCode");
            AddRule("Коробка передач", "Auto.AutoTransmissionTypeCode");
            AddRule("Тип двигателя ТС", "Auto.AutoEngineTypeCode");
            AddRule("Установка системы автоматического запуска двигателя", "Auto.HasAutostart");
            AddRule("Прицеп", auto => FormatAutoHasTrailer(auto as Auto), "Auto.HasTrailer");
            AddRule("Цель использования", auto => (auto as Auto)?.PurposeOfUsing?.Name, "Auto.PurposeOfUsingCode", "Auto.PurposeOfUsing.Code");
            AddRuleWithCustomCaption(
                auto => (auto as Auto)?.TsIdentityCards?.FirstOrDefault()?.TsIdentityCardType?.Name,
                auto => FormatAutoTsIdentityCard(auto as Auto),
                "Auto.TsIdentityCards[0].TsIdentityCardTypeCode",
                "Auto.TsIdentityCards[0].TsIdentityCardType.Code",
                "Auto.TsIdentityCards[0].Series",
                "Auto.TsIdentityCards[0].Number",
                "Auto.TsIdentityCards[0].DateOfIssued");
            AddRule("Номер диагностической карты", "Auto.DiagnosticCard.Number");
            AddRule("Дата окончания диагностической карты", "Auto.DiagnosticCard.DateEnd");

            // Drivers
            AddRule(
                "ФИО водителя",
                driver => FormatDriverName(driver as Driver),
                "Driver.Subject.IndividualEntity.Person.LastName",
                "Driver.Subject.IndividualEntity.Person.Name",
                "Driver.Subject.IndividualEntity.Person.MiddleName");
            AddRule(
                null,
                driver => FormatDriverDateOfBirth(driver as Driver),
                "Driver.Subject.IndividualEntity.Person.DateOfBirth");
            AddRule(
                null,
                driver => FormatDriverLicense(driver as Driver),
                "Driver.DriverLicenseSeries",
                "Driver.DriverLicenseNumber");
            AddRule(
                null,
                driver => FormatIsForeingDriverLicense(driver as Driver),
                "Driver.IsForeignDrivingLicense");
            AddRule(
                null,
                driver => FormatDriverDrivingExperiences(driver as Driver),
                "Driver.DrivingExperiences[0].DrivingExperienceDateStart");

            // AutoDeal
            AddRule(
                "Изменен тип использования",
                autoDeal => FormatPeriodType(autoDeal as AutoDeal),
                "AutoDeal.InsurancePeriodTypeCode");
            AddRule(
                "Изменен тип использования",
                autoDeal => FormatIsMultidrive(autoDeal as AutoDeal),
                "AutoDeal.IsMultidrive");
            AddRule(
                "ПТС",
                autoDeal => FormatAutoTsIdentityCard(autoDeal as AutoDeal),
                "AutoDeal.TsIdentityCard.TsIdentityCardTypeCode",
                "AutoDeal.TsIdentityCard.TsIdentityCardType.Code",
                "AutoDeal.TsIdentityCard.Series",
                "AutoDeal.TsIdentityCard.Number",
                "AutoDeal.TsIdentityCard.DateOfIssued");

            // TransformRules
            AddTransformRule(new DisplayedChangesFormatterTransformRule
            {
                ChangedField = "AutoDeal.InsurancePeriodTypeCode",
                SourcePath = DealPathConstants.AutoDeal,
                DestinationPath = DealPathConstants.DealPeriods,
                ItemsShouldBeRemovedFunc = autoDeal => (autoDeal as AutoDeal)?.InsurancePeriodTypeCode == InsurancePeriodTypeCodes.OneYear,
                ItemsShouldBeIsAddedFunc = autoDeal => (autoDeal as AutoDeal)?.InsurancePeriodTypeCode == InsurancePeriodTypeCodes.UsePeriod
            });
            AddTransformRule(new DisplayedChangesFormatterTransformRule
            {
                ChangedField = "AutoDeal.IsMultidrive",
                SourcePath = DealPathConstants.AutoDeal,
                DestinationPath = DealPathConstants.Drivers,
                ItemsShouldBeRemovedFunc = autoDeal => (autoDeal as AutoDeal)?.IsMultidrive == true,
                ItemsShouldBeIsAddedFunc = autoDeal => (autoDeal as AutoDeal)?.IsMultidrive == false
            });
        }

        private string GetAddressCaption(Address address, string typeName) =>
            address.AddressTypeCode switch
            {
                AddressTypeCodes.Registration when typeName == nameof(Insurant) => ChangesCaptions.InsurantRegistrationAddress,
                AddressTypeCodes.Registration when typeName == nameof(Owner) => ChangesCaptions.OwnerRegistrationAddress,
                AddressTypeCodes.Fact when typeName == nameof(Insurant) => ChangesCaptions.InsurantFactAddress,
                AddressTypeCodes.Fact when typeName == nameof(Owner) => ChangesCaptions.OwnerFactAddress,
                _ => string.Empty,
            };

        protected override int GetObjectOrderNumber(string path)
        {
            switch (path)
            {
                case DealPathConstants.DealPeriods:
                    return 1;

                case DealPathConstants.Insurant:
                    return 2;

                case DealPathConstants.Owner:
                    return 3;

                case DealPathConstants.Auto:
                    return 4;

                case DealPathConstants.Drivers:
                    return 5;

                default:
                    return int.MaxValue;
            }
        }

        protected override string GetObjectTitle(string path)
        {
            switch (path)
            {
                case DealPathConstants.DealPeriods:
                    return "Периоды использования";

                case DealPathConstants.Insurant:
                    return "Страхователь";

                case DealPathConstants.Owner:
                    return "Владелец ТС";

                case DealPathConstants.Auto:
                    return "Транспортное средство";

                case DealPathConstants.Drivers:
                    return "Изменение списка ЛДУ";
            }

            return null;
        }

        protected override string GetItemName(string path)
        {
            if (path == DealPathConstants.Drivers)
            {
                return "водитель";
            }

            if (path == DealPathConstants.DealPeriods)
            {
                return "период";
            }

            return null;
        }

        protected override string GetItemText(string path, ObjectCompareResult compareResult)
        {
            if (path == DealPathConstants.DealPeriods)
            {
                if (compareResult.IsAdded)
                {
                    return FormatPeriod(compareResult.ActualObject as Period);
                }

                if (compareResult.IsDeleted)
                {
                    return FormatPeriod(compareResult.ExpectedObject as Period);
                }

                return null;
            }

            if (path == DealPathConstants.Drivers)
            {
                if (compareResult.IsAdded)
                {
                    return FormatDriver(compareResult.ActualObject as Driver);
                }

                if (compareResult.IsDeleted)
                {
                    return FormatDriverName(compareResult.ExpectedObject as Driver);
                }

                if (!compareResult.ChangedFields.Any(x => x.EndsWith("Name")))
                {
                    return FormatDriverName(compareResult.ExpectedObject as Driver);
                }
            }

            return null;
        }

        private static string FormatPeriodType(AutoDeal autoDeal)
        {
            if (autoDeal == null)
            {
                return null;
            }

            return autoDeal.InsurancePeriodTypeCode == InsurancePeriodTypeCodes.OneYear
                ? "Годовое использование"
                : "Периоды использования";
        }

        private static string FormatPeriod(Period period)
        {
            if (period == null)
            {
                return null;
            }

            var monthDifference = DurationHelper.FormatMonthDifference(
                period.DateStart.GetValueOrDefault(),
                period.DateEnd.GetValueOrDefault());

            return $"{monthDifference}, {period.DateStart?.ToShortDateForUi()} - {period.DateEnd?.ToShortDateForUi()}";
        }

        private static string FormatPersonName(Subject subject)
        {
            var person = subject?.IndividualEntity?.Person;

            if (person == null)
            {
                return null;
            }

            return $"{person.LastName} {person.Name} {person.MiddleName}".TrimEnd();
        }

        private static string FormatCitizenship(Citizenship citizenship)
        {
            if (citizenship == null)
            {
                return null;
            }

            if (citizenship.Code == "other")
            {
                return "Иное";
            }

            return citizenship.FullName;
        }

        private static string FormatPersonIdentityDocument(IdentityDocument document)
        {
            if (document == null)
            {
                return null;
            }

            var unitCodeString = string.IsNullOrEmpty(document.UnitCode)
                ? string.Empty
                : $", код подразделения {document.UnitCode}";

            var issueInfoString = document.DateIssue.HasValue || !string.IsNullOrEmpty(document.IssuedBy)
                ? $", {document.DateIssue?.ToShortDateForUi()} {document.IssuedBy}"
                : string.Empty;

            return $"{document.IdentityDocumentType?.Name} {document.Series} {document.Number}{unitCodeString}{issueInfoString}"
                .Trim();
        }

        private static string FormatAutoHasTrailer(Auto auto)
        {
            return auto?.HasTrailer == true ? "С прицепом" : "Без прицепа";
        }

        private static string FormatAutoTsIdentityCard(Auto auto)
        {
            var tsIdentityCard = auto?.TsIdentityCards?.FirstOrDefault();
            if (tsIdentityCard == null)
            {
                return null;
            }

            var dateOfIssuedString = tsIdentityCard.DateOfIssued.HasValue
                ? $", дата выдачи {tsIdentityCard.DateOfIssued.Value.ToShortDateForUi()}"
                : string.Empty;

            return $"{tsIdentityCard.Series} {tsIdentityCard.Number}{dateOfIssuedString}";
        }

        private static string FormatAutoTsIdentityCard(AutoDeal autoDeal)
        {
            var tsIdentityCard = autoDeal?.TsIdentityCard;
            if (tsIdentityCard == null)
            {
                return null;
            }

            var dateOfIssuedString = tsIdentityCard.DateOfIssued.HasValue
                ? $", дата выдачи {tsIdentityCard.DateOfIssued.Value.ToShortDateForUi()}"
                : string.Empty;

            return $"{tsIdentityCard.Series} {tsIdentityCard.Number}{dateOfIssuedString}";
        }

        private static string FormatIsMultidrive(AutoDeal autoDeal)
        {
            if (autoDeal == null)
            {
                return null;
            }

            return autoDeal.IsMultidrive == true ? "Без ограничения" : "Ограниченный круг водителей";
        }

        public static string FormatDriver(Driver driver)
        {
            if (driver == null)
            {
                return null;
            }

            var fields = new List<string>(4)
            {
                FormatDriverName(driver),
                FormatDriverDateOfBirth(driver),
                FormatDriverLicense(driver),
                FormatDriverDrivingExperiences(driver)
            };

            return string.Join(", ", fields.Where(x => x != null));
        }

        private static string FormatDriverName(Driver driver)
        {
            if (driver == null)
            {
                return null;
            }

            return FormatPersonName(driver.Subject);
        }

        private static string FormatDriverDateOfBirth(Driver driver)
        {
            var dateOfBirth = driver?.Subject?.IndividualEntity?.Person?.DateOfBirth;

            return dateOfBirth.HasValue
                ? $"дата рождения {dateOfBirth.Value.ToShortDateForUi()}"
                : null;
        }

        private static string FormatDriverLicense(Driver driver)
        {
            if (driver == null || (string.IsNullOrEmpty(driver.DriverLicenseSeries) && string.IsNullOrEmpty(driver.DriverLicenseNumber)))
            {
                return null;
            }

            return $"ВУ №{driver.DriverLicenseSeries} {driver.DriverLicenseNumber}";
        }

        private static string FormatIsForeingDriverLicense(Driver driver)
        {
            if (driver?.IsForeignDrivingLicense == null)
            {
                return null;
            }

            var result = "Иностранное ВУ";

            return driver.IsForeignDrivingLicense == true
                ? $"{result} Да"
                : $"{result} Нет";
        }

        private static string FormatDriverDrivingExperiences(Driver driver)
        {
            return driver?.DrivingExperiences.Count > 0
                ? $"стаж вождения с {driver.DrivingExperiences[0].DrivingExperienceDateStart?.ToShortDateForUi()}"
                : null;
        }
    }
}


using iRIS.InsuranceCore.Entities;
using iRIS.KaskoLink2.Modification.Code.Interfaces.Comparison;
using System;
using System.Collections;
using System.Linq;

namespace iRIS.KaskoLink2.Modification.Code.Implementations.Comparison
{
    public static class KaskoObjectComparerBuilder
    {
        public static IObjectComparer<Deal> CreateDealComparer(IServiceProvider serviceProvider)
        {
            var objectComparer = (ObjectComparer<Deal>)ObjectComparerBuilder.CreateDealComparer(serviceProvider);

            return objectComparer
                .WithEqualityComparer(typeof(Driver), new KaskoDriverEqualityComparer());
        }

        #region EqualityComparers

        private sealed class KaskoDriverEqualityComparer : IEqualityComparer
        {
            public new bool Equals(object x, object y)
            {
                var expected = (Driver)x;
                var actual = (Driver)y;
                var expectedPerson = expected?.Subject?.IndividualEntity?.Person;
                var actualPerson = actual?.Subject?.IndividualEntity?.Person;
                var expectedDrivingExperience = expected?.DrivingExperiences.FirstOrDefault();
                var actualDrivingExperience = actual?.DrivingExperiences.FirstOrDefault();

                var isEqual =
                    StringCompare(expectedPerson?.LastName, actualPerson?.LastName)
                    && StringCompare(expectedPerson?.Name, actualPerson?.Name)
                    && StringCompare(expectedPerson?.MiddleName, actualPerson?.MiddleName)
                    && expectedPerson?.DateOfBirth == actualPerson?.DateOfBirth
                    && StringCompare(expected?.DriverLicenseSeries, actual?.DriverLicenseSeries)
                    && StringCompare(expected?.DriverLicenseNumber, actual?.DriverLicenseNumber)
                    && expectedDrivingExperience?.DrivingExperienceDateStart == actualDrivingExperience?.DrivingExperienceDateStart
                    && expected?.PreviousDriverLicenseIssueDate == actual?.PreviousDriverLicenseIssueDate
                    && StringCompare(expected?.PreviousDriverLicenseNumber, actual?.PreviousDriverLicenseNumber)
                    && StringCompare(expected?.PreviousDriverLicenseSeries, actual?.PreviousDriverLicenseSeries)
                    && StringCompare(expected?.PreviousDriverMiddleName, actual?.PreviousDriverMiddleName)
                    && StringCompare(expected?.PreviousDriverName, actual?.PreviousDriverName)
                    && StringCompare(expected?.PreviousDriverLastName, actual?.PreviousDriverLastName)
                    && expected?.IsForeignDrivingLicense == actual?.IsForeignDrivingLicense;

                return isEqual;
            }

            public int GetHashCode(object obj)
            {
                return obj.GetHashCode();
            }

            private bool StringCompare(string expected, string actual)
            {
                return string.Equals(expected, actual, StringComparison.OrdinalIgnoreCase);
            }
        }

        #endregion
    }
}


using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using iRIS.Framework.Entity.Metadata.Manager.Interfaces;
using iRIS.InsuranceCore.Code.Implementations.Comparison;
using iRIS.InsuranceCore.Entities.Custom;

namespace iRIS.KaskoLink2.Modification.Code.Implementations.Comparison
{
    public sealed class ObjectComparer<TRootEntity> : Interfaces.Comparison.IObjectComparer<TRootEntity>
    {
        private readonly IPathManager _pathManager;
        private readonly ObjectComparerPropertyIterator _objectComparerPropertyIterator;

        private readonly Dictionary<Type, IEqualityComparer> _identityComparers = new Dictionary<Type, IEqualityComparer>();
        private readonly Dictionary<Type, IEqualityComparer> _equalityComparers = new Dictionary<Type, IEqualityComparer>();
        private readonly Dictionary<string, string[]> _paths = new Dictionary<string, string[]>();

        public ObjectComparer(IPathManager pathManager, ObjectComparerPropertyIterator objectComparerPropertyIterator)
        {
            _pathManager = pathManager;
            _objectComparerPropertyIterator = objectComparerPropertyIterator;
        }

        public Dictionary<string, List<ObjectCompareResult>> GetChanges(TRootEntity expectedRootObject, TRootEntity actualRootObject)
        {
            var changes = new Dictionary<string, List<ObjectCompareResult>>();

            foreach (var path in _paths)
            {
                object expectedObject = null;
                object actualObject = null;

                var pathValues = new[] { path.Key }.Union(_paths[path.Key]);
                foreach (var pathValue in pathValues)
                {
                    expectedObject = _pathManager.GetValueByPath(expectedRootObject, pathValue);
                    actualObject = _pathManager.GetValueByPath(actualRootObject, pathValue);

                    if (expectedObject != null || actualObject != null)
                    {
                        break;
                    }
                }

                if (expectedObject == null && actualObject == null)
                {
                    continue;
                }

                // collection
                if (expectedObject is IEnumerable<object> expectedCollection
                    && actualObject is IEnumerable<object> actualCollection)
                {
                    changes.Add(path.Key, CollectionCompareLogic(expectedCollection, actualCollection));
                }

                // object
                else
                {
                    var objectCompareResult = ObjectCompareLogic(expectedObject, actualObject);
                    if (objectCompareResult != null)
                    {
                        changes.Add(path.Key, new List<ObjectCompareResult> { objectCompareResult });
                    }
                }
            }

            return changes;
        }

        public ObjectComparer<TRootEntity> WithIdentityComparer(Type entityType, IEqualityComparer identityComparer)
        {
            _identityComparers[entityType] = identityComparer;
            return this;
        }

        public ObjectComparer<TRootEntity> WithEqualityComparer(Type entityType, IEqualityComparer equalityComparer)
        {
            _equalityComparers[entityType] = equalityComparer;
            return this;
        }

        public ObjectComparer<TRootEntity> WithChangePath(string path, params string[] alternativePathes)
        {
            _paths[path] = alternativePathes ?? new string[0];
            return this;
        }

        private List<ObjectCompareResult> CollectionCompareLogic(IEnumerable<object> expectedCollection, IEnumerable<object> actualCollection)
        {
            var results = new List<ObjectCompareResult>();

            var elementType = expectedCollection.GetType().GetGenericArguments()[0];
            var identityComparer = _identityComparers[elementType];
            var equalityComparer = _equalityComparers[elementType];

            // expected collection
            foreach (var expecteditem in expectedCollection)
            {
                var actualItem = actualCollection.SingleOrDefault(i => identityComparer.Equals(expecteditem, i));
                if (actualItem == null)
                {
                    results.Add(ObjectCompareResult.CreateIsDeleted(expecteditem));
                    continue;
                }

                var isEqual = equalityComparer.Equals(expecteditem, actualItem);
                if (!isEqual)
                {
                    results.Add(GetIsChanged(expecteditem, actualItem));
                }
            }

            // actual collection
            foreach (var actualItem in actualCollection)
            {
                var expectedItem = expectedCollection.SingleOrDefault(i => identityComparer.Equals(actualItem, i));
                if (expectedItem == null)
                {
                    results.Add(ObjectCompareResult.CreateIsAdded(actualItem));
                }
            }

            return results;
        }

        private ObjectCompareResult ObjectCompareLogic(object expected, object actual)
        {
            var objectType = (expected ?? actual).GetType();

            if (expected != null && actual == null)
            {
                return ObjectCompareResult.CreateIsDeleted(expected);
            }

            if (expected == null && actual != null)
            {
                return ObjectCompareResult.CreateIsAdded(actual);
            }

            if (expected != null && actual != null)
            {
                var equalityComparer = _equalityComparers[objectType];
                var isEqual = equalityComparer.Equals(expected, actual);

                if (!isEqual)
                {
                    return GetIsChanged(expected, actual);
                }
            }

            return null;
        }

        private ObjectCompareResult GetIsChanged<T>(T expected, T actual)
        {
            var result = ObjectCompareResult.CreateIsChanged(expected, actual);

            if (_objectComparerPropertyIterator != null)
            {
                var expectedProperties = _objectComparerPropertyIterator.GetProperties(expected);
                var actualProperties = _objectComparerPropertyIterator.GetProperties(actual);

                foreach (var expectedProperty in expectedProperties)
                {
                    var actualProperty = actualProperties.FirstOrDefault(x =>
                        x.PathInHierarchy == expectedProperty.PathInHierarchy);

                    if (actualProperty != null &&
                        !Equals(expectedProperty.ObjectInstance, actualProperty.ObjectInstance))
                    {
                        result.ChangedFields.Add(expectedProperty.PathInHierarchy);
                    }
                }
            }

            return result;
        }
    }
}


using System;
using System.Collections;
using System.Linq;
using iRIS.Framework.Data.Interfaces;
using iRIS.Framework.Entity.Metadata.Manager.Interfaces;
using iRIS.InsuranceCore.Code.Implementations.Comparison;
using iRIS.InsuranceCore.Code.Interfaces;
using iRIS.InsuranceCore.Entities;
using iRIS.InsuranceCore.Infrastructure.Constants;
using Microsoft.Extensions.DependencyInjection;

namespace iRIS.KaskoLink2.Modification.Code.Implementations.Comparison
{
    public static class ObjectComparerBuilder
    {
        public static Interfaces.Comparison.IObjectComparer<Deal> CreateDealComparer(IServiceProvider serviceProvider)
        {
            var pathManager = serviceProvider.GetService<IPathManager>();
            var propertyIterator = serviceProvider.GetService<IPropertyIterator>();

            var collectionItemIdentityComparer = new CollectionItemIdentityComparer();
            var objectComparerIterator = new ObjectComparerPropertyIterator(propertyIterator);

            return new ObjectComparer<Deal>(pathManager, objectComparerIterator)

                // идентифицирующие компараторы элементов коллекций
                .WithIdentityComparer(typeof(Driver), collectionItemIdentityComparer)
                .WithIdentityComparer(typeof(Period), collectionItemIdentityComparer)

                // сравнивающие компараторы
                .WithEqualityComparer(typeof(Driver), new DriverEqualityComparer())
                .WithEqualityComparer(typeof(Period), new PeriodEqualityComparer())
                .WithEqualityComparer(typeof(Auto), new AutoEqualityComparer())
                .WithEqualityComparer(typeof(AutoDeal), new AutoDealEqualityComparer())
                .WithEqualityComparer(typeof(Insurant), new InsurantEqualityComparer())
                .WithEqualityComparer(typeof(Owner), new OwnerEqualityComparer())

                // задание путей для ченджей
                .WithChangePath(DealPathConstants.Drivers)
                .WithChangePath(DealPathConstants.DealPeriods)
                .WithChangePath(DealPathConstants.Auto, DealPathConstants.AutoAltPath)
                .WithChangePath(DealPathConstants.AutoDeal)
                .WithChangePath(DealPathConstants.Insurant)
                .WithChangePath(DealPathConstants.Owner, DealPathConstants.OwnerAltPath);
        }

        private abstract class DefaultEqualityComparer : IEqualityComparer
        {
            public new abstract bool Equals(object x, object y);

            public int GetHashCode(object obj)
            {
                return obj.GetHashCode();
            }

            /// <summary>
            /// Сравение строк с учетом регистра
            /// Из ОСС текст может приходить капсом
            /// </summary>
            /// <param name="expected"></param>
            /// <param name="actual"></param>
            /// <returns></returns>
            protected bool StringCompare(string expected, string actual)
            {
                return string.Equals(expected, actual, StringComparison.OrdinalIgnoreCase);
            }
        }

        #region IdentityComparers
        private sealed class CollectionItemIdentityComparer : DefaultEqualityComparer
        {
            public override bool Equals(object x, object y)
            {
                var expected = (IUniqueble)x;
                var actual = (IUniqueble)y;

                return expected?.UniqueId == actual?.UniqueId;
            }
        }
        #endregion

        #region EqualityComparers

        private sealed class DriverEqualityComparer : DefaultEqualityComparer
        {
            public override bool Equals(object x, object y)
            {
                var expected = (Driver)x;
                var actual = (Driver)y;
                var expectedPerson = expected?.Subject?.IndividualEntity?.Person;
                var actualPerson = actual?.Subject?.IndividualEntity?.Person;
                var expectedDrivingExperience = expected?.DrivingExperiences.FirstOrDefault();
                var actualDrivingExperience = actual?.DrivingExperiences.FirstOrDefault();

                var isEqual =
                    StringCompare(expectedPerson?.LastName, actualPerson?.LastName)
                    && StringCompare(expectedPerson?.Name, actualPerson?.Name)
                    && StringCompare(expectedPerson?.MiddleName, actualPerson?.MiddleName)
                    && expectedPerson?.DateOfBirth == actualPerson?.DateOfBirth
                    && StringCompare(expected?.DriverLicenseSeries, actual?.DriverLicenseSeries)
                    && StringCompare(expected?.DriverLicenseNumber, actual?.DriverLicenseNumber)
                    && expectedDrivingExperience?.DrivingExperienceDateStart == actualDrivingExperience?.DrivingExperienceDateStart;

                return isEqual;
            }
        }

        private sealed class PeriodEqualityComparer : DefaultEqualityComparer
        {
            public override bool Equals(object x, object y)
            {
                var expected = (Period)x;
                var actual = (Period)y;
                var isEqual =
                       expected?.DateStart == actual?.DateStart
                       && expected?.DateEnd == actual?.DateEnd;

                return isEqual;
            }
        }

        private sealed class AutoEqualityComparer : DefaultEqualityComparer
        {
            public override bool Equals(object x, object y)
            {
                var expected = (Auto)x;
                var actual = (Auto)y;
                var expectedTsIdentityCard = expected?.TsIdentityCards?.FirstOrDefault();
                var actualTsIdentityCard = actual?.TsIdentityCards?.FirstOrDefault();

                return StringCompare(expected?.Vin, actual?.Vin)
                    && StringCompare(expected?.BodyNumber, actual?.BodyNumber)
                    && StringCompare(expected?.ChassisNumber, actual?.ChassisNumber)
                    && StringCompare(expected?.RegistrationNumber, actual?.RegistrationNumber)
                    && expected?.AutoYearOfIssueValue == actual?.AutoYearOfIssueValue
                    && StringCompare(expectedTsIdentityCard?.Series, actualTsIdentityCard?.Series)
                    && StringCompare(expectedTsIdentityCard?.Number, actualTsIdentityCard?.Number)
                    && expectedTsIdentityCard?.DateOfIssued == actualTsIdentityCard?.DateOfIssued
                    && StringCompare(expected?.DiagnosticCard?.Number, actual?.DiagnosticCard?.Number)
                    && expected?.DiagnosticCard?.DateDiagnostic == actual?.DiagnosticCard?.DateDiagnostic
                    && StringCompare(expected?.PurposeOfUsingCode, actual?.PurposeOfUsingCode)
                    && StringCompare(expected?.AutoTypeCode, actual?.AutoTypeCode)
                    && StringCompare(expected?.AutoYearOfIssueCode, actual?.AutoYearOfIssueCode)
                    && StringCompare(expected?.AutoEnginePowerCode, actual?.AutoEnginePowerCode)
                    && StringCompare(expected?.AutoEngineTypeCode, actual?.AutoEngineTypeCode)
                    && expected?.HasAutostart == actual?.HasAutostart
                    && expected?.OriginalKeysAmount == actual?.OriginalKeysAmount
                    && StringCompare(expected?.AutoTransmissionTypeCode, actual?.AutoTransmissionTypeCode)
                    && expected?.HasTrailer == actual?.HasTrailer;
            }
        }

        private sealed class AutoDealEqualityComparer : DefaultEqualityComparer
        {
            public override bool Equals(object x, object y)
            {
                var expected = (AutoDeal)x;
                var actual = (AutoDeal)y;
                var expectedTsIdentityCard = expected?.TsIdentityCard;
                var actualTsIdentityCard = actual?.TsIdentityCard;

                return expected?.IsMultidrive == actual?.IsMultidrive
                    && expected?.MonthsCount == actual?.MonthsCount
                    && StringCompare(expectedTsIdentityCard?.Series, actualTsIdentityCard?.Series)
                    && StringCompare(expectedTsIdentityCard?.Number, actualTsIdentityCard?.Number)
                    && expectedTsIdentityCard?.DateOfIssued == actualTsIdentityCard?.DateOfIssued
                    && StringCompare(expectedTsIdentityCard?.TsIdentityCardTypeCode, actualTsIdentityCard?.TsIdentityCardTypeCode);
            }
        }

        private sealed class InsurantEqualityComparer : DefaultEqualityComparer
        {
            public override bool Equals(object x, object y)
            {
                var expected = (Insurant)x;
                var actual = (Insurant)y;
                var expectedPerson = expected?.Subject?.IndividualEntity?.Person;
                var actualPerson = actual?.Subject?.IndividualEntity?.Person;
                var expectedIdentityDocument = expected?.IdentityDocument;
                var actualIdentityDocument = actual?.IdentityDocument;

                return StringCompare(expectedPerson?.LastName, actualPerson?.LastName)
                    && StringCompare(expectedPerson?.Name, actualPerson?.Name)
                    && StringCompare(expectedPerson?.MiddleName, actualPerson?.MiddleName)
                    && StringCompare(expectedPerson?.SnilsNumber, actualPerson?.SnilsNumber)
                    && expectedPerson?.DateOfBirth == actualPerson?.DateOfBirth
                    && StringCompare(expectedIdentityDocument?.CitizenshipCode, actualIdentityDocument?.CitizenshipCode)
                    && StringCompare(expectedIdentityDocument?.IdentityDocumentTypeCode, actualIdentityDocument?.IdentityDocumentTypeCode)
                    && StringCompare(expectedIdentityDocument?.Series, actualIdentityDocument?.Series)
                    && StringCompare(expectedIdentityDocument?.Number, actualIdentityDocument?.Number)
                    && StringCompare(expectedIdentityDocument?.IssuedBy, actualIdentityDocument?.IssuedBy)
                    && expectedIdentityDocument?.DateIssue == actualIdentityDocument?.DateIssue
                    && StringCompare(expectedIdentityDocument?.UnitCode, actualIdentityDocument?.UnitCode)
                    && StringCompare(
                        expectedPerson?.Addresses?.FirstOrDefault(a => a.AddressTypeCode == AddressTypeCodes.Registration)?.EnteredAddress,
                        actualPerson?.Addresses?.FirstOrDefault(a => a.AddressTypeCode == AddressTypeCodes.Registration)?.EnteredAddress)
                    && StringCompare(
                        expectedPerson?.Addresses?.FirstOrDefault(a => a.AddressTypeCode == AddressTypeCodes.Fact)?.EnteredAddress,
                        actualPerson?.Addresses?.FirstOrDefault(a => a.AddressTypeCode == AddressTypeCodes.Fact)?.EnteredAddress)
                    && StringCompare(expectedPerson?.Phone1, actualPerson?.Phone1)
                    && StringCompare(expectedPerson.Email1, actualPerson?.Email1);
            }
        }

        private sealed class OwnerEqualityComparer : DefaultEqualityComparer
        {
            public override bool Equals(object x, object y)
            {
                var expected = (Owner)x;
                var actual = (Owner)y;
                var expectedPerson = expected?.Subject?.IndividualEntity?.Person;
                var actualPerson = actual?.Subject?.IndividualEntity?.Person;
                var expectedIdentityDocument = expected?.IdentityDocument;
                var actualIdentityDocument = actual?.IdentityDocument;

                return StringCompare(expectedPerson?.LastName, actualPerson?.LastName)
                    && StringCompare(expectedPerson?.Name, actualPerson?.Name)
                    && StringCompare(expectedPerson?.MiddleName, actualPerson?.MiddleName)
                    && StringCompare(expectedPerson?.SnilsNumber, actualPerson?.SnilsNumber)
                    && expectedPerson?.DateOfBirth == actualPerson?.DateOfBirth
                    && StringCompare(expectedIdentityDocument?.CitizenshipCode, actualIdentityDocument?.CitizenshipCode)
                    && StringCompare(expectedIdentityDocument?.IdentityDocumentTypeCode, actualIdentityDocument?.IdentityDocumentTypeCode)
                    && StringCompare(expectedIdentityDocument?.Series, actualIdentityDocument?.Series)
                    && StringCompare(expectedIdentityDocument?.Number, actualIdentityDocument?.Number)
                    && StringCompare(expectedIdentityDocument?.IssuedBy, actualIdentityDocument?.IssuedBy)
                    && expectedIdentityDocument?.DateIssue == actualIdentityDocument?.DateIssue
                    && StringCompare(expectedIdentityDocument?.UnitCode, actualIdentityDocument?.UnitCode)
                    && StringCompare(
                        expectedPerson?.Addresses?.FirstOrDefault(a => a.AddressTypeCode == AddressTypeCodes.Registration)?.EnteredAddress,
                        actualPerson?.Addresses?.FirstOrDefault(a => a.AddressTypeCode == AddressTypeCodes.Registration)?.EnteredAddress)
                    && StringCompare(
                        expectedPerson?.Addresses?.FirstOrDefault(a => a.AddressTypeCode == AddressTypeCodes.Fact)?.EnteredAddress,
                        actualPerson?.Addresses?.FirstOrDefault(a => a.AddressTypeCode == AddressTypeCodes.Fact)?.EnteredAddress)
                    && StringCompare(expectedPerson?.RegistrationRegionCode, actualPerson?.RegistrationRegionCode);
            }
        }
        #endregion
    }
}


Как мне обрабатывать изменение нового поля, например deal.auto.autoCost