using Microsoft.Extensions.Options; using Serilog; using CRIF.ACTion.Common.Parsers; using CRIF.ACTion.Configuration; using CRIF.ACTion.Decisioning; using CRIF.ACTion.Files; using CRIF.ACTion.Loan; using CRIF.ACTion.Loan.Individuals; using CRIF.ACTion.Services.Landmark.DistributionChannel; using Landmark.OLA.Models; using Landmark.OLA.Models.Decisioning; using Landmark.OLA.Models.Decisioning.Enums; using Landmark.OLA.Models.Individuals; using Landmark.OLA.Models.Loan; namespace CRIF.ACTion.Services.Crif { public class CrifVendorClient : ICrifVendorClient { private readonly string _getCustomFieldsString = "getCustomFields"; private readonly string _getProductDetailsString = "getProducts"; private readonly int retryLimit = 6; private readonly ICrifService _crifService; private readonly IResponseParser _individualResponseParser; private readonly IResponseParser _loanApplicationResponseParser; private readonly IResponseParser _decisioningStateResponseParser; private readonly IResponseParser _decisioningDetailsResponseParser; private readonly IResponseParser> _configurationResponseParser; private readonly IResponseParser> _productDetailsResponseParser; private readonly IResponseParser _customFieldResponseParser; private readonly IResponseParser _dealershipResponseParser; private readonly CrifSettings _settings; public CrifVendorClient(ICrifService crifService, IResponseParser individualResponseParser, IResponseParser loanApplicationResponseParser, IResponseParser decisioningStateResponseParser, IResponseParser decisioningDetailsResponseParser, IResponseParser> crifConfigurationResponseParser, IResponseParser> productDetailsResponseParser, IResponseParser customFieldResponseParser, IResponseParser dealershipResponseParser, IOptions settings) { _crifService = crifService; _individualResponseParser = individualResponseParser; _loanApplicationResponseParser = loanApplicationResponseParser; _decisioningStateResponseParser = decisioningStateResponseParser; _decisioningDetailsResponseParser = decisioningDetailsResponseParser; _configurationResponseParser = crifConfigurationResponseParser; _productDetailsResponseParser = productDetailsResponseParser; _customFieldResponseParser = customFieldResponseParser; _dealershipResponseParser = dealershipResponseParser; _settings = settings.Value; _settings.CustomFields = GetCustomFieldConfiguration().GetAwaiter().GetResult(); } public async Task GetDealershipAuthDetails(string applicationId) { var getApp = new DealershipRequestBuilder(applicationId, _settings); var xmlApp = getApp.Build(); try { var response = await _crifService.SendMessage(xmlApp); return _dealershipResponseParser.Parse(response); } catch (Exception e) { Log.Fatal("Error parsing response: " + e.Message); throw; } } public async Task CreateApplication(LoanApplication loanApplication, IDistributionChannelLookupService channelService = null) { Log.Debug("Building Loan Application"); ModelValidator.Validate(loanApplication); var createApp = new LoanApplicationRequestBuilder(loanApplication, _settings, null, null, null, channelService); var xmlApp = createApp.Build(); var response = await _crifService.SendMessage(xmlApp); try { return _loanApplicationResponseParser.Parse(response); } catch (Exception) { Log.Fatal("Error parsing response."); throw; } } public async Task CreateApplicationAndGetId(LoanApplication loanApplication, IDistributionChannelLookupService channelService = null) { var parsedLoanApplication = await CreateApplication(loanApplication, channelService); return parsedLoanApplication.Id; } public async Task AddFile(string applicationId, LoanApplicationFile file, string fileBase64Value) { try { var addFile = new FileRequestBuilder(applicationId, file, fileBase64Value, _settings); var response = await _crifService.SendMessage(addFile.Build()); return true; } catch (Exception e) { Log.Fatal("Error uploading file to loan application: " + applicationId); Log.Fatal(e.ToString()); } return false; } public async Task AddIndividual(string applicationId, Individual individual) { ModelValidator.Validate(individual); var addIndividual = new AddIndividualRequestBuilder(applicationId, individual, _settings); var response = await _crifService.SendMessage(addIndividual.Build()); try { return _individualResponseParser.Parse(response); } catch (Exception) { Log.Fatal("Error parsing response."); throw; } } public async Task SendToDecisioning(string applicationId) { var sendToDecisioning = new SendToDecisioningRequestBuilder(applicationId, _settings); var response = await _crifService.SendMessage(sendToDecisioning.Build()); //TODO: This cast is not particularly Dependency Injection-y //TODO: Requiring the Application Id is really just a double check, and may not matter all that much ((DecisioningStateResponseParser)_decisioningStateResponseParser).SetLoanId(applicationId); var result = _decisioningStateResponseParser.Parse(response); if (result == DecisioningState.MissingRequiredInformation) { throw new InvalidOperationException("Loan application cannot be sent to decisioning, and is likely missing required information."); } if (result == DecisioningState.Requested || result == DecisioningState.Ready) { return true; } else { return false; } } public async Task GetDecisionStatus(string applicationId) { //TODO: This cast is not particularly Dependency Injection-y //TODO: Requiring the Application Id is really just a double check, and may not matter all that much ((GetApplicationProductsResponseParser)_decisioningDetailsResponseParser).SetLoanId(applicationId); var result = new DecisioningDetails() { DecisioningState = DecisioningState.Unknown }; int retryAttempts = 0; Thread.Sleep(2500); do { Thread.Sleep((retryAttempts * 200) + 1500); var getDecisionStatus = new GetApplicationProductsRequestBuilder(applicationId, _settings); var response = await _crifService.SendMessage(getDecisionStatus.Build()); result = _decisioningDetailsResponseParser.Parse(response); retryAttempts++; } while (retryAttempts < retryLimit && result.DecisioningState == DecisioningState.Unknown); if (retryAttempts >= retryLimit && result.DecisioningState == DecisioningState.Unknown) { result.DecisioningState = DecisioningState.Ready; result.DecisioningResult = DecisioningResult.DecisionStatus_Review; } Log.Information($"Application ID: {applicationId}, Number of Attempts to Get DecisionStatus: {retryAttempts}"); return result; } public async Task> GetBranchNameConfiguration(string ConfigurationSectionName) { var configurationRequest = new ConfigurationRequestBuilder(ConfigurationSectionName, _settings); var response = await _crifService.SendMessage(configurationRequest.Build()); var result = _configurationResponseParser.Parse(response); return result; } public async Task GetCustomFieldConfiguration() { CustomFieldConfigs customFieldConfig = new CustomFieldConfigs(); string municipalityTypeId = null; string municipalityWhereKeptId = null; try { var configurationRequest = new ConfigurationRequestBuilder(_getCustomFieldsString, _settings); var response = await _crifService.SendMessage(configurationRequest.Build()); customFieldConfig = _customFieldResponseParser.Parse(response); if (_settings?.CustomFieldMappings?.CustomFieldID != null) { _settings.CustomFieldMappings.CustomFieldID.TryGetValue("MunicipalityType", out municipalityTypeId); _settings.CustomFieldMappings.CustomFieldID.TryGetValue("MunicipalityWhereKept", out municipalityWhereKeptId); } Log.Information( "CRIF custom field mappings loaded: MunicipalityType={MunicipalityTypeId}, MunicipalityWhereKept={MunicipalityWhereKeptId}", municipalityTypeId ?? "", municipalityWhereKeptId ?? ""); } catch (Exception ex) { Log.Fatal("Error parsing CRIF custom fields configuration response." + ex.StackTrace); } customFieldConfig.ProductIdToCategoryId = await GetProductDetailsConfiguration(); return customFieldConfig; } public async Task> GetProductDetailsConfiguration() { Dictionary mappingList = []; try { var configurationRequest = new ConfigurationRequestBuilder(_getProductDetailsString, _settings); var response = await _crifService.SendMessage(configurationRequest.Build()); mappingList = _productDetailsResponseParser.Parse(response); } catch (Exception ex) { Log.Fatal("Error parsing CRIF custom fields configuration response." + ex.StackTrace); } return mappingList; } } }