using iRIS.Framework.Common;
using iRIS.Framework.Data;
using iRIS.Framework.Data.Services.Client;
using iRIS.Framework.Entity.Dictionary.EntitySettings;
using iRIS.Framework.Link2;
using iRIS.Framework.Platform.Entities;
using iRIS.Framework.Validation;
using iRIS.Framework.Workflow.Process.Testing.Fakes;
using iRIS.InsuranceCore.Entities;
using iRIS.InsuranceCore.Entities.Enums;
using iRIS.InsuranceCore.Infrastructure.Constants;
using SasClient.Code.CheckKaskoUpProjectService;
using iRIS.KaskoLink2.Code.Constants;
using iRIS.KaskoLink2.Code.CustomProcesses;
using iRIS.KaskoLink2.Tests.Code.CustomProcesses;
using Moq;
using NUnit.Framework;
using Serilog;
using System;
using System.Collections.Generic;
using iRIS.KaskoLink2.Code.Implementations.AutoCostCalculator;
using iRIS.Framework.Data.Services.Client.Interfaces;

namespace iRIS.KaskoLink2.Tests.Code.Link2.CustomProcess
{
    [TestFixture(Description = "Тестирование процесса проверки KaskoSasCheckProcessForLink2 в SAS.")]
    public class KaskoSasCheckProcessForLink2Tests
    {
        [TestCase(KaskoSasCheckProcessConstants.KaskoSasCheckAccept, true)]
        [TestCase(KaskoSasCheckProcessConstants.KaskoSasCheckAccept, false)]
        [Test(Description = "При получении статуса из SAS происходит переход по сценарию Целевой сегмент")]
        public void KaskoSasCheckProcess_ShouldContinue_WithTargetedSegment(string finDecision, bool entitySettingValue)
        {
            // Arrange
            var basket = GetBasket(Deal());
            var errorCollection = new List<ValidationError>();

            var serviceProvider = CreateServiceProvider(basket, finDecision, entitySettingValue, errorCollection);

            // Act
            var result = ExecuteCustomProcess(serviceProvider.Object, basket);

            // Assert
            Assert.AreEqual(errorCollection.Count, 0);
            Assert.AreEqual(result.ExitBranch, KaskoSasCheckProcessForLink2.ExitBranch_TargetedSegment);
        }

        [TestCase(KaskoSasCheckProcessConstants.KaskoSasCheckReject)]
        [TestCase("test")]
        [Test(Description = "При получении статуса из SAS происходит переход по сценарию не Целевой сегмент Reject")]
        public void KaskoSasCheckProcess_ShouldContinue_NotTargetedSegment_Reject(string finDecision)
        {
            // Arrange
            var basket = GetBasket(Deal());
            var errorCollection = new List<ValidationError>();

            var serviceProvider = CreateServiceProvider(basket, finDecision, false, errorCollection);

            // Act
            var result = ExecuteCustomProcess(serviceProvider.Object, basket);

            // Assert
            Assert.AreEqual(errorCollection.Count, 1);
            Assert.AreEqual(result.ExitBranch, KaskoSasCheckProcessForLink2.ExitBranch_NotTargetedSegment_Reject);
        }

        [TestCase(KaskoSasCheckProcessConstants.KaskoSasCheckUrm)]
        [TestCase(KaskoSasCheckProcessConstants.KaskoSasCheckNoDecision)]
        [Test(Description = "При получении статуса из SAS происходит переход по сценарию не Целевой сегмент CorrData")]
        public void KaskoSasCheckProcess_ShouldContinue_NotTargetedSegment_CorrData(string finDecision)
        {
            // Arrange
            var basket = GetBasket(Deal());
            var errorCollection = new List<ValidationError>();

            var serviceProvider = CreateServiceProvider(basket, finDecision, true, errorCollection);

            // Act
            var result = ExecuteCustomProcess(serviceProvider.Object, basket);

            // Assert
            Assert.AreEqual(errorCollection.Count, 0);
            Assert.AreEqual(result.ExitBranch, KaskoSasCheckProcessForLink2.ExitBranch_NotTargetedSegment_CorrData);
        }

        private Basket GetBasket(params Deal[] deals)
        {
            return new Basket
            {
                Number = "TST-12345",
                Deals = new List<Deal>(deals)
            };
        }

        private Deal Deal()
        {
            return new Deal
            {
                AutoDeal = new AutoDeal
                {
                    DriverMinAge = 19,
                    KbmClass = "4",
                    KbmValue = 0.95m,
                    IsSasCoefficientOutdate = false,
                },
                DictionaryDate = DT.Now,
                DealIntermediaries = new ObservableList<DealIntermediary>
                {
                    new DealIntermediary
                    {
                        Intermediary = new Intermediary
                        {
                            InteractionTypeCode = InteractionTypeEnum.Agent.ToString()
                        }
                    }
                },
                Product = new Product
                {
                    ProductDictCode = ProductDictCodes.Kasko
                },
                InsuranceObjects = new ObservableList<InsuranceObject>()
                {
                    new InsuranceObject()
                    {
                        Auto = new Auto()
                        {
                            AutoTypeCode = "lts",
                            HasMileage = true,
                        }
                    }
                },

                Insurant = new Insurant
                {
                    Subject = new Subject
                    {
                        IndividualEntity = new IndividualEntity
                        {
                            Person = new Person
                            {
                                Name = "SomeName",
                                LastName = "SomeLastName",
                                MiddleName = "SomeMiddleName",
                                DateOfBirth = DateTime.Parse("01.01.2001"),
                                RegistrationRegionCode = "Moskva",
                                Addresses = new ObservableList<Address>
                                {
                                    new Address
                                    {
                                        AddressTypeCode = AddressTypeCodes.Registration,
                                        Region = "Москва"
                                    }
                                }
                            }
                        }
                    }
                }
            };
        }

        private FakeCustomOutputData ExecuteCustomProcess(
            IServiceProvider serviceProvider,
            Basket basket)
        {
            var customModelExecutor = new CustomModelExecutor();

            var result = customModelExecutor.ExecuteCustomProcess(
                typeof(KaskoSasCheckProcessForLink2).AssemblyQualifiedName,
                new Dictionary<string, object>
                {
                    [KaskoSasCheckProcessForLink2.InputArgument_Basket] = basket,
                    [KaskoSasCheckProcessForLink2.InputArgument_Deal] = basket.Deals[0]
                },
                serviceProvider,
                new Dictionary<string, object>
                {
                    [Link2Constants.IntegrationLoginKey] = "1"
                });

            return result;
        }

        private void AddValidationResultsAggregator(
            Mock<IServiceProvider> serviceProviderMock,
            List<ValidationError> errorCollection)
        {
            var validationResultsAggregatorMock = new Mock<IValidationResultsAggregator>();
            validationResultsAggregatorMock.Setup(x => x.AppendValidationErrors(It.IsAny<ValidationError>()))
                .Callback((ValidationError[] errors) => errorCollection.AddRange(errors));
            serviceProviderMock.Setup(x => x.GetService(typeof(IValidationResultsAggregator)))
                .Returns(validationResultsAggregatorMock.Object);
        }

        private IEntitySettingsManager GetEntitySettingsManager(bool entitySettingValue)
        {
            var mock = new Mock<IEntitySettingsManager>();

            mock
           .Setup(x => x.TryGetSettingValue<bool>(It.IsAny<string>(), It.IsAny<BaseEntity>(), out entitySettingValue, It.IsAny<Func<IEnumerable<EntitySettingConfiguration>, IEnumerable<EntitySettingConfiguration>>>(), null))
           .Returns(true);

            bool isB2CPartner = true;
            mock.Setup(s => s.GetSettingValue<bool>(It.Is<string>(s => s == "IsB2CPartner"), null, null)).Returns(true);

            return mock.Object;
        }

        private Mock<IServiceProvider> CreateServiceProvider(Basket basket, string finDecision, bool entitySettingValue, List<ValidationError> errorCollection)
        {
            var serviceProvider = new Mock<IServiceProvider>();

            var loggerMock = new Mock<ILogger>();
            var invoker = new Mock<IDataServiceInvoker>();
            var adapter = new Mock<ICheckKaskoUpProjectServiceAdapter<Basket, Deal>>();

            adapter.Setup(x => x.FirstCheckBasketAsync(It.Is<Basket>(b => b == basket), It.Is<Deal>(d => d == basket.Deals[0]), It.IsAny<decimal?>(), It.IsAny<string>()))
                .ReturnsAsync(new CheckKaskoUpProjectResultDto()
                {
                    PreDecision = KaskoSasCheckProcessConstants.KaskoSasCheckAccept,
                    SasCoefficient = null
                });
            adapter.Setup(x => x.SecondCheckBasketAsync(It.Is<Basket>(b => b == basket), It.Is<Deal>(d => d == basket.Deals[0]), It.IsAny<string>()))
                .ReturnsAsync(new CheckKaskoUpProjectResultDto()
                {
                    FinDecision = finDecision,
                    SasCoefficient = null
                });

            loggerMock
                .Setup(x => x.ForContext(It.IsAny<string>(), It.IsAny<object>(), It.IsAny<bool>()))
                .Returns(loggerMock.Object);

            serviceProvider.Setup(x => x.GetService(It.Is<Type>(t => t == typeof(ICheckKaskoUpProjectServiceAdapter<Basket, Deal>))))
                .Returns(adapter.Object);
            serviceProvider.Setup(x => x.GetService(It.Is<Type>(t => t == typeof(IAutoCostCalculator))))
                .Returns(new FakeAutoCostCalculator());

            serviceProvider.Setup(m => m.GetService(typeof(IEntitySettingsManager)))
                .Returns(GetEntitySettingsManager(entitySettingValue));

            serviceProvider.Setup(x => x.GetService(typeof(IDataServiceInvoker)))
                .Returns(invoker.Object);

            var queryServiceInvokerMock = new Mock<ICachedQueryServiceInvoker>();
            serviceProvider
                .Setup(s => s.GetService(typeof(ICachedQueryServiceInvoker)))
                .Returns(queryServiceInvokerMock.Object);

            AddValidationResultsAggregator(serviceProvider, errorCollection);

            return serviceProvider;
        }
    }
}