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

            // Insurant
            changes.TryGetValue("Insurant", out var insurantPersonChanges);
            var actualInsurant = insurantPersonChanges?.FirstOrDefault()?.ActualObject as Insurant;
            var expectedInsurant = insurantPersonChanges?.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");
            
            if (IsInsurantChangedCompletely(actualInsurant, expectedInsurant) == true)
            {
                AddRule(
                    "Смена Страхователя, не являющегося Собственником", 
                    insurant => FormatPersonName((insurant as Insurant)?.Subject),
                    "Insurant.Subject.IndividualEntity.Person.LastName",
                    "Insurant.Subject.IndividualEntity.Person.Name",
                    "Insurant.Subject.IndividualEntity.Person.MiddleName");
            }

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

            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
            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)
        {
            var fullNameChanges = FormatPersonName(actualInsurant.Subject) != FormatPersonName(expectedInsurant.Subject);
            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 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;
        }
    }
}

Мне нужно сделать доработки
1) Смена Страхователя, не являющегося Собственником сейчас нет проверки на то что новый страхователь не является собственником, надо добавить
2) Добавить правило "Смена Страхователя, являющего Собственником"