using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using iRIS.Framework.Entity.Metadata.Manager.Interfaces;
using iRIS.Framework.UI.PresentationModel.PathManager;
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");
        AddRule("Увеличение страховой суммы по рискам \"Ущерб\" и \"Угон / Хищение\"", "Auto.AutoCost");
        AddRule("Демонтаж ПСС и / или ПУУ", "Auto.HasAntiTheftSystem");
        AddRule("Марка и / или Модель", "Auto.AutoBrandName");
        AddRule("Марка и / или Модель", "Auto.AutoModelName");

        // 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");

        AddRule("Изменение возраста / стажа", "AutoDeal.DriverMinAge");
        AddRule("Изменение возраста / стажа", "AutoDeal.DriverMinExperience");

        // 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 System;
using System.Collections.Generic;
using System.Linq;
using iRIS.Framework.Entity.Metadata.Manager.Interfaces;
using iRIS.Framework.ExtensionMethods;
using iRIS.InsuranceApp.Infrastructure.Extensions;
using iRIS.InsuranceCore.Entities.Custom;

namespace iRIS.InsuranceCore.Code.Implementations.Comparison.DisplayedChanges
{
public abstract class DisplayedChangesFormatter
{
private readonly IPathManager _pathManager;
private readonly List _rules;
private readonly List _transformRules;


    protected DisplayedChangesFormatter(IPathManager pathManager, int? rulesCapacity = null)
    {
        _pathManager = pathManager;
        
        _rules = rulesCapacity.HasValue
            ? new List<DisplayedChangesFormatterRule>(rulesCapacity.Value)
            : new List<DisplayedChangesFormatterRule>();
        
        _transformRules = new List<DisplayedChangesFormatterTransformRule>();
    }

    public List<Entities.DisplayedChangeObject> ToDisplayedChanges(IDictionary<string, List<ObjectCompareResult>> changes)
    {
        var transformedChanges = TransformInputChanges(changes);

        var displayedChanges = transformedChanges
            .Select(x => GetDisplayedChangeObject(x.Key, x.Value))
            .OrderBy(x => x.OrderNumber)
            .ToList();

        TransformOutputChanges(displayedChanges);
        
        RemoveEmptyChangeObjects(displayedChanges);

        return displayedChanges;
    }
    
    protected abstract int GetObjectOrderNumber(string path);

    protected abstract string GetObjectTitle(string path);

    protected abstract string GetItemName(string path);

    protected abstract string GetItemText(string path, ObjectCompareResult compareResult);

    protected void AddRule(string caption, string path)
    {
        AddRule(caption, rootObject => DefaultFormatFunc(rootObject, path), new[] { path });
    }

    protected void AddRule(string caption, Func<object, string> formatFunc, string path)
    {
        AddRule(caption, formatFunc, new[] { path });
    }

    protected void AddRule(Func<object, string> formatFunc, params string[] paths)
    {
        AddRule(null, formatFunc, paths);
    }

    protected void AddRule(Func<object, string> formatFunc, string path)
    {
        AddRule(null, formatFunc, path);
    }

    protected void AddRule(string caption, Func<object, string> formatFunc, params string[] paths)
    {
        _rules.Add(
            new DisplayedChangesFormatterRule
            {
                Caption = caption,
                FormatFunc = formatFunc,
                ChangedFieldPaths = paths
            });
    }

    protected void AddRuleWithCustomCaption(Func<object, string> captionFunc, Func<object, string> formatFunc, params string[] paths)
    {
        _rules.Add(
            new DisplayedChangesFormatterRule
            {
                CaptionFunc = captionFunc,
                FormatFunc = formatFunc,
                ChangedFieldPaths = paths
            });
    }
    
    protected void AddRuleWithInfo(string caption, string info, string path)
    {
        AddRuleWithInfo(caption, info, rootObject => DefaultFormatFunc(rootObject, path), new[] { path });
    }
    
    protected void AddRuleWithInfo(string caption, string info, Func<object, string> formatFunc, params string[] paths)
    {
        _rules.Add(
            new DisplayedChangesFormatterRule
            {
                Caption = caption,
                AdditionalInfo = info,
                FormatFunc = formatFunc,
                ChangedFieldPaths = paths
            });
    }

    protected void AddTransformRule(DisplayedChangesFormatterTransformRule rule)
    {
        _transformRules.Add(rule);
    }

    private Dictionary<string, List<ObjectCompareResult>> TransformInputChanges(
        IDictionary<string, List<ObjectCompareResult>> allChanges)
    {
        var result = allChanges.ToDictionary(x => x.Key, x => x.Value);

        foreach (var transformRule in _transformRules)
        {
            if (!result.TryGetValue(transformRule.SourcePath, out var sourceChanges)
                || sourceChanges.Count == 0
                || !sourceChanges.Any(x => x.ChangedFields.Contains(transformRule.ChangedField)))
            {
                continue;
            }
            
            if (transformRule.ItemsShouldBeRemovedFunc(sourceChanges[0].ActualObject))
            {
                if (result.ContainsKey(transformRule.DestinationPath))
                {
                    result[transformRule.DestinationPath] = new List<ObjectCompareResult>();
                }
                
                continue;
            }

            if (transformRule.ItemsShouldBeIsAddedFunc(sourceChanges[0].ActualObject))
            {
                if (!result.TryGetValue(transformRule.DestinationPath, out var destinationChanges)
                    || destinationChanges.Count == 0)
                {
                    var transformedChanges = sourceChanges.Select(x => x.DeepCopy()).ToList();
                
                    transformedChanges
                        .ForEach(x => x.ChangedFields.RemoveAll(field => field == transformRule.ChangedField));

                    result[transformRule.SourcePath] = transformedChanges;
                }
                else
                {
                    var transformedChanges = result[transformRule.DestinationPath]
                        .Where(x => !x.IsDeleted)
                        .Select(x => x.DeepCopy())
                        .ToList();

                    transformedChanges
                        .ForEach(x =>
                        {
                            x.IsAdded = true;
                            x.IsDeleted = false;
                            x.IsChanged = false;
                            x.ChangedFields.Clear();
                        });

                    result[transformRule.DestinationPath] = transformedChanges;
                }
            }
        }

        return result;
    }

    private void TransformOutputChanges(ICollection<Entities.DisplayedChangeObject> changes)
    {
        foreach (var transformRule in _transformRules)
        {
            var sourceChangeObject = changes.SingleOrDefault(x => x.Path == transformRule.SourcePath);
            if (sourceChangeObject == null)
            {
                continue;
            }

            var destinationChangeObject = changes.SingleOrDefault(x => x.Path == transformRule.DestinationPath);
            if (destinationChangeObject == null)
            {
                destinationChangeObject = new Entities.DisplayedChangeObject
                {
                    OrderNumber = GetObjectOrderNumber(transformRule.DestinationPath),
                    Path = transformRule.DestinationPath,
                    Title = GetObjectTitle(transformRule.DestinationPath)
                };

                changes.Add(destinationChangeObject);
            }

            destinationChangeObject.ChangedFields.AddRange(
                sourceChangeObject.ChangedItems
                    .SelectMany(x => x.ChangedFields)
                    .Where(x => x.Path == transformRule.ChangedField));

            foreach (var sourceChangeItem in sourceChangeObject.ChangedItems)
            {
                sourceChangeItem.ChangedFields.RemoveAll(x => x.Path == transformRule.ChangedField);
            }
        }
    }

    private Entities.DisplayedChangeObject GetDisplayedChangeObject(string path, List<ObjectCompareResult> compareResults)
    {
        var changedObject = new Entities.DisplayedChangeObject
        {
            OrderNumber = GetObjectOrderNumber(path),
            Path = path,
            Title = GetObjectTitle(path),
            AdditionalInfo = GetAdditionalInfo(compareResults)
        };

        foreach (var compareResult in compareResults)
        {
            var changedItem = new Entities.DisplayedChangeItem
            {
                IsAdded = compareResult.IsAdded,
                IsDeleted = compareResult.IsDeleted,
                IsChanged = compareResult.IsChanged,
                ItemName = GetItemName(path),
                ItemText = GetItemText(path, compareResult),
                ChangedFields = GetChangedFields(compareResult)
            };

            changedObject.ChangedItems.Add(changedItem);
        }

        return changedObject;
    }

    private List<Entities.DisplayedChangeField> GetChangedFields(ObjectCompareResult compareResult)
    {
        var result = new List<Entities.DisplayedChangeField>(compareResult.ChangedFields.Count);

        foreach (var rule in _rules)
        {
            if (rule.ChangedFieldPaths.Any(x => compareResult.ChangedFields.Contains(x)))
            {
                result.Add(new Entities.DisplayedChangeField
                {
                    Caption = rule.CaptionFunc != null
                        ? rule.CaptionFunc(compareResult.ExpectedObject ?? compareResult.ActualObject)
                        : rule.Caption,
                    Path = rule.ChangedFieldPaths.FirstOrDefault(),
                    ExpectedValue = rule.FormatFunc(compareResult.ExpectedObject),
                    ActualValue = rule.FormatFunc(compareResult.ActualObject)
                });
            }
        }

        return result;
    }

    private string GetAdditionalInfo(List<ObjectCompareResult> compareResults)
    {
        var rulesWithInfo = _rules
            .Where(x => x.AdditionalInfo != null)
            .FirstOrDefault(rule => compareResults.Any(x => x.ChangedFields.Intersect(rule.ChangedFieldPaths).Any()));

        return rulesWithInfo?.AdditionalInfo;
    }

    private void RemoveEmptyChangeObjects(List<Entities.DisplayedChangeObject> result)
    {
        result.RemoveAll(x => x.ChangedFields.Count == 0
                              && (x.ChangedItems.Count == 0
                                  || x.ChangedItems.All(c => c.IsChanged && c.ChangedFields.Count == 0)));
    }

    private string DefaultFormatFunc(object rootObject, string path)
    {
        var value = _pathManager.GetValueByPath(rootObject, GetInnerFieldPath(path));

        if (value is DateTime dateTime)
        {
            return dateTime.ToShortDateForUi();
        }

        return value?.ToString();
    }
    
    private static string GetInnerFieldPath(string changedFieldPath)
    {
        var innerPath = changedFieldPath;
        var indexOfDot = changedFieldPath.IndexOf('.');
        if (indexOfDot > 0)
        {
            innerPath = innerPath.Substring(indexOfDot + 1);
        }

        return innerPath;
    }
}