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, Dictionary<string, List> changes, Basket expectedBasket)
: base(pathManager, 30)
{
// Periods
AddRule(
period => FormatPeriod(period as Period),
"Period.DateStart",
"Period.DateEnd");


        // Insurant
        changes.TryGetValue("Insurant", out var insurantChanges);
        var actualInsurant = insurantChanges?.FirstOrDefault()?.ActualObject as Insurant;
        var expectedInsurant = insurantChanges?.FirstOrDefault()?.ExpectedObject as 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");

        AddRuleWithCustomCaption(              
            insurant => IsInsurantChangedCompletely(actualInsurant, expectedInsurant) && !IsOwnerAndInsurantTheSamePerson(actualInsurant, expectedBasket.GetKaskoDeal().GetOwner())
                ? "Смена Страхователя, не являющегося Собственником"
                : "Смена Страхователя, являющегося Собственником",
            insurant => FormatPersonName((insurant as Insurant)?.Subject),
            "Insurant.Subject.IndividualEntity.Person.LastName",
            "Insurant.Subject.IndividualEntity.Person.Name",
            "Insurant.Subject.IndividualEntity.Person.MiddleName");

        // 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))
        {
            AddRule(
                "Смена Собственника и Выгодоприобретателя",
                owner => FormatPersonName((owner as Owner)?.Subject),
                "Owner.Subject.IndividualEntity.Person.LastName",
                "Owner.Subject.IndividualEntity.Person.Name",
                "Owner.Subject.IndividualEntity.Person.MiddleName");
        }

        // 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.AutoCost");
        AddRule("Демонтаж ПСС и / или ПУУ", "Auto.HasAntiTheftSystem");
        AddRule("Марка и / или Модель", "Auto.AutoBrandName");
        AddRule("Марка и / или Модель", "Auto.AutoModelName");
        AddRule("Смена Выгодоприобретателя", "Auto.IsCreditAuto");

        if (actualAuto?.OriginalKeysAmount == 0 &&
            (expectedAuto?.OriginalKeysAmount > 0 ||
            expectedAuto?.OriginalKeysAmount == null))
        {
            AddRule("Кража ключей", "Auto.OriginalKeysAmount");
        }

        if (actualAuto?.OriginalKeysAmount >= 2 &&
            (expectedAuto?.OriginalKeysAmount < 2 ||
            expectedAuto?.OriginalKeysAmount == null))
        {
            AddRule("Количество ключей меняется на 2 или более", "Auto.OriginalKeysAmount");
        }

        if (actualAuto?.OriginalKeysAmount == 1 &&
            (expectedAuto?.OriginalKeysAmount == 2 ||
            expectedAuto?.OriginalKeysAmount == null))
        {
            AddRule("Количество ключей меняется с 2 - х на 1", "Auto.OriginalKeysAmount");
        }

        // 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 Auto;
        var expectedAutodeal = autoDealChanges?.FirstOrDefault()?.ExpectedObject as Auto;

        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 bool IsInsurantChangedCompletely(Insurant actualInsurant, Insurant expectedInsurant)
    {
        if (actualInsurant == null || expectedInsurant == null)
        {
            return false;
        }

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

        return fullNameChanges && dateOfBirthChange && identityDocumentChanges;
    }

    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 bool IsOwnerAndInsurantTheSamePerson(Insurant actualInsurant, Owner expectedOwner)
    {
        if (actualInsurant == null || expectedOwner == null)
        {
            return false;
        }

        var actualInsurantPerson = actualInsurant.Subject.IndividualEntity.Person;
        var expectedOwnerPerson = expectedOwner.Subject.IndividualEntity.Person;

        return actualInsurantPerson.LastName == expectedOwnerPerson.LastName &&
               actualInsurantPerson.Name == expectedOwnerPerson.Name &&
               actualInsurantPerson.MiddleName == expectedOwnerPerson.MiddleName &&
               actualInsurantPerson.DateOfBirth == expectedOwnerPerson.DateOfBirth &&
               actualInsurant.IdentityDocument.Series == expectedOwner.IdentityDocument.Series &&
               actualInsurant.IdentityDocument.Number == expectedOwner.IdentityDocument.Number;
    }

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

Мне надо изменить проверку AutoDeal.DriverMinAge и AutoDeal.DriverMinExperience, так что бы если в actual пришло значение больше чем в expected добавить правило Увеличение возраста / стажа, а если наоборот то Уменьшение возраста / стажа. Также необходимо учесть если в expected DriverMinAge и DriverMinExperience == null, то в таком случае они равны минимальным значениям 18 и 0 соответственно