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

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

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

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

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

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

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

            return new ChangeValidationResult(resultErrors);
        }

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

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

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

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

            return new ChangeValidationResult(resultErrors);
        }

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

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

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

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

            return new ChangeValidationResult(resultErrors);
        }

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

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

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

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

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

            return new ChangeValidationResult(resultErrors);
        }

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

            var autoChanges = GetChangedItems(DealPathConstants.Auto);

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

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

            return new ChangeValidationResult(resultErrors);
        }

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

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

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

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

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

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

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

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

            var checkLevenshtein = CheckLevenshteinDistanceFor(
                allowedInsurantChanges,
                fieldsValues);

            return checkLevenshtein;
        }

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

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

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

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

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

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Channels;
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, Dictionary<string, List<ObjectCompareResult>> changes)
            : 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");

            // Auto
            changes.TryGetValue("InsuranceObjects[0].Auto", out var autoChanges);
            var actualAuto = autoChanges?.FirstOrDefault()?.ActualObject as Auto;
            var expectedAuto = autoChanges?.FirstOrDefault()?.ExpectedObject as 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.AutoBrandName");
            AddRule("Марка и / или Модель", "Auto.AutoModelName");
            AddRule("Смена Выгодоприобретателя", "Auto.IsCreditAuto");

            if (actualAuto?.OriginalKeysAmount != 0 &&
                actualAuto?.OriginalKeysAmount != expectedAuto?.OriginalKeysAmount)
            {
                AddRule("Изменение количества ключей", "Auto.OriginalKeysAmount");
            }

            // Owner
            changes.TryGetValue("InsuranceObjects[0].Owners[0]", out var ownerChanges);
            var actualOwner = ownerChanges?.FirstOrDefault()?.ActualObject as Owner;
            var expectedOwner = ownerChanges?.FirstOrDefault()?.ExpectedObject as 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");
            
            if (IsOwnerChangedCompletely(actualOwner, expectedOwner)
                && (expectedAuto?.IsCreditAuto == false || actualAuto?.IsCreditAuto == false))
            {
                AddRule(
                    "Смена Собственника и Выгодоприобретателя",
                    owner => FormatPersonName((owner as Owner)?.Subject),
                    "Owner.Subject.IndividualEntity.Person.LastName",
                    "Owner.Subject.IndividualEntity.Person.Name",
                    "Owner.Subject.IndividualEntity.Person.MiddleName");
            }

            // 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
            changes.TryGetValue("AutoDeal", out var autoDealChanges);
            var actualAutodeal = autoDealChanges?.FirstOrDefault()?.ActualObject as AutoDeal;
            var expectedAutodeal = autoDealChanges?.FirstOrDefault()?.ExpectedObject as 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");

            AddRuleForMinAgeExperience(actualAutodeal, expectedAutodeal);

            // 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 bool IsOwnerChangedCompletely(Owner actualOwner, Owner expectedOwner)
        {
            if (actualOwner == null || expectedOwner == null)
            {
                return false;
            }

            var fullNameChanges = InsuranceApp.Infrastructure.Procedures.LevenshteinDistance(FormatPersonName(actualOwner.Subject), FormatPersonName(expectedOwner.Subject)) > 2;
            var dateOfBirthChange = actualOwner.Subject.IndividualEntity.Person.DateOfBirth != expectedOwner.Subject.IndividualEntity.Person.DateOfBirth;
            var identityDocumentChanges = actualOwner.IdentityDocument.Series != expectedOwner.IdentityDocument.Series || actualOwner.IdentityDocument.Number != expectedOwner.IdentityDocument.Number;

            return fullNameChanges && dateOfBirthChange && identityDocumentChanges;
        }

        private void AddRuleForMinAgeExperience(AutoDeal actualAutodeal, AutoDeal expectedAutodeal)
        {
            var driverMinAge = expectedAutodeal?.DriverMinAge ?? 18;
            var driverMinExperience = expectedAutodeal?.DriverMinExperience ?? 0;

            if (actualAutodeal?.DriverMinAge > driverMinAge)
            {
                AddRule("Увеличение минимального возраста", "AutoDeal.DriverMinAge");
            }
            else if (actualAutodeal?.DriverMinAge < driverMinAge)
            {
                AddRule("Уменьшение минимального возраста", "AutoDeal.DriverMinAge");
            }

            if (actualAutodeal?.DriverMinExperience > driverMinExperience)
            {
                AddRule("Увеличение минимального стажа", "AutoDeal.DriverMinExperience");
            }
            else if (actualAutodeal?.DriverMinExperience < driverMinExperience)
            {
                AddRule("Уменьшение минимального стажа", "AutoDeal.DriverMinExperience");
            }
        }

        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;
        }
    }
}

Расскажи что это за код