using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using LCU.API.Collections.RepoApi.Models.Responses; using LCU.Shared.Collections.RepoApiLib.Models; using LCU.Shared.Collections.RepoApiLib.Repositories; using Serilog; namespace LCU.API.Collections.RepoApi.Services { public class FactoidService : IFactoidService { private readonly IReportingRepository _reportingRepository; private readonly IRepoAccountRepository _repoAccountRepository; private readonly IImsAutoFileDataRepository _imsAutoFileDataRepository; private const string O4RStatus = "O4R"; private const string RepoStatus = "REPO"; private const string RedeemedStatus = "REDEEMED"; public FactoidService( IReportingRepository reportingRepository, IRepoAccountRepository repoAccountRepository, IImsAutoFileDataRepository imsAutoFileDataRepository) { _reportingRepository = reportingRepository; _repoAccountRepository = repoAccountRepository; _imsAutoFileDataRepository = imsAutoFileDataRepository; } public int FindAuctionIndex(IReadOnlyCollection auctionHouses, string auctionId) { if (auctionId != null) { foreach (var item in auctionHouses.Select((value, index) => new {index, value})) { if (item.value == auctionId.Trim().ToLower()) { return item.index; } } } return auctionHouses.Count - 1; } public async Task GetCurrentInventory() { var accountsInRepo = await _repoAccountRepository.FindByCurrentStatus(RepoStatus); var numCollateral = accountsInRepo.Aggregate(0, (total, curAccount) => total += curAccount.Collaterals.Count()); return new FactoidResponse { CumulativeValue = numCollateral.ToString(), Name = "Vehicles", Description = "Current Inventory (REPO)", FontAwesomeIcon = "fa-car", Records = accountsInRepo }; } public async Task GetAllAccountsO4R() { var filteredRepoAccounts = await _reportingRepository.GetAllAccountsInO4R(); return new FactoidResponse { CumulativeValue = filteredRepoAccounts.Count().ToString(), Name = "Accounts", Description = "Total Accounts in O4R", FontAwesomeIcon = "fa-car", Records = filteredRepoAccounts }; } public async Task GetPercentFloorPrice(DateTime lowDateTime, DateTime highDateTime) { var soldImsAccounts = await _imsAutoFileDataRepository.GetImsSoldRecordsForPeriod(lowDateTime, highDateTime); decimal? percentFloor = 0m; if (soldImsAccounts.Count() != 0) { percentFloor = soldImsAccounts.Select(x => x.GrossPercentFloor).Sum() / soldImsAccounts.Count; } var records = await _repoAccountRepository.FindByRepoIdAsync(soldImsAccounts.Select(x => x.RepoId.ToString()).ToList()); return new FactoidResponse { CumulativeValue = percentFloor?.ToString("#.#") + "%", Name = "Percentage of Floor", Description = "Last 30 Days", FontAwesomeIcon = "fa-money", Records = records }; } public async Task GetRecentlyRedeemed(DateTime lowDate, DateTime highDate) { var filteredRepoAccounts = await _repoAccountRepository.FindByStatusChangeInPeriod(RedeemedStatus, lowDate, highDate); return new FactoidResponse { CumulativeValue = filteredRepoAccounts.Count().ToString(), Name = "Recently Redeemed", Description = "Last 30 Days", FontAwesomeIcon = "fa-calendar", Records = filteredRepoAccounts }; } public async Task GetAvgDaysO4RToREPO(DateTime lowDate, DateTime highDate) { var recentAccountsInREPO = await _repoAccountRepository.FindByStatusChangeInPeriod(RepoStatus, lowDate, highDate); var avgDaysO4RtoRepo = GetAverageDaysBetweenStatuses(recentAccountsInREPO, RepoStatus, O4RStatus); return new FactoidResponse { CumulativeValue = avgDaysO4RtoRepo != null ? avgDaysO4RtoRepo.ToString() : "N/A", Name = "Avg. Days O4R to Repo", Description = "Last 30 Days", FontAwesomeIcon = "fa-calendar", Records = recentAccountsInREPO }; } public async Task GetGetAvgDaysDelqToO4R(DateTime lowDate, DateTime highDate) { IEnumerable recentAccountsInO4R = await _repoAccountRepository.FindByStatusChangeInPeriod(O4RStatus, lowDate, highDate); var accountsWithoutDateDelinquent = recentAccountsInO4R.Where(account => account.DaysDelinquent == null); if (accountsWithoutDateDelinquent.Count() > 0) { Log.Error($"Accounts found without DaysDelinquent when added: {accountsWithoutDateDelinquent} ", accountsWithoutDateDelinquent); recentAccountsInO4R = recentAccountsInO4R.Except(accountsWithoutDateDelinquent); } var totalAccounts = recentAccountsInO4R.Count(); var avgDaysDelqToO4R = 0; if (totalAccounts > 0) { var delqToO4RSum = recentAccountsInO4R.Aggregate(0, (sum, repoAccount) => sum += repoAccount.DaysDelinquent ?? 0); avgDaysDelqToO4R = delqToO4RSum / totalAccounts; } return new FactoidResponse() { CumulativeValue = avgDaysDelqToO4R.ToString(), Name = "Avg. Days Dlqt To O4R", Description = "Last 30 Days", FontAwesomeIcon = "fa-calendar", Records = recentAccountsInO4R.ToList() }; } public async Task GetGetAvgDaysRepoToSold(DateTime lowDate, DateTime highDate) { var recentAccountsSold = await _repoAccountRepository.FindByStatusChangeInPeriod("SOLD", lowDate, highDate); var avgDaysRepoToSold = GetAverageDaysBetweenStatuses(recentAccountsSold, "SOLD", RepoStatus); return new FactoidResponse { CumulativeValue = avgDaysRepoToSold != null ? avgDaysRepoToSold.ToString() : "N/A", Name = "Avg. Days Repo to Sold", Description = "Last 30 Days", FontAwesomeIcon = "fa-calendar", Records = recentAccountsSold }; } public async Task GetCurrentlyOnHold() { var accountsOnHold = await _repoAccountRepository.GetCurrentAccountsOnHoldAtAuction(); return new FactoidResponse { CumulativeValue = accountsOnHold.Count().ToString(), Name = "On Hold at Auction", Description = "Accounts", FontAwesomeIcon = "fa-car", Records = accountsOnHold }; } public async Task GetRecentlyRepoed(DateTime lowDate, DateTime highDate) { var accountsRecentlyRepoed = await _repoAccountRepository.FindByStatusChangeInPeriod(RepoStatus, lowDate, highDate); return new FactoidResponse { CumulativeValue = accountsRecentlyRepoed.Count().ToString(), Name = "Recently Repoed", Description = "Last 30 Days", FontAwesomeIcon = "fa-car", Records = accountsRecentlyRepoed }; } public async Task GetRecentlySold(DateTime lowDate, DateTime highDate) { var accountsRecentlySold = await _repoAccountRepository.FindByStatusChangeInPeriod("SOLD", lowDate, highDate); return new FactoidResponse { CumulativeValue = accountsRecentlySold.Count().ToString(), Name = "Recently Sold", Description = "Last 30 Days", FontAwesomeIcon = "fa-car", Records = accountsRecentlySold }; } // Get the average day difference between two status changes. // If the accounts given don't have valid to and from status, those accounts are ignored from the calulcation. // However, if calling this array, the accounts SHOULD have valid status changes. private int? GetAverageDaysBetweenStatuses(IEnumerable repoAccount, string statusTo, string statusFrom) { IEnumerable daysBetweenStatuses = repoAccount.Select( (account) => GetDaysBetweenStatuses(account, statusTo, statusFrom)); var validRepos = daysBetweenStatuses.Aggregate(0, (sum, days) => sum += days != null ? 1 : 0); var sumOfDays = daysBetweenStatuses.Aggregate(0, (sum, days) => sum += days != null ? days.Value : 0); if (validRepos == 0) { return null; } return sumOfDays / validRepos; } // If either status cannot be found, return null, indicating an that days could not be calculated. // If both the from status and to status are found, floor their difference of dates. private int? GetDaysBetweenStatuses(RepoAccount repoAccount, string newStatus, string oldStatus) { var fromStatusDate = GetLatestStatusChange(repoAccount, oldStatus)?.ChangedDate; var toStatusDate = GetLatestStatusChange(repoAccount, newStatus)?.ChangedDate; if (toStatusDate != null && fromStatusDate != null) { return (int) Math.Floor(toStatusDate.Value.Subtract(fromStatusDate.Value).TotalDays); } return null; } private RepoStatusChange GetLatestStatusChange(RepoAccount repoAccount, string status) { return ( from statusChange in repoAccount.RepoStatusChanges where string.Equals(statusChange.StatusTo, status, StringComparison.CurrentCultureIgnoreCase) select statusChange).LastOrDefault(); } } }