using CashLineERP.Application.IslamicFinance.Requests;
using CashLineERP.Domain.IslamicFinance.Requests;
using CashLineERP.SharedKernel.IslamicFinance;
using Microsoft.EntityFrameworkCore;

namespace CashLineERP.Infrastructure.Persistence.Repositories.IslamicFinance;

public sealed class IslamicFinanceRequestRepository : IIslamicFinanceRequestRepository
{
    private readonly CashLineErpDbContext _dbContext;

    public IslamicFinanceRequestRepository(CashLineErpDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public IReadOnlyCollection<IslamicFinanceRequestDetailView> GetRequests(
        string? statusCode = null,
        long? customerPartyId = null,
        long? productProfileId = null)
    {
        var normalizedStatusCode = string.IsNullOrWhiteSpace(statusCode)
            ? null
            : IslamicFinanceWorkflowCatalog.NormalizeStatusCode(statusCode);

        return _dbContext.IslamicFinanceRequests
            .AsNoTracking()
            .Where(entry => normalizedStatusCode == null || entry.StatusCode == normalizedStatusCode)
            .Where(entry => !customerPartyId.HasValue || entry.CustomerPartyId == customerPartyId.Value)
            .Where(entry => !productProfileId.HasValue || entry.ProductProfileId == productProfileId.Value)
            .OrderBy(entry => entry.RequestNumber)
            .Select(Map)
            .ToArray();
    }

    public IslamicFinanceRequestDetailView? GetRequest(long financingRequestId)
    {
        var entry = _dbContext.IslamicFinanceRequests
            .AsNoTracking()
            .SingleOrDefault(request => request.Id == financingRequestId);

        return entry is null ? null : Map(entry);
    }

    public IslamicFinanceRequestDetailView Create(CreateIslamicFinanceRequestCommand command)
    {
        if (!_dbContext.BusinessParties.Any(entry => entry.Id == command.CustomerPartyId))
        {
            throw new InvalidOperationException("Islamic Finance financing request requires a valid customer business party.");
        }

        if (!_dbContext.BusinessParties.Any(entry => entry.Id == command.ApplicantPartyId))
        {
            throw new InvalidOperationException("Islamic Finance financing request requires a valid applicant business party.");
        }

        if (!_dbContext.IslamicFinanceProductProfiles.Any(entry => entry.Id == command.ProductProfileId))
        {
            throw new InvalidOperationException("Islamic Finance financing request requires a valid product profile.");
        }

        if (command.FacilityId.HasValue && !_dbContext.IslamicFinanceFacilities.Any(entry => entry.Id == command.FacilityId.Value))
        {
            throw new InvalidOperationException("Islamic Finance financing request facility reference is not valid.");
        }

        var normalizedRequestNumber = command.RequestNumber.Trim().ToUpperInvariant();
        if (_dbContext.IslamicFinanceRequests.AsNoTracking().Any(entry => entry.RequestNumber == normalizedRequestNumber))
        {
            throw new InvalidOperationException("Islamic Finance financing request number already exists.");
        }

        var entry = new IslamicFinanceRequest(
            normalizedRequestNumber,
            command.CustomerPartyId,
            command.ApplicantPartyId,
            command.ApplicantPartyTypeCode,
            command.ProductProfileId,
            command.FacilityId,
            IslamicFinanceProductCatalog.ParseFundingSourceMode(command.FundingSourceMode),
            command.SourceDocumentType,
            command.SourceDocumentId,
            command.TransactionCurrencyCode,
            command.RequestedAmount);

        _dbContext.IslamicFinanceRequests.Add(entry);
        _dbContext.SaveChanges();
        IslamicFinanceLifecycleOutboxWriter.EnqueueFinancingRequestEvent(
            _dbContext,
            entry,
            IslamicFinanceIntegrationEventCatalog.FinancingRequestCreated,
            entry.CreatedByUserId);
        _dbContext.SaveChanges();
        return GetRequest(entry.Id)!;
    }

    public IslamicFinanceRequestDetailView Submit(long financingRequestId, long actorUserId)
    {
        var entry = _dbContext.IslamicFinanceRequests.Single(request => request.Id == financingRequestId);
        entry.Submit(actorUserId);
        IslamicFinanceLifecycleOutboxWriter.EnqueueFinancingRequestEvent(
            _dbContext,
            entry,
            IslamicFinanceIntegrationEventCatalog.FinancingRequestSubmitted,
            actorUserId);
        _dbContext.SaveChanges();
        return GetRequest(entry.Id)!;
    }

    public IslamicFinanceRequestDetailView Approve(long financingRequestId, long actorUserId)
    {
        var entry = _dbContext.IslamicFinanceRequests.Single(request => request.Id == financingRequestId);
        entry.Approve(actorUserId);
        IslamicFinanceLifecycleOutboxWriter.EnqueueFinancingRequestEvent(
            _dbContext,
            entry,
            IslamicFinanceIntegrationEventCatalog.FinancingRequestApproved,
            actorUserId);
        _dbContext.SaveChanges();
        return GetRequest(entry.Id)!;
    }

    public IslamicFinanceRequestDetailView Return(long financingRequestId, long actorUserId)
    {
        var entry = _dbContext.IslamicFinanceRequests.Single(request => request.Id == financingRequestId);
        entry.Return(actorUserId);
        IslamicFinanceLifecycleOutboxWriter.EnqueueFinancingRequestEvent(
            _dbContext,
            entry,
            IslamicFinanceIntegrationEventCatalog.FinancingRequestReturned,
            actorUserId);
        _dbContext.SaveChanges();
        return GetRequest(entry.Id)!;
    }

    public IslamicFinanceRequestDetailView Reject(long financingRequestId, long actorUserId)
    {
        var entry = _dbContext.IslamicFinanceRequests.Single(request => request.Id == financingRequestId);
        entry.Reject(actorUserId);
        IslamicFinanceLifecycleOutboxWriter.EnqueueFinancingRequestEvent(
            _dbContext,
            entry,
            IslamicFinanceIntegrationEventCatalog.FinancingRequestRejected,
            actorUserId);
        _dbContext.SaveChanges();
        return GetRequest(entry.Id)!;
    }

    public IslamicFinanceRequestDetailView AssignPartnerLender(long financingRequestId, long partnerLenderId, long actorUserId)
    {
        var entry = _dbContext.IslamicFinanceRequests.Single(request => request.Id == financingRequestId);
        ValidatePartnerLenderAssignment(entry, partnerLenderId);

        entry.AssignPartnerLender(partnerLenderId, actorUserId);
        IslamicFinanceLifecycleOutboxWriter.EnqueueFinancingRequestEvent(
            _dbContext,
            entry,
            IslamicFinanceIntegrationEventCatalog.FinancingRequestPartnerAssigned,
            actorUserId);
        _dbContext.SaveChanges();
        return GetRequest(entry.Id)!;
    }

    public IslamicFinanceRequestDetailView SelectOffer(long financingRequestId, long financingDecisionId, long actorUserId)
    {
        var entry = _dbContext.IslamicFinanceRequests.Single(request => request.Id == financingRequestId);
        var decision = _dbContext.IslamicFinanceDecisions
            .AsNoTracking()
            .SingleOrDefault(item => item.Id == financingDecisionId && item.FinancingRequestId == financingRequestId)
            ?? throw new InvalidOperationException("Islamic Finance financing request selected offer reference is not valid.");

        if (!decision.PartnerLenderId.HasValue)
        {
            throw new InvalidOperationException("Islamic Finance financing request selected offer must reference a partner lender.");
        }

        if (!string.Equals(decision.DecisionCode, IslamicFinanceDecisionCatalog.ToCode(IslamicFinanceDecisionCode.Approved), StringComparison.OrdinalIgnoreCase)
            || !string.Equals(decision.StatusCode, IslamicFinanceWorkflowCatalog.Approved, StringComparison.OrdinalIgnoreCase))
        {
            throw new InvalidOperationException("Islamic Finance financing request can only select an approved lender offer.");
        }

        ValidatePartnerLenderAssignment(entry, decision.PartnerLenderId.Value);

        entry.SelectOffer(financingDecisionId, decision.PartnerLenderId.Value, actorUserId);
        IslamicFinanceLifecycleOutboxWriter.EnqueueFinancingRequestEvent(
            _dbContext,
            entry,
            IslamicFinanceIntegrationEventCatalog.FinancingRequestOfferSelected,
            actorUserId);
        _dbContext.SaveChanges();
        return GetRequest(entry.Id)!;
    }

    public IslamicFinanceRequestDetailView AttachShariahReview(long financingRequestId, long shariahReviewCaseId, long actorUserId)
    {
        var reviewCase = _dbContext.IslamicFinanceShariahReviewCases
            .AsNoTracking()
            .SingleOrDefault(entry => entry.Id == shariahReviewCaseId);

        if (reviewCase is null)
        {
            throw new InvalidOperationException("Islamic Finance financing request Shariah review reference is not valid.");
        }

        var entry = _dbContext.IslamicFinanceRequests.Single(request => request.Id == financingRequestId);

        if (reviewCase.ProductProfileId != entry.ProductProfileId)
        {
            throw new InvalidOperationException("Islamic Finance financing request can only attach a Shariah review from the same product profile.");
        }

        if (reviewCase.FinancingRequestId.HasValue && reviewCase.FinancingRequestId.Value != financingRequestId)
        {
            throw new InvalidOperationException("Islamic Finance financing request can only attach a Shariah review that belongs to the same financing request.");
        }

        entry.AttachShariahReview(shariahReviewCaseId, actorUserId);
        IslamicFinanceLifecycleOutboxWriter.EnqueueFinancingRequestEvent(
            _dbContext,
            entry,
            IslamicFinanceIntegrationEventCatalog.FinancingRequestShariahAttached,
            actorUserId);
        _dbContext.SaveChanges();
        return GetRequest(entry.Id)!;
    }

    public IReadOnlyCollection<IslamicFinanceOfficerWorkbenchItemView> GetOfficerWorkbenchItems()
    {
        var nowUtc = DateTime.UtcNow;
        var requests = _dbContext.IslamicFinanceRequests
            .AsNoTracking()
            .Where(entry =>
                string.Equals(entry.ApplicantPartyTypeCode, "CUSTOMER", StringComparison.OrdinalIgnoreCase)
                || string.Equals(entry.ApplicantPartyTypeCode, "SUPPLIER", StringComparison.OrdinalIgnoreCase))
            .OrderByDescending(entry => entry.SubmittedAtUtc ?? entry.CreatedAtUtc)
            .ThenByDescending(entry => entry.Id)
            .ToArray();

        if (requests.Length == 0)
        {
            return [];
        }

        var requestIds = requests.Select(entry => entry.Id).ToArray();
        var applicantPartyIds = requests.Select(entry => entry.ApplicantPartyId)
            .Concat(requests.Select(entry => entry.CustomerPartyId))
            .Distinct()
            .ToArray();
        var productIds = requests.Select(entry => entry.ProductProfileId).Distinct().ToArray();
        var selectedPartnerIds = requests.Where(entry => entry.AssignedPartnerLenderId.HasValue)
            .Select(entry => entry.AssignedPartnerLenderId!.Value)
            .ToArray();
        var selectedDecisionIds = requests.Where(entry => entry.SelectedFinancingDecisionId.HasValue)
            .Select(entry => entry.SelectedFinancingDecisionId!.Value)
            .ToArray();

        var partiesById = _dbContext.BusinessParties
            .AsNoTracking()
            .Where(entry => applicantPartyIds.Contains(entry.Id))
            .ToDictionary(entry => entry.Id);
        var productsById = _dbContext.IslamicFinanceProductProfiles
            .AsNoTracking()
            .Where(entry => productIds.Contains(entry.Id))
            .ToDictionary(entry => entry.Id);
        var lendersById = _dbContext.IslamicFinancePartnerLenders
            .AsNoTracking()
            .Where(entry => selectedPartnerIds.Contains(entry.Id))
            .ToDictionary(entry => entry.Id);
        var lenderNamesById = lendersById.Values
            .Join(
                _dbContext.BusinessParties.AsNoTracking(),
                lender => lender.PartnerLenderPartyId,
                party => party.Id,
                (lender, party) => new { lender.Id, party.PartyName })
            .ToDictionary(entry => entry.Id, entry => entry.PartyName);

        var approvedOfferCountsByRequestId = _dbContext.IslamicFinanceDecisions
            .AsNoTracking()
            .Where(entry => requestIds.Contains(entry.FinancingRequestId)
                && entry.PartnerLenderId.HasValue
                && string.Equals(entry.DecisionCode, IslamicFinanceDecisionCatalog.ToCode(IslamicFinanceDecisionCode.Approved), StringComparison.OrdinalIgnoreCase)
                && string.Equals(entry.StatusCode, IslamicFinanceWorkflowCatalog.Approved, StringComparison.OrdinalIgnoreCase))
            .ToArray()
            .GroupBy(entry => entry.FinancingRequestId)
            .ToDictionary(group => group.Key, group => (long)group.Count());
        var selectedDecisionsById = _dbContext.IslamicFinanceDecisions
            .AsNoTracking()
            .Where(entry => selectedDecisionIds.Contains(entry.Id))
            .ToDictionary(entry => entry.Id);

        return requests
            .Select(entry =>
            {
                var applicantName = partiesById.GetValueOrDefault(entry.ApplicantPartyId)?.PartyName ?? $"Party {entry.ApplicantPartyId}";
                var customerName = partiesById.GetValueOrDefault(entry.CustomerPartyId)?.PartyName ?? $"Party {entry.CustomerPartyId}";
                var product = productsById.GetValueOrDefault(entry.ProductProfileId);
                var selectedPartnerName = entry.AssignedPartnerLenderId.HasValue
                    ? lenderNamesById.GetValueOrDefault(entry.AssignedPartnerLenderId.Value)
                    : null;
                var availableOfferCount = approvedOfferCountsByRequestId.GetValueOrDefault(entry.Id);
                var pendingActionCode = ResolveOfficerPendingAction(entry.StatusCode, availableOfferCount, entry.AssignedPartnerLenderId);
                var slaDueAtUtc = ResolveOfficerSlaDueAtUtc(entry, pendingActionCode);
                var openAgeDays = (int)Math.Max(0d, Math.Floor((nowUtc - (entry.SubmittedAtUtc ?? entry.CreatedAtUtc)).TotalDays));
                var slaStatusCode = ResolveOfficerSlaStatusCode(nowUtc, slaDueAtUtc, pendingActionCode);
                var attentionStatusCode = ResolveOfficerAttentionStatusCode(entry.StatusCode, pendingActionCode, slaStatusCode);

                return new IslamicFinanceOfficerWorkbenchItemView(
                    entry.Id,
                    entry.RequestNumber,
                    entry.ApplicantPartyId,
                    entry.ApplicantPartyTypeCode,
                    applicantName,
                    entry.CustomerPartyId,
                    customerName,
                    entry.ProductProfileId,
                    product?.ProductCode ?? $"PRODUCT-{entry.ProductProfileId}",
                    product?.ProductName ?? $"Product {entry.ProductProfileId}",
                    entry.FundingSourceMode,
                    entry.StatusCode,
                    entry.RequestedAmount,
                    entry.TransactionCurrencyCode,
                    availableOfferCount,
                    entry.SelectedFinancingDecisionId,
                    entry.AssignedPartnerLenderId,
                    selectedPartnerName,
                    entry.SelectedFinancingDecisionId.HasValue ? selectedDecisionsById.GetValueOrDefault(entry.SelectedFinancingDecisionId.Value)?.OfferProductTypeCode : null,
                    entry.SelectedFinancingDecisionId.HasValue ? selectedDecisionsById.GetValueOrDefault(entry.SelectedFinancingDecisionId.Value)?.OfferFundingSourceMode : null,
                    entry.SelectedFinancingDecisionId.HasValue ? selectedDecisionsById.GetValueOrDefault(entry.SelectedFinancingDecisionId.Value)?.OfferedAmount : null,
                    entry.SelectedFinancingDecisionId.HasValue ? selectedDecisionsById.GetValueOrDefault(entry.SelectedFinancingDecisionId.Value)?.TenorMonths : null,
                    pendingActionCode,
                    openAgeDays,
                    attentionStatusCode,
                    slaStatusCode,
                    slaDueAtUtc,
                    entry.CreatedAtUtc,
                    entry.SubmittedAtUtc,
                    entry.ApprovedAtUtc);
            })
            .ToArray();
    }

    public IReadOnlyCollection<IslamicFinanceExecutionWorkbenchItemView> GetExecutionWorkbenchItems()
    {
        var requests = _dbContext.IslamicFinanceRequests
            .AsNoTracking()
            .Where(entry =>
                (string.Equals(entry.ApplicantPartyTypeCode, "CUSTOMER", StringComparison.OrdinalIgnoreCase)
                    || string.Equals(entry.ApplicantPartyTypeCode, "SUPPLIER", StringComparison.OrdinalIgnoreCase))
                && (entry.SelectedFinancingDecisionId.HasValue
                    || _dbContext.IslamicFinanceMurabahaCases.Any(item => item.FinancingRequestId == entry.Id)
                    || _dbContext.IslamicFinanceReceivablesCases.Any(item => item.FinancingRequestId == entry.Id)
                    || _dbContext.IslamicFinanceSettlementHeaders.Any(item => item.FinancingRequestId == entry.Id)))
            .OrderByDescending(entry => entry.SelectedOfferAtUtc ?? entry.ApprovedAtUtc ?? entry.CreatedAtUtc)
            .ThenByDescending(entry => entry.Id)
            .ToArray();

        if (requests.Length == 0)
        {
            return [];
        }

        var requestIds = requests.Select(entry => entry.Id).ToArray();
        var applicantPartyIds = requests.Select(entry => entry.ApplicantPartyId).Distinct().ToArray();
        var productIds = requests.Select(entry => entry.ProductProfileId).Distinct().ToArray();
        var selectedDecisionIds = requests.Where(entry => entry.SelectedFinancingDecisionId.HasValue)
            .Select(entry => entry.SelectedFinancingDecisionId!.Value)
            .ToArray();
        var selectedPartnerIds = requests.Where(entry => entry.AssignedPartnerLenderId.HasValue)
            .Select(entry => entry.AssignedPartnerLenderId!.Value)
            .Distinct()
            .ToArray();

        var partiesById = _dbContext.BusinessParties
            .AsNoTracking()
            .Where(entry => applicantPartyIds.Contains(entry.Id))
            .ToDictionary(entry => entry.Id);
        var productsById = _dbContext.IslamicFinanceProductProfiles
            .AsNoTracking()
            .Where(entry => productIds.Contains(entry.Id))
            .ToDictionary(entry => entry.Id);
        var selectedDecisionsById = _dbContext.IslamicFinanceDecisions
            .AsNoTracking()
            .Where(entry => selectedDecisionIds.Contains(entry.Id))
            .ToDictionary(entry => entry.Id);
        var lenderNamesById = _dbContext.IslamicFinancePartnerLenders
            .AsNoTracking()
            .Where(entry => selectedPartnerIds.Contains(entry.Id))
            .Join(
                _dbContext.BusinessParties.AsNoTracking(),
                lender => lender.PartnerLenderPartyId,
                party => party.Id,
                (lender, party) => new { lender.Id, party.PartyName })
            .ToDictionary(entry => entry.Id, entry => entry.PartyName);

        var murabahaCasesByRequestId = _dbContext.IslamicFinanceMurabahaCases
            .AsNoTracking()
            .Where(entry => requestIds.Contains(entry.FinancingRequestId))
            .ToArray()
            .GroupBy(entry => entry.FinancingRequestId)
            .ToDictionary(group => group.Key, group => group.OrderByDescending(item => item.Id).First());
        var receivablesCasesByRequestId = _dbContext.IslamicFinanceReceivablesCases
            .AsNoTracking()
            .Where(entry => requestIds.Contains(entry.FinancingRequestId))
            .ToArray()
            .GroupBy(entry => entry.FinancingRequestId)
            .ToDictionary(group => group.Key, group => group.OrderByDescending(item => item.Id).First());
        var disbursementsByRequestId =
            (from disbursement in _dbContext.IslamicFinanceMurabahaDisbursements.AsNoTracking()
             join murabahaCase in _dbContext.IslamicFinanceMurabahaCases.AsNoTracking()
                on disbursement.MurabahaCaseId equals murabahaCase.Id
             where requestIds.Contains(murabahaCase.FinancingRequestId)
             select new { murabahaCase.FinancingRequestId, Disbursement = disbursement })
            .ToArray()
            .GroupBy(entry => entry.FinancingRequestId)
            .ToDictionary(group => group.Key, group => group.OrderByDescending(item => item.Disbursement.Id).First().Disbursement);
        var settlementsByRequestId = _dbContext.IslamicFinanceSettlementHeaders
            .AsNoTracking()
            .Where(entry => requestIds.Contains(entry.FinancingRequestId))
            .ToArray()
            .GroupBy(entry => entry.FinancingRequestId)
            .ToDictionary(group => group.Key, group => group.OrderByDescending(item => item.Id).First());

        var externalNotificationCounts = _dbContext.NotificationQueueItems
            .AsNoTracking()
            .Where(entry => entry.RelatedEntityType == "FINANCING_REQUEST"
                && entry.RelatedEntityId.HasValue
                && requestIds.Contains(entry.RelatedEntityId.Value)
                && (entry.StatusCode == "PENDING" || entry.StatusCode == "RETRY")
                && entry.RecipientReference.StartsWith("PARTY:"))
            .ToArray()
            .GroupBy(entry => entry.RelatedEntityId!.Value)
            .ToDictionary(group => group.Key, group => group.Count());
        var partnerNotificationCounts = _dbContext.NotificationQueueItems
            .AsNoTracking()
            .Where(entry => entry.RelatedEntityType == "FINANCING_REQUEST"
                && entry.RelatedEntityId.HasValue
                && requestIds.Contains(entry.RelatedEntityId.Value)
                && (entry.StatusCode == "PENDING" || entry.StatusCode == "RETRY")
                && entry.RecipientReference.StartsWith("PARTNER_LENDER:"))
            .ToArray()
            .GroupBy(entry => entry.RelatedEntityId!.Value)
            .ToDictionary(group => group.Key, group => group.Count());

        return requests.Select(entry =>
        {
            var selectedDecision = entry.SelectedFinancingDecisionId.HasValue
                ? selectedDecisionsById.GetValueOrDefault(entry.SelectedFinancingDecisionId.Value)
                : null;
            var murabahaCase = murabahaCasesByRequestId.GetValueOrDefault(entry.Id);
            var receivablesCase = receivablesCasesByRequestId.GetValueOrDefault(entry.Id);
            var disbursement = disbursementsByRequestId.GetValueOrDefault(entry.Id);
            var settlement = settlementsByRequestId.GetValueOrDefault(entry.Id);
            var executionStageCode = ResolveExecutionWorkbenchStage(murabahaCase, receivablesCase, disbursement, settlement, entry.SelectedFinancingDecisionId);
            var pendingExecutionActionCode = ResolveExecutionWorkbenchAction(murabahaCase, receivablesCase, disbursement, settlement, entry.SelectedFinancingDecisionId);

            return new IslamicFinanceExecutionWorkbenchItemView(
                entry.Id,
                entry.RequestNumber,
                entry.ApplicantPartyId,
                entry.ApplicantPartyTypeCode,
                partiesById.GetValueOrDefault(entry.ApplicantPartyId)?.PartyName ?? $"Party {entry.ApplicantPartyId}",
                entry.ProductProfileId,
                productsById.GetValueOrDefault(entry.ProductProfileId)?.ProductType ?? "UNKNOWN",
                productsById.GetValueOrDefault(entry.ProductProfileId)?.ProductName ?? $"Product {entry.ProductProfileId}",
                entry.FundingSourceMode,
                entry.StatusCode,
                entry.SelectedFinancingDecisionId,
                entry.AssignedPartnerLenderId,
                entry.AssignedPartnerLenderId.HasValue ? lenderNamesById.GetValueOrDefault(entry.AssignedPartnerLenderId.Value) : null,
                selectedDecision?.OfferedAmount,
                selectedDecision?.TenorMonths,
                executionStageCode,
                pendingExecutionActionCode,
                externalNotificationCounts.GetValueOrDefault(entry.Id),
                partnerNotificationCounts.GetValueOrDefault(entry.Id),
                murabahaCase?.Id,
                receivablesCase?.Id,
                disbursement?.Id,
                settlement?.Id,
                entry.SelectedOfferAtUtc,
                entry.CreatedAtUtc);
        }).ToArray();
    }

    public IslamicFinancePartnerOrchestrationView? GetPartnerOrchestration(long financingRequestId)
    {
        var request = _dbContext.IslamicFinanceRequests
            .AsNoTracking()
            .SingleOrDefault(entry => entry.Id == financingRequestId);

        if (request is null)
        {
            return null;
        }

        var product = _dbContext.IslamicFinanceProductProfiles
            .AsNoTracking()
            .SingleOrDefault(entry => entry.Id == request.ProductProfileId);

        var partnerLenders = _dbContext.IslamicFinancePartnerLenders
            .AsNoTracking()
            .Where(entry => entry.IsActive && entry.StatusCode == IslamicFinanceWorkflowCatalog.Active)
            .ToArray();

        var partnerNamesById = partnerLenders
            .Join(
                _dbContext.BusinessParties.AsNoTracking(),
                lender => lender.PartnerLenderPartyId,
                party => party.Id,
                (lender, party) => new { lender.Id, party.PartyName })
            .ToDictionary(entry => entry.Id, entry => entry.PartyName);

        var approvedDecisions = _dbContext.IslamicFinanceDecisions
            .AsNoTracking()
            .Where(entry => entry.FinancingRequestId == financingRequestId
                && entry.PartnerLenderId.HasValue
                && string.Equals(entry.DecisionCode, IslamicFinanceDecisionCatalog.ToCode(IslamicFinanceDecisionCode.Approved), StringComparison.OrdinalIgnoreCase)
                && string.Equals(entry.StatusCode, IslamicFinanceWorkflowCatalog.Approved, StringComparison.OrdinalIgnoreCase))
            .ToArray();

        var candidatePartners = partnerLenders
            .Select(entry =>
            {
                var supportsProduct = product is not null && entry.SupportsProduct(product.ProductTypeValue);
                var supportsFundingMode = entry.SupportsFundingSource(request.FundingSourceModeValue);
                var hasApprovedOffer = approvedDecisions.Any(decision => decision.PartnerLenderId == entry.Id);
                var isSelectedOffer = request.SelectedFinancingDecisionId.HasValue
                    && approvedDecisions.Any(decision => decision.Id == request.SelectedFinancingDecisionId.Value && decision.PartnerLenderId == entry.Id);

                return new IslamicFinancePartnerOrchestrationPartnerView(
                    entry.Id,
                    partnerNamesById.GetValueOrDefault(entry.Id) ?? $"Partner {entry.Id}",
                    entry.DefaultCurrencyCode,
                    supportsProduct,
                    supportsFundingMode,
                    request.AssignedPartnerLenderId == entry.Id,
                    hasApprovedOffer,
                    isSelectedOffer);
            })
            .OrderByDescending(entry => entry.IsAssignedPartner)
            .ThenByDescending(entry => entry.HasApprovedOffer)
            .ThenBy(entry => entry.PartnerLenderName, StringComparer.OrdinalIgnoreCase)
            .ToArray();

        var compatiblePartnerCount = candidatePartners.LongCount(entry => entry.SupportsProductType && entry.SupportsFundingMode);
        var approvedOfferCount = approvedDecisions.LongLength;
        var officerNotificationCount = _dbContext.NotificationQueueItems
            .AsNoTracking()
            .LongCount(entry => entry.RelatedEntityType == "FINANCING_REQUEST"
                && entry.RelatedEntityId == financingRequestId
                && entry.RecipientReference == "ROLE:ISLAMIC_FINANCE_OFFICER"
                && (entry.StatusCode == "PENDING" || entry.StatusCode == "RETRY"));

        var nextInternalActionCode = ResolvePartnerOrchestrationNextAction(request, compatiblePartnerCount, approvedOfferCount);
        var orchestrationStageCode = ResolvePartnerOrchestrationStage(request, approvedOfferCount);

        return new IslamicFinancePartnerOrchestrationView(
            request.Id,
            request.RequestNumber,
            request.ApplicantPartyTypeCode,
            product?.ProductType ?? "UNKNOWN",
            request.FundingSourceMode,
            request.StatusCode,
            orchestrationStageCode,
            nextInternalActionCode,
            request.AssignedPartnerLenderId,
            request.AssignedPartnerLenderId.HasValue ? partnerNamesById.GetValueOrDefault(request.AssignedPartnerLenderId.Value) : null,
            compatiblePartnerCount,
            approvedOfferCount,
            officerNotificationCount,
            request.SelectedFinancingDecisionId,
            candidatePartners);
    }

    public IReadOnlyCollection<IslamicFinanceRequestHistoryEntryView> GetRequestHistory(long financingRequestId)
    {
        var request = _dbContext.IslamicFinanceRequests
            .AsNoTracking()
            .SingleOrDefault(entry => entry.Id == financingRequestId);

        if (request is null)
        {
            return Array.Empty<IslamicFinanceRequestHistoryEntryView>();
        }

        var events = new List<IslamicFinanceRequestHistoryEntryView>
        {
            new(
                request.Id,
                "REQUEST_CREATED",
                "FINANCING_REQUEST",
                request.Id,
                request.CreatedByUserId,
                request.RequestNumber,
                request.CreatedAtUtc)
        };

        AddIfPresent(events, request.Id, "REQUEST_SUBMITTED", "FINANCING_REQUEST", request.Id, request.SubmittedByUserId, null, request.SubmittedAtUtc);
        AddIfPresent(events, request.Id, "REQUEST_APPROVED", "FINANCING_REQUEST", request.Id, request.ApprovedByUserId, null, request.ApprovedAtUtc);
        AddIfPresent(events, request.Id, "REQUEST_RETURNED", "FINANCING_REQUEST", request.Id, request.ReturnedByUserId, null, request.ReturnedAtUtc);
        AddIfPresent(events, request.Id, "REQUEST_REJECTED", "FINANCING_REQUEST", request.Id, request.RejectedByUserId, null, request.RejectedAtUtc);
        AddIfPresent(events, request.Id, "PARTNER_LENDER_ASSIGNED", "FINANCING_REQUEST", request.AssignedPartnerLenderId, request.AssignedPartnerLenderByUserId, null, request.AssignedPartnerLenderAtUtc);
        AddIfPresent(events, request.Id, "OFFER_SELECTED", "FINANCING_DECISION", request.SelectedFinancingDecisionId, request.SelectedOfferByUserId, null, request.SelectedOfferAtUtc);
        AddIfPresent(events, request.Id, "SHARIAH_REVIEW_ATTACHED", "SHARIAH_REVIEW", request.ShariahReviewCaseId, request.ShariahReviewAttachedByUserId, null, request.ShariahReviewAttachedAtUtc);

        var decisions = _dbContext.IslamicFinanceDecisions
            .AsNoTracking()
            .Where(entry => entry.FinancingRequestId == financingRequestId)
            .OrderBy(entry => entry.Id)
            .ToArray();

        foreach (var decision in decisions)
        {
            events.Add(new IslamicFinanceRequestHistoryEntryView(
                financingRequestId,
                "DECISION_CREATED",
                "FINANCING_DECISION",
                decision.Id,
                decision.CreatedByUserId,
                IslamicFinanceDecisionCatalog.NormalizeDecisionCode(decision.DecisionCode),
                decision.CreatedAtUtc));

            AddIfPresent(events, financingRequestId, "DECISION_SUBMITTED", "FINANCING_DECISION", decision.Id, decision.SubmittedByUserId, null, decision.SubmittedAtUtc);
            AddIfPresent(events, financingRequestId, "DECISION_APPROVED", "FINANCING_DECISION", decision.Id, decision.ApprovedByUserId, null, decision.ApprovedAtUtc);
            AddIfPresent(events, financingRequestId, "DECISION_RETURNED", "FINANCING_DECISION", decision.Id, decision.ReturnedByUserId, null, decision.ReturnedAtUtc);
            AddIfPresent(events, financingRequestId, "DECISION_REJECTED", "FINANCING_DECISION", decision.Id, decision.RejectedByUserId, null, decision.RejectedAtUtc);
        }

        return events.OrderBy(entry => entry.OccurredAtUtc).ThenBy(entry => entry.EventCode).ToArray();
    }

    public IslamicFinanceRequestAuditView? GetRequestAudit(long financingRequestId)
    {
        var request = GetRequest(financingRequestId);
        if (request is null)
        {
            return null;
        }

        return new IslamicFinanceRequestAuditView(
            request,
            GetDecisions(financingRequestId),
            GetRequestHistory(financingRequestId));
    }

    public IReadOnlyCollection<IslamicFinanceDecisionDetailView> GetDecisions(long financingRequestId) =>
        _dbContext.IslamicFinanceDecisions
            .AsNoTracking()
            .Where(entry => entry.FinancingRequestId == financingRequestId)
            .OrderBy(entry => entry.Id)
            .Select(Map)
            .ToArray();

    public IslamicFinanceDecisionDetailView? GetDecision(long financingDecisionId)
    {
        var entry = _dbContext.IslamicFinanceDecisions
            .AsNoTracking()
            .SingleOrDefault(decision => decision.Id == financingDecisionId);

        return entry is null ? null : Map(entry);
    }

    public IslamicFinanceDecisionAuditView? GetDecisionAudit(long financingDecisionId)
    {
        var decision = GetDecision(financingDecisionId);
        if (decision is null)
        {
            return null;
        }

        var events = new List<IslamicFinanceRequestHistoryEntryView>
        {
            new(
                decision.FinancingRequestId,
                "DECISION_CREATED",
                "FINANCING_DECISION",
                decision.FinancingDecisionId,
                decision.CreatedByUserId,
                decision.DecisionCode,
                decision.CreatedAtUtc)
        };

        AddIfPresent(events, decision.FinancingRequestId, "DECISION_SUBMITTED", "FINANCING_DECISION", decision.FinancingDecisionId, decision.SubmittedByUserId, null, decision.SubmittedAtUtc);
        AddIfPresent(events, decision.FinancingRequestId, "DECISION_APPROVED", "FINANCING_DECISION", decision.FinancingDecisionId, decision.ApprovedByUserId, null, decision.ApprovedAtUtc);
        AddIfPresent(events, decision.FinancingRequestId, "DECISION_RETURNED", "FINANCING_DECISION", decision.FinancingDecisionId, decision.ReturnedByUserId, null, decision.ReturnedAtUtc);
        AddIfPresent(events, decision.FinancingRequestId, "DECISION_REJECTED", "FINANCING_DECISION", decision.FinancingDecisionId, decision.RejectedByUserId, null, decision.RejectedAtUtc);

        return new IslamicFinanceDecisionAuditView(
            decision,
            events.OrderBy(entry => entry.OccurredAtUtc).ThenBy(entry => entry.EventCode).ToArray());
    }

    public IslamicFinanceDecisionDetailView CreateDecision(CreateIslamicFinanceDecisionCommand command)
    {
        if (!_dbContext.IslamicFinanceRequests.Any(entry => entry.Id == command.FinancingRequestId))
        {
            throw new InvalidOperationException("Islamic Finance decision requires a valid financing request.");
        }

        if (command.DecidingPartyId.HasValue && !_dbContext.BusinessParties.Any(entry => entry.Id == command.DecidingPartyId.Value))
        {
            throw new InvalidOperationException("Islamic Finance decision deciding party reference is not valid.");
        }

        if (command.PartnerLenderId.HasValue && !_dbContext.IslamicFinancePartnerLenders.Any(entry => entry.Id == command.PartnerLenderId.Value))
        {
            throw new InvalidOperationException("Islamic Finance decision partner lender reference is not valid.");
        }

        var request = _dbContext.IslamicFinanceRequests
            .AsNoTracking()
            .Single(entry => entry.Id == command.FinancingRequestId);
        var product = _dbContext.IslamicFinanceProductProfiles
            .AsNoTracking()
            .Single(entry => entry.Id == request.ProductProfileId);

        var entry = new IslamicFinanceDecision(
            command.FinancingRequestId,
            IslamicFinanceDecisionCatalog.ParseDecisionCode(command.DecisionCode),
            command.DecisionNotes,
            command.DecidingPartyId,
            command.PartnerLenderId,
            string.IsNullOrWhiteSpace(command.OfferProductTypeCode) ? product.ProductType : command.OfferProductTypeCode,
            string.IsNullOrWhiteSpace(command.OfferFundingSourceMode) ? request.FundingSourceMode : command.OfferFundingSourceMode,
            string.IsNullOrWhiteSpace(command.OfferCurrencyCode) ? request.TransactionCurrencyCode : command.OfferCurrencyCode,
            command.OfferedAmount ?? request.RequestedAmount,
            command.TenorMonths,
            command.ProfitRatePercent,
            command.ProcessingFeeAmount,
            command.SecurityRequirementText,
            command.OfferRank);

        _dbContext.IslamicFinanceDecisions.Add(entry);
        _dbContext.SaveChanges();
        var requestNumber = _dbContext.IslamicFinanceRequests
            .AsNoTracking()
            .Where(request => request.Id == command.FinancingRequestId)
            .Select(request => request.RequestNumber)
            .Single();
        IslamicFinanceLifecycleOutboxWriter.EnqueueFinancingDecisionEvent(
            _dbContext,
            entry,
            requestNumber,
            IslamicFinanceIntegrationEventCatalog.FinancingDecisionCreated,
            entry.CreatedByUserId);
        _dbContext.SaveChanges();
        return GetDecision(entry.Id)!;
    }

    public IslamicFinanceDecisionDetailView SubmitDecision(long financingDecisionId, long actorUserId)
    {
        var entry = _dbContext.IslamicFinanceDecisions.Single(decision => decision.Id == financingDecisionId);
        entry.Submit(actorUserId);
        var requestNumber = _dbContext.IslamicFinanceRequests
            .AsNoTracking()
            .Where(request => request.Id == entry.FinancingRequestId)
            .Select(request => request.RequestNumber)
            .Single();
        IslamicFinanceLifecycleOutboxWriter.EnqueueFinancingDecisionEvent(
            _dbContext,
            entry,
            requestNumber,
            IslamicFinanceIntegrationEventCatalog.FinancingDecisionSubmitted,
            actorUserId);
        _dbContext.SaveChanges();
        return GetDecision(entry.Id)!;
    }

    public IslamicFinanceDecisionDetailView ApproveDecision(long financingDecisionId, long actorUserId)
    {
        var entry = _dbContext.IslamicFinanceDecisions.Single(decision => decision.Id == financingDecisionId);
        entry.Approve(actorUserId);
        var requestNumber = _dbContext.IslamicFinanceRequests
            .AsNoTracking()
            .Where(request => request.Id == entry.FinancingRequestId)
            .Select(request => request.RequestNumber)
            .Single();
        IslamicFinanceLifecycleOutboxWriter.EnqueueFinancingDecisionEvent(
            _dbContext,
            entry,
            requestNumber,
            IslamicFinanceIntegrationEventCatalog.FinancingDecisionApproved,
            actorUserId);
        _dbContext.SaveChanges();
        return GetDecision(entry.Id)!;
    }

    public IslamicFinanceDecisionDetailView ReturnDecision(long financingDecisionId, long actorUserId)
    {
        var entry = _dbContext.IslamicFinanceDecisions.Single(decision => decision.Id == financingDecisionId);
        entry.Return(actorUserId);
        var requestNumber = _dbContext.IslamicFinanceRequests
            .AsNoTracking()
            .Where(request => request.Id == entry.FinancingRequestId)
            .Select(request => request.RequestNumber)
            .Single();
        IslamicFinanceLifecycleOutboxWriter.EnqueueFinancingDecisionEvent(
            _dbContext,
            entry,
            requestNumber,
            IslamicFinanceIntegrationEventCatalog.FinancingDecisionReturned,
            actorUserId);
        _dbContext.SaveChanges();
        return GetDecision(entry.Id)!;
    }

    public IslamicFinanceDecisionDetailView RejectDecision(long financingDecisionId, long actorUserId)
    {
        var entry = _dbContext.IslamicFinanceDecisions.Single(decision => decision.Id == financingDecisionId);
        entry.Reject(actorUserId);
        var requestNumber = _dbContext.IslamicFinanceRequests
            .AsNoTracking()
            .Where(request => request.Id == entry.FinancingRequestId)
            .Select(request => request.RequestNumber)
            .Single();
        IslamicFinanceLifecycleOutboxWriter.EnqueueFinancingDecisionEvent(
            _dbContext,
            entry,
            requestNumber,
            IslamicFinanceIntegrationEventCatalog.FinancingDecisionRejected,
            actorUserId);
        _dbContext.SaveChanges();
        return GetDecision(entry.Id)!;
    }

    private static IslamicFinanceRequestDetailView Map(IslamicFinanceRequest entry) =>
        new(
            entry.Id,
            entry.RequestNumber,
            entry.CustomerPartyId,
            entry.ApplicantPartyId,
            entry.ApplicantPartyTypeCode,
            entry.ProductProfileId,
            entry.FacilityId,
            IslamicFinanceProductCatalog.NormalizeFundingSourceMode(entry.FundingSourceMode),
            entry.SourceDocumentType,
            entry.SourceDocumentId,
            entry.TransactionCurrencyCode,
            entry.RequestedAmount,
            IslamicFinanceWorkflowCatalog.NormalizeStatusCode(entry.StatusCode),
            entry.WorkflowInstanceId,
            entry.ShariahReviewCaseId,
            entry.AssignedPartnerLenderId,
            entry.SelectedFinancingDecisionId,
            entry.CreatedAtUtc,
            entry.CreatedByUserId,
            entry.SubmittedAtUtc,
            entry.SubmittedByUserId,
            entry.ApprovedAtUtc,
            entry.ApprovedByUserId,
            entry.ReturnedAtUtc,
            entry.ReturnedByUserId,
            entry.RejectedAtUtc,
            entry.RejectedByUserId,
            entry.AssignedPartnerLenderAtUtc,
            entry.AssignedPartnerLenderByUserId,
            entry.SelectedOfferAtUtc,
            entry.SelectedOfferByUserId,
            entry.ShariahReviewAttachedAtUtc,
            entry.ShariahReviewAttachedByUserId,
            entry.UpdatedAtUtc,
            entry.UpdatedByUserId);

    private static IslamicFinanceDecisionDetailView Map(IslamicFinanceDecision entry) =>
        new(
            entry.Id,
            entry.FinancingRequestId,
            IslamicFinanceDecisionCatalog.NormalizeDecisionCode(entry.DecisionCode),
            entry.DecisionNotes,
            entry.DecidingPartyId,
            entry.PartnerLenderId,
            entry.OfferProductTypeCode,
            entry.OfferFundingSourceMode,
            entry.OfferCurrencyCode,
            entry.OfferedAmount,
            entry.TenorMonths,
            entry.ProfitRatePercent,
            entry.ProcessingFeeAmount,
            entry.SecurityRequirementText,
            entry.OfferRank,
            IslamicFinanceWorkflowCatalog.NormalizeStatusCode(entry.StatusCode),
            entry.CreatedAtUtc,
            entry.CreatedByUserId,
            entry.SubmittedAtUtc,
            entry.SubmittedByUserId,
            entry.ApprovedAtUtc,
            entry.ApprovedByUserId,
            entry.ReturnedAtUtc,
            entry.ReturnedByUserId,
            entry.RejectedAtUtc,
            entry.RejectedByUserId,
            entry.UpdatedAtUtc,
            entry.UpdatedByUserId);

    private void ValidatePartnerLenderAssignment(IslamicFinanceRequest entry, long partnerLenderId)
    {
        var partnerLender = _dbContext.IslamicFinancePartnerLenders
            .AsNoTracking()
            .SingleOrDefault(item => item.Id == partnerLenderId);

        if (partnerLender is null)
        {
            throw new InvalidOperationException("Islamic Finance financing request partner lender reference is not valid.");
        }

        if (!partnerLender.IsActive
            || !string.Equals(partnerLender.StatusCode, IslamicFinanceWorkflowCatalog.Active, StringComparison.OrdinalIgnoreCase))
        {
            throw new InvalidOperationException("Islamic Finance financing request can only assign an active partner lender.");
        }

        var product = _dbContext.IslamicFinanceProductProfiles
            .AsNoTracking()
            .SingleOrDefault(profile => profile.Id == entry.ProductProfileId);

        if (product is null)
        {
            throw new InvalidOperationException("Islamic Finance financing request product profile reference is not valid.");
        }

        if (!product.SupportsFundingSource(entry.FundingSourceModeValue)
            || !partnerLender.SupportsFundingSource(entry.FundingSourceModeValue))
        {
            throw new InvalidOperationException("Islamic Finance financing request partner-lender assignment does not support the request funding mode.");
        }

        if (product.ProductTypeValue == IslamicFinanceProductType.Murabaha && !partnerLender.SupportsMurabaha
            || product.ProductTypeValue == IslamicFinanceProductType.ReceivablesFinance && !partnerLender.SupportsReceivablesFinance
            || product.ProductTypeValue == IslamicFinanceProductType.Tawarruq && !partnerLender.SupportsTawarruq)
        {
            throw new InvalidOperationException("Islamic Finance financing request partner lender does not support the request product type.");
        }

        if (!entry.FacilityId.HasValue)
        {
            return;
        }

        var facility = _dbContext.IslamicFinanceFacilities
            .AsNoTracking()
            .SingleOrDefault(item => item.Id == entry.FacilityId.Value);

        if (facility is null)
        {
            throw new InvalidOperationException("Islamic Finance financing request facility reference is not valid.");
        }

        if (!facility.IsActive
            || !string.Equals(facility.StatusCode, IslamicFinanceWorkflowCatalog.Active, StringComparison.OrdinalIgnoreCase))
        {
            throw new InvalidOperationException("Islamic Finance financing request partner-lender assignment requires an active linked facility.");
        }

        if (facility.PartnerLenderId != partnerLenderId
            || facility.CustomerPartyId != entry.CustomerPartyId
            || facility.ProductProfileId != entry.ProductProfileId
            || !string.Equals(facility.CurrencyCode, entry.TransactionCurrencyCode, StringComparison.OrdinalIgnoreCase))
        {
            throw new InvalidOperationException("Islamic Finance financing request partner-lender assignment must match the linked facility partner, customer, product, and currency.");
        }
    }

    private static string ResolveOfficerPendingAction(string statusCode, long availableOfferCount, long? selectedPartnerLenderId)
    {
        if (string.Equals(statusCode, IslamicFinanceWorkflowCatalog.Submitted, StringComparison.OrdinalIgnoreCase))
        {
            return "REVIEW_APPLICATION";
        }

        if (string.Equals(statusCode, IslamicFinanceWorkflowCatalog.Returned, StringComparison.OrdinalIgnoreCase))
        {
            return "FOLLOW_UP_EXTERNAL_PARTY";
        }

        if (string.Equals(statusCode, IslamicFinanceWorkflowCatalog.Approved, StringComparison.OrdinalIgnoreCase) && availableOfferCount > 0 && !selectedPartnerLenderId.HasValue)
        {
            return "AWAIT_EXTERNAL_OFFER_SELECTION";
        }

        if (string.Equals(statusCode, IslamicFinanceWorkflowCatalog.Assigned, StringComparison.OrdinalIgnoreCase))
        {
            return "ADVANCE_EXECUTION_WORKFLOW";
        }

        if (string.Equals(statusCode, IslamicFinanceWorkflowCatalog.Rejected, StringComparison.OrdinalIgnoreCase))
        {
            return "CLOSED_REJECTED";
        }

        return "MONITOR";
    }

    private static string ResolvePartnerOrchestrationStage(IslamicFinanceRequest request, long approvedOfferCount)
    {
        if (request.SelectedFinancingDecisionId.HasValue)
        {
            return "OFFER_SELECTED";
        }

        if (approvedOfferCount > 0)
        {
            return "OFFERS_PUBLISHED";
        }

        if (request.AssignedPartnerLenderId.HasValue)
        {
            return "PARTNER_ASSIGNED";
        }

        if (string.Equals(request.StatusCode, IslamicFinanceWorkflowCatalog.Submitted, StringComparison.OrdinalIgnoreCase))
        {
            return "APPLICATION_REVIEW";
        }

        if (string.Equals(request.StatusCode, IslamicFinanceWorkflowCatalog.Rejected, StringComparison.OrdinalIgnoreCase))
        {
            return "CLOSED_REJECTED";
        }

        return "APPLICATION_INTAKE";
    }

    private static string ResolvePartnerOrchestrationNextAction(IslamicFinanceRequest request, long compatiblePartnerCount, long approvedOfferCount)
    {
        if (string.Equals(request.StatusCode, IslamicFinanceWorkflowCatalog.Submitted, StringComparison.OrdinalIgnoreCase))
        {
            return "REVIEW_APPLICATION";
        }

        if (string.Equals(request.StatusCode, IslamicFinanceWorkflowCatalog.Returned, StringComparison.OrdinalIgnoreCase))
        {
            return "WAIT_FOR_EXTERNAL_RESUBMISSION";
        }

        if (string.Equals(request.StatusCode, IslamicFinanceWorkflowCatalog.Rejected, StringComparison.OrdinalIgnoreCase))
        {
            return "CLOSED_REJECTED";
        }

        if (string.Equals(request.StatusCode, IslamicFinanceWorkflowCatalog.Approved, StringComparison.OrdinalIgnoreCase)
            && !request.AssignedPartnerLenderId.HasValue)
        {
            return compatiblePartnerCount > 0 ? "ASSIGN_PARTNER_LENDER" : "RESOLVE_LENDER_GAP";
        }

        if ((string.Equals(request.StatusCode, IslamicFinanceWorkflowCatalog.Approved, StringComparison.OrdinalIgnoreCase)
                || string.Equals(request.StatusCode, IslamicFinanceWorkflowCatalog.Assigned, StringComparison.OrdinalIgnoreCase))
            && request.AssignedPartnerLenderId.HasValue
            && approvedOfferCount == 0)
        {
            return "COLLECT_PARTNER_OFFERS";
        }

        if (approvedOfferCount > 0 && !request.SelectedFinancingDecisionId.HasValue)
        {
            return "PUBLISH_OFFERS_TO_EXTERNAL";
        }

        if (request.SelectedFinancingDecisionId.HasValue)
        {
            return "ADVANCE_EXECUTION_WORKFLOW";
        }

        return "MONITOR_REQUEST";
    }

    private static string ResolveExecutionWorkbenchStage(
        Domain.IslamicFinance.Murabaha.IslamicFinanceMurabahaCase? murabahaCase,
        Domain.IslamicFinance.ReceivablesFinance.IslamicFinanceReceivablesCase? receivablesCase,
        Domain.IslamicFinance.Disbursements.IslamicFinanceMurabahaDisbursement? disbursement,
        Domain.IslamicFinance.Settlements.IslamicFinanceSettlementHeader? settlement,
        long? selectedFinancingDecisionId)
    {
        if (settlement is not null)
        {
            return string.Equals(settlement.StatusCode, "CLOSED", StringComparison.OrdinalIgnoreCase)
                ? "COMPLETED"
                : "SETTLEMENT";
        }

        if (disbursement is not null)
        {
            return "DISBURSEMENT";
        }

        if (receivablesCase is not null)
        {
            return "RECEIVABLES_CASE";
        }

        if (murabahaCase is not null)
        {
            return "MURABAHA_CASE";
        }

        if (selectedFinancingDecisionId.HasValue)
        {
            return "OFFER_SELECTED";
        }

        return "APPLICATION";
    }

    private static string ResolveExecutionWorkbenchAction(
        Domain.IslamicFinance.Murabaha.IslamicFinanceMurabahaCase? murabahaCase,
        Domain.IslamicFinance.ReceivablesFinance.IslamicFinanceReceivablesCase? receivablesCase,
        Domain.IslamicFinance.Disbursements.IslamicFinanceMurabahaDisbursement? disbursement,
        Domain.IslamicFinance.Settlements.IslamicFinanceSettlementHeader? settlement,
        long? selectedFinancingDecisionId)
    {
        if (settlement is not null)
        {
            return string.Equals(settlement.StatusCode, "CLOSED", StringComparison.OrdinalIgnoreCase)
                ? "COMPLETE_RELEASE_TRACK"
                : "ADVANCE_SETTLEMENT";
        }

        if (disbursement is not null)
        {
            return string.Equals(disbursement.StatusCode, "EXECUTED", StringComparison.OrdinalIgnoreCase)
                ? "PREPARE_SETTLEMENT"
                : "EXECUTE_DISBURSEMENT";
        }

        if (receivablesCase is not null)
        {
            return "ADVANCE_COLLECTION_CASE";
        }

        if (murabahaCase is not null)
        {
            return "PREPARE_DISBURSEMENT";
        }

        if (selectedFinancingDecisionId.HasValue)
        {
            return "START_EXECUTION_CASE";
        }

        return "MONITOR_EXECUTION";
    }

    private static DateTime? ResolveOfficerSlaDueAtUtc(IslamicFinanceRequest request, string pendingActionCode)
    {
        if (string.Equals(pendingActionCode, "AWAIT_EXTERNAL_OFFER_SELECTION", StringComparison.OrdinalIgnoreCase)
            || string.Equals(request.StatusCode, IslamicFinanceWorkflowCatalog.Rejected, StringComparison.OrdinalIgnoreCase))
        {
            return null;
        }

        var anchor = request.SubmittedAtUtc ?? request.CreatedAtUtc;
        var slaDays =
            string.Equals(request.StatusCode, IslamicFinanceWorkflowCatalog.Submitted, StringComparison.OrdinalIgnoreCase)
            || string.Equals(request.StatusCode, IslamicFinanceWorkflowCatalog.Returned, StringComparison.OrdinalIgnoreCase)
                ? 2
                : 3;

        return anchor.AddDays(slaDays);
    }

    private static string ResolveOfficerSlaStatusCode(DateTime nowUtc, DateTime? slaDueAtUtc, string pendingActionCode)
    {
        if (!slaDueAtUtc.HasValue)
        {
            return string.Equals(pendingActionCode, "AWAIT_EXTERNAL_OFFER_SELECTION", StringComparison.OrdinalIgnoreCase)
                ? "WAITING_EXTERNAL_RESPONSE"
                : "NOT_APPLICABLE";
        }

        if (nowUtc > slaDueAtUtc.Value)
        {
            return "BREACHED";
        }

        if (nowUtc >= slaDueAtUtc.Value.AddHours(-12))
        {
            return "AT_RISK";
        }

        return "ON_TRACK";
    }

    private static string ResolveOfficerAttentionStatusCode(string statusCode, string pendingActionCode, string slaStatusCode)
    {
        if (string.Equals(statusCode, IslamicFinanceWorkflowCatalog.Rejected, StringComparison.OrdinalIgnoreCase))
        {
            return "CLOSED";
        }

        if (string.Equals(slaStatusCode, "BREACHED", StringComparison.OrdinalIgnoreCase))
        {
            return "IMMEDIATE_ATTENTION";
        }

        if (string.Equals(slaStatusCode, "AT_RISK", StringComparison.OrdinalIgnoreCase))
        {
            return "PRIORITIZE_TODAY";
        }

        if (string.Equals(pendingActionCode, "AWAIT_EXTERNAL_OFFER_SELECTION", StringComparison.OrdinalIgnoreCase))
        {
            return "EXTERNAL_RESPONSE_PENDING";
        }

        return "BACKOFFICE_ACTION_REQUIRED";
    }

    private static void AddIfPresent(
        ICollection<IslamicFinanceRequestHistoryEntryView> events,
        long financingRequestId,
        string eventCode,
        string sourceType,
        long? sourceId,
        long? actorUserId,
        string? notes,
        DateTime? occurredAtUtc)
    {
        if (!occurredAtUtc.HasValue)
        {
            return;
        }

        events.Add(new IslamicFinanceRequestHistoryEntryView(
            financingRequestId,
            eventCode,
            sourceType,
            sourceId,
            actorUserId,
            notes,
            occurredAtUtc.Value));
    }
}
