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
                    && expected?.AutoCost >= actual?.AutoCost
                    && expected?.HasAntiTheftSystem == actual?.HasAntiTheftSystem
                    && StringCompare(expected?.AutoBrandName, actual?.AutoBrandName)
                    && StringCompare(expected?.AutoModelName, actual?.AutoModelName)
                    && expected?.IsCreditAuto == actual?.IsCreditAuto
                    && expected?.ReplacingLocks == actual?.ReplacingLocks;
            }
        }

        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)
                    && expected?.DriverMinAge == actual.DriverMinAge
                    && expected?.DriverMinExperience == actual.DriverMinExperience;
            }
        }

        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)
                    && StringCompare(expectedPerson.InnNumber, actualPerson?.InnNumber);
            }
        }

        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)
                    && StringCompare(expectedPerson.InnNumber, actualPerson?.InnNumber);
            }
        }
        #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;
        }
    }
}


Мне надо добавить обработку пути Deal.DateStart, сейчас сравниваются только сущности, которые лежат в Deal (Например Auto), а DateStart это поле формата DateTime