using CashLineERP.Application.IslamicFinance;
using CashLineERP.Application.IslamicFinance.Murabaha;
using CashLineERP.Domain.IslamicFinance.Murabaha;
using CashLineERP.SharedKernel.Documents;
using CashLineERP.SharedKernel.IslamicFinance;
using Microsoft.EntityFrameworkCore;

namespace CashLineERP.Infrastructure.Persistence.Repositories.IslamicFinance;

public sealed class MurabahaCaseRepository : IMurabahaCaseRepository
{
    private const string MurabahaCaseSourceEntityType = "MURABAHA_CASE";
    private readonly CashLineErpDbContext _dbContext;

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

    public IReadOnlyCollection<IslamicFinanceMurabahaCaseDetailView> GetCases(
        string? statusCode = null,
        long? financingRequestId = null,
        long? partnerLenderId = null)
    {
        var normalizedStatusCode = string.IsNullOrWhiteSpace(statusCode)
            ? null
            : IslamicFinanceWorkflowCatalog.NormalizeStatusCode(statusCode);

        return _dbContext.IslamicFinanceMurabahaCases
            .AsNoTracking()
            .Where(entry => normalizedStatusCode == null || entry.StatusCode == normalizedStatusCode)
            .Where(entry => !financingRequestId.HasValue || entry.FinancingRequestId == financingRequestId.Value)
            .Where(entry => !partnerLenderId.HasValue || entry.PartnerLenderId == partnerLenderId.Value)
            .OrderBy(entry => entry.CaseNumber)
            .Select(MapCase)
            .ToArray();
    }

    public IslamicFinanceMurabahaCaseDetailView? GetCase(long murabahaCaseId)
    {
        var entry = _dbContext.IslamicFinanceMurabahaCases
            .AsNoTracking()
            .SingleOrDefault(caseEntry => caseEntry.Id == murabahaCaseId);

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

    public IslamicFinanceMurabahaCaseDetailView Create(CreateIslamicFinanceMurabahaCaseCommand command)
    {
        var request = _dbContext.IslamicFinanceRequests
            .AsNoTracking()
            .SingleOrDefault(entry => entry.Id == command.FinancingRequestId);

        if (request is null)
        {
            throw new InvalidOperationException("Islamic Finance Murabaha case requires a valid financing request.");
        }

        if (!_dbContext.IslamicFinancePartnerLenders.Any(entry => entry.Id == command.PartnerLenderId))
        {
            throw new InvalidOperationException("Islamic Finance Murabaha case requires a valid partner lender.");
        }

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

        if (command.ContractTemplateId.HasValue)
        {
            var contractTemplate = _dbContext.IslamicFinanceContractTemplates
                .AsNoTracking()
                .SingleOrDefault(entry => entry.Id == command.ContractTemplateId.Value);

            if (contractTemplate is null)
            {
                throw new InvalidOperationException("Islamic Finance Murabaha case contract template reference is not valid.");
            }

            if (!contractTemplate.IsActive
                || !string.Equals(contractTemplate.StatusCode, IslamicFinanceWorkflowCatalog.Active, StringComparison.OrdinalIgnoreCase))
            {
                throw new InvalidOperationException("Islamic Finance Murabaha case requires an active contract template.");
            }

            if (contractTemplate.ProductProfileId != command.ProductProfileId)
            {
                throw new InvalidOperationException("Islamic Finance Murabaha case contract template must match the Murabaha product profile.");
            }
        }

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

        if (!string.Equals(request.StatusCode, IslamicFinanceWorkflowCatalog.Assigned, StringComparison.OrdinalIgnoreCase))
        {
            throw new InvalidOperationException("Islamic Finance Murabaha case can only be created from an assigned financing request.");
        }

        if (request.AssignedPartnerLenderId != command.PartnerLenderId)
        {
            throw new InvalidOperationException("Islamic Finance Murabaha case partner lender must match the financing request assignment.");
        }

        if (request.ProductProfileId != command.ProductProfileId
            || request.CustomerPartyId != command.CustomerPartyId
            || !string.Equals(request.TransactionCurrencyCode, command.TransactionCurrencyCode, StringComparison.OrdinalIgnoreCase))
        {
            throw new InvalidOperationException("Islamic Finance Murabaha case must align with the source financing request customer, product, and currency.");
        }

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

        if (product.RequiresShariahReview)
        {
            if (!request.ShariahReviewCaseId.HasValue)
            {
                throw new InvalidOperationException("Islamic Finance Murabaha case requires an attached approved Shariah review.");
            }

            var review = _dbContext.IslamicFinanceShariahReviewCases
                .AsNoTracking()
                .SingleOrDefault(entry => entry.Id == request.ShariahReviewCaseId.Value);

            if (review is null || !string.Equals(review.StatusCode, IslamicFinanceWorkflowCatalog.Approved, StringComparison.OrdinalIgnoreCase))
            {
                throw new InvalidOperationException("Islamic Finance Murabaha case requires an approved Shariah review.");
            }
        }

        var normalizedCaseNumber = command.CaseNumber.Trim().ToUpperInvariant();
        if (_dbContext.IslamicFinanceMurabahaCases.AsNoTracking().Any(entry => entry.CaseNumber == normalizedCaseNumber))
        {
            throw new InvalidOperationException("Islamic Finance Murabaha case number already exists.");
        }

        var entry = new IslamicFinanceMurabahaCase(
            normalizedCaseNumber,
            command.FinancingRequestId,
            command.CustomerPartyId,
            command.PartnerLenderId,
            command.SupplierPartyId,
            command.ProductProfileId,
            command.ContractTemplateId,
            command.TransactionCurrencyCode,
            command.PurchaseCostAmount,
            command.ProfitAmount);

        _dbContext.IslamicFinanceMurabahaCases.Add(entry);
        _dbContext.SaveChanges();
        IslamicFinanceLifecycleOutboxWriter.EnqueueMurabahaCaseEvent(
            _dbContext,
            entry,
            IslamicFinanceIntegrationEventCatalog.MurabahaCaseCreated,
            entry.CreatedByUserId);
        _dbContext.SaveChanges();
        return GetCase(entry.Id)!;
    }

    public IslamicFinanceMurabahaCaseDetailView Submit(long murabahaCaseId, long actorUserId)
    {
        var entry = _dbContext.IslamicFinanceMurabahaCases.Single(caseEntry => caseEntry.Id == murabahaCaseId);
        entry.Submit(actorUserId);
        IslamicFinanceLifecycleOutboxWriter.EnqueueMurabahaCaseEvent(
            _dbContext,
            entry,
            IslamicFinanceIntegrationEventCatalog.MurabahaCaseSubmitted,
            actorUserId);
        _dbContext.SaveChanges();
        return GetCase(entry.Id)!;
    }

    public IslamicFinanceMurabahaCaseDetailView Approve(long murabahaCaseId, long actorUserId)
    {
        var entry = _dbContext.IslamicFinanceMurabahaCases.Single(caseEntry => caseEntry.Id == murabahaCaseId);
        entry.Approve(actorUserId);
        IslamicFinanceLifecycleOutboxWriter.EnqueueMurabahaCaseEvent(
            _dbContext,
            entry,
            IslamicFinanceIntegrationEventCatalog.MurabahaCaseApproved,
            actorUserId);
        _dbContext.SaveChanges();
        return GetCase(entry.Id)!;
    }

    public IslamicFinanceMurabahaCaseDetailView Activate(long murabahaCaseId, long actorUserId)
    {
        var entry = _dbContext.IslamicFinanceMurabahaCases.Single(caseEntry => caseEntry.Id == murabahaCaseId);
        entry.Activate(actorUserId);
        IslamicFinanceLifecycleOutboxWriter.EnqueueMurabahaCaseEvent(
            _dbContext,
            entry,
            IslamicFinanceIntegrationEventCatalog.MurabahaCaseActivated,
            actorUserId);
        _dbContext.SaveChanges();
        return GetCase(entry.Id)!;
    }

    public IslamicFinanceMurabahaScheduleView GenerateSchedule(
        long murabahaCaseId,
        long actorUserId,
        GenerateIslamicFinanceMurabahaScheduleCommand command)
    {
        if (command.InstallmentCount <= 0)
        {
            throw new InvalidOperationException("Islamic Finance Murabaha schedule requires a positive installment count.");
        }

        if (command.InstallmentIntervalMonths <= 0)
        {
            throw new InvalidOperationException("Islamic Finance Murabaha schedule installment interval must be positive.");
        }

        var murabahaCase = _dbContext.IslamicFinanceMurabahaCases
            .SingleOrDefault(entry => entry.Id == murabahaCaseId);

        if (murabahaCase is null)
        {
            throw new InvalidOperationException("Islamic Finance Murabaha schedule generation requires a valid Murabaha case.");
        }

        if (!string.Equals(murabahaCase.StatusCode, IslamicFinanceWorkflowCatalog.Approved, StringComparison.OrdinalIgnoreCase)
            && !string.Equals(murabahaCase.StatusCode, IslamicFinanceWorkflowCatalog.Active, StringComparison.OrdinalIgnoreCase))
        {
            throw new InvalidOperationException("Islamic Finance Murabaha schedule can only be generated for approved or active Murabaha cases.");
        }

        var hasLockedDisbursement = _dbContext.IslamicFinanceMurabahaDisbursements
            .AsNoTracking()
            .Any(entry => entry.MurabahaCaseId == murabahaCaseId
                && entry.StatusCode != IslamicFinanceWorkflowCatalog.Draft
                && entry.StatusCode != IslamicFinanceWorkflowCatalog.Returned
                && entry.StatusCode != IslamicFinanceWorkflowCatalog.Rejected);

        if (hasLockedDisbursement)
        {
            throw new InvalidOperationException("Islamic Finance Murabaha schedule cannot be regenerated after disbursement processing has started.");
        }

        var hasPublishedAccounting = _dbContext.IslamicFinanceAccountingHooks
            .AsNoTracking()
            .Any(entry => entry.SourceEntityType == MurabahaCaseSourceEntityType
                && entry.SourceEntityId == murabahaCaseId
                && entry.GeneralLedgerBatchId.HasValue);

        if (hasPublishedAccounting)
        {
            throw new InvalidOperationException("Islamic Finance Murabaha schedule cannot be regenerated after accounting publication.");
        }

        var existingActiveLines = _dbContext.IslamicFinanceProfitSchedules
            .Where(entry => entry.MurabahaCaseId == murabahaCaseId
                && entry.StatusCode == IslamicFinanceWorkflowCatalog.Active)
            .OrderBy(entry => entry.InstallmentNumber)
            .ToArray();

        foreach (var line in existingActiveLines)
        {
            line.SuspendForRegeneration(actorUserId);
        }

        var principalBaseAmount = decimal.Round(
            murabahaCase.PurchaseCostAmount / command.InstallmentCount,
            2,
            MidpointRounding.AwayFromZero);
        var profitBaseAmount = decimal.Round(
            murabahaCase.ProfitAmount / command.InstallmentCount,
            2,
            MidpointRounding.AwayFromZero);

        decimal allocatedPrincipalAmount = 0m;
        decimal allocatedProfitAmount = 0m;

        for (var installmentNumber = 1; installmentNumber <= command.InstallmentCount; installmentNumber++)
        {
            var isFinalInstallment = installmentNumber == command.InstallmentCount;
            var principalAmount = isFinalInstallment
                ? murabahaCase.PurchaseCostAmount - allocatedPrincipalAmount
                : principalBaseAmount;
            var profitAmount = isFinalInstallment
                ? murabahaCase.ProfitAmount - allocatedProfitAmount
                : profitBaseAmount;

            allocatedPrincipalAmount += principalAmount;
            allocatedProfitAmount += profitAmount;

            var cumulativeSalePriceAmount = allocatedPrincipalAmount + allocatedProfitAmount;
            var outstandingBalanceAmount = decimal.Max(0m, murabahaCase.SalePriceAmount - cumulativeSalePriceAmount);

            _dbContext.IslamicFinanceProfitSchedules.Add(new IslamicFinanceProfitSchedule(
                murabahaCaseId,
                installmentNumber,
                command.FirstDueDate.AddMonths((installmentNumber - 1) * command.InstallmentIntervalMonths),
                principalAmount,
                profitAmount,
                outstandingBalanceAmount));
        }

        IslamicFinanceLifecycleOutboxWriter.EnqueueMurabahaScheduleGeneratedEvent(
            _dbContext,
            murabahaCase,
            command.InstallmentCount,
            command.FirstDueDate,
            command.InstallmentIntervalMonths,
            actorUserId);
        _dbContext.SaveChanges();
        return GetSchedule(murabahaCaseId)!;
    }

    public IslamicFinanceMurabahaScheduleView? GetSchedule(long murabahaCaseId)
    {
        var murabahaCase = _dbContext.IslamicFinanceMurabahaCases
            .AsNoTracking()
            .SingleOrDefault(entry => entry.Id == murabahaCaseId);

        if (murabahaCase is null)
        {
            return null;
        }

        var lines = _dbContext.IslamicFinanceProfitSchedules
            .AsNoTracking()
            .Where(entry => entry.MurabahaCaseId == murabahaCaseId)
            .Where(entry => entry.StatusCode == IslamicFinanceWorkflowCatalog.Active)
            .OrderBy(entry => entry.InstallmentNumber)
            .Select(MapLine)
            .ToArray();

        return new IslamicFinanceMurabahaScheduleView(
            murabahaCase.Id,
            murabahaCase.CaseNumber,
            murabahaCase.FinancingRequestId,
            murabahaCase.CustomerPartyId,
            murabahaCase.PartnerLenderId,
            murabahaCase.TransactionCurrencyCode,
            murabahaCase.PurchaseCostAmount,
            murabahaCase.ProfitAmount,
            murabahaCase.SalePriceAmount,
            IslamicFinanceWorkflowCatalog.NormalizeStatusCode(murabahaCase.StatusCode),
            lines);
    }

    public IslamicFinanceMurabahaScheduleLineView? GetScheduleInstallment(long murabahaCaseId, int installmentNumber)
    {
        var entry = _dbContext.IslamicFinanceProfitSchedules
            .AsNoTracking()
            .SingleOrDefault(line => line.MurabahaCaseId == murabahaCaseId
                && line.InstallmentNumber == installmentNumber
                && line.StatusCode == IslamicFinanceWorkflowCatalog.Active);

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

    public IReadOnlyCollection<IslamicFinanceLifecycleHistoryEntryView> GetCaseHistory(long murabahaCaseId)
    {
        var murabahaCase = _dbContext.IslamicFinanceMurabahaCases
            .AsNoTracking()
            .SingleOrDefault(entry => entry.Id == murabahaCaseId);

        if (murabahaCase is null)
        {
            return Array.Empty<IslamicFinanceLifecycleHistoryEntryView>();
        }

        var events = new List<IslamicFinanceLifecycleHistoryEntryView>
        {
            new(
                murabahaCase.Id,
                "MURABAHA_CASE_CREATED",
                "MURABAHA_CASE",
                murabahaCase.Id,
                murabahaCase.CreatedByUserId,
                murabahaCase.CaseNumber,
                murabahaCase.CreatedAtUtc)
        };

        AddIfPresent(events, murabahaCase.Id, "MURABAHA_CASE_SUBMITTED", "MURABAHA_CASE", murabahaCase.Id, murabahaCase.SubmittedByUserId, null, murabahaCase.SubmittedAtUtc);
        AddIfPresent(events, murabahaCase.Id, "MURABAHA_CASE_APPROVED", "MURABAHA_CASE", murabahaCase.Id, murabahaCase.ApprovedByUserId, null, murabahaCase.ApprovedAtUtc);
        AddIfPresent(events, murabahaCase.Id, "MURABAHA_CASE_ACTIVATED", "MURABAHA_CASE", murabahaCase.Id, murabahaCase.ActivatedByUserId, null, murabahaCase.ActivatedAtUtc);

        var assets = _dbContext.IslamicFinanceMurabahaAssets
            .AsNoTracking()
            .Where(entry => entry.MurabahaCaseId == murabahaCaseId)
            .OrderBy(entry => entry.CreatedAtUtc)
            .ToArray();
        foreach (var asset in assets)
        {
            events.Add(new IslamicFinanceLifecycleHistoryEntryView(
                murabahaCaseId,
                "MURABAHA_ASSET_CAPTURED",
                "MURABAHA_ASSET",
                asset.Id,
                asset.CreatedByUserId,
                asset.PurchaseReferenceNumber ?? asset.AssetDescription,
                asset.CreatedAtUtc));
        }

        var assetIds = assets.Select(entry => entry.Id).ToArray();
        if (assetIds.Length > 0)
        {
            var assetEvidence = _dbContext.DocumentLinks
                .AsNoTracking()
                .Where(entry => entry.ModuleCode == IslamicFinanceDocumentCatalog.ModuleCode)
                .Where(entry => entry.EntityName == IslamicFinanceDocumentCatalog.MurabahaAssetEntityName)
                .Where(entry => assetIds.Contains(entry.EntityId))
                .OrderBy(entry => entry.UploadedAtUtc)
                .ToArray();

            foreach (var document in assetEvidence)
            {
                events.Add(new IslamicFinanceLifecycleHistoryEntryView(
                    murabahaCaseId,
                    "MURABAHA_ASSET_EVIDENCE_ATTACHED",
                    "DOCUMENT_LINK",
                    document.Id,
                    document.UploadedByUserId ?? document.CreatedByUserId,
                    document.FileName,
                    document.UploadedAtUtc));
            }
        }

        var contracts = _dbContext.DocumentLinks
            .AsNoTracking()
            .Where(entry => entry.ModuleCode == IslamicFinanceDocumentCatalog.ModuleCode)
            .Where(entry => entry.EntityName == IslamicFinanceDocumentCatalog.MurabahaCaseEntityName)
            .Where(entry => entry.EntityId == murabahaCaseId)
            .Where(entry => entry.DocumentType == IslamicFinanceDocumentCatalog.MurabahaContractDocumentType)
            .OrderBy(entry => entry.UploadedAtUtc)
            .ToArray();
        foreach (var contract in contracts)
        {
            events.Add(new IslamicFinanceLifecycleHistoryEntryView(
                murabahaCaseId,
                "MURABAHA_CONTRACT_GENERATED",
                "DOCUMENT_LINK",
                contract.Id,
                contract.UploadedByUserId ?? contract.CreatedByUserId,
                contract.FileName,
                contract.UploadedAtUtc));
        }

        var firstScheduleLine = _dbContext.IslamicFinanceProfitSchedules
            .AsNoTracking()
            .Where(entry => entry.MurabahaCaseId == murabahaCaseId)
            .OrderBy(entry => entry.CreatedAtUtc)
            .FirstOrDefault();
        if (firstScheduleLine is not null)
        {
            var installmentCount = _dbContext.IslamicFinanceProfitSchedules
                .AsNoTracking()
                .Count(entry => entry.MurabahaCaseId == murabahaCaseId);

            events.Add(new IslamicFinanceLifecycleHistoryEntryView(
                murabahaCaseId,
                "MURABAHA_SCHEDULE_GENERATED",
                "PROFIT_SCHEDULE",
                murabahaCaseId,
                firstScheduleLine.CreatedByUserId,
                $"{installmentCount} installment lines",
                firstScheduleLine.CreatedAtUtc));
        }

        var hooks = _dbContext.IslamicFinanceAccountingHooks
            .AsNoTracking()
            .Where(entry => entry.SourceEntityType == MurabahaCaseSourceEntityType && entry.SourceEntityId == murabahaCaseId)
            .OrderBy(entry => entry.CreatedAtUtc)
            .ToArray();
        foreach (var hook in hooks)
        {
            events.Add(new IslamicFinanceLifecycleHistoryEntryView(
                murabahaCaseId,
                "MURABAHA_ACCOUNTING_HOOK_REGISTERED",
                "ACCOUNTING_HOOK",
                hook.Id,
                hook.CreatedByUserId,
                hook.PostingStatusCode,
                hook.CreatedAtUtc));

            if (hook.GeneralLedgerBatchId.HasValue && hook.UpdatedAtUtc.HasValue)
            {
                events.Add(new IslamicFinanceLifecycleHistoryEntryView(
                    murabahaCaseId,
                    "MURABAHA_ACCOUNTING_PUBLISHED",
                    "ACCOUNTING_HOOK",
                    hook.Id,
                    hook.UpdatedByUserId,
                    $"GL batch {hook.GeneralLedgerBatchId.Value}",
                    hook.UpdatedAtUtc.Value));
            }
        }

        var disbursements = _dbContext.IslamicFinanceMurabahaDisbursements
            .AsNoTracking()
            .Where(entry => entry.MurabahaCaseId == murabahaCaseId)
            .OrderBy(entry => entry.CreatedAtUtc)
            .ToArray();
        foreach (var disbursement in disbursements)
        {
            events.Add(new IslamicFinanceLifecycleHistoryEntryView(
                murabahaCaseId,
                "DISBURSEMENT_CREATED",
                "DISBURSEMENT",
                disbursement.Id,
                disbursement.CreatedByUserId,
                disbursement.DisbursementNumber,
                disbursement.CreatedAtUtc));

            AddIfPresent(events, murabahaCaseId, "DISBURSEMENT_SUBMITTED", "DISBURSEMENT", disbursement.Id, disbursement.SubmittedByUserId, null, disbursement.SubmittedAtUtc);
            AddIfPresent(events, murabahaCaseId, "DISBURSEMENT_APPROVED", "DISBURSEMENT", disbursement.Id, disbursement.ApprovedByUserId, null, disbursement.ApprovedAtUtc);
            AddIfPresent(events, murabahaCaseId, "DISBURSEMENT_EXECUTED", "DISBURSEMENT", disbursement.Id, disbursement.ExecutedByUserId, disbursement.ExecutionReferenceNumber, disbursement.ExecutedAtUtc);
            AddIfPresent(events, murabahaCaseId, "DISBURSEMENT_SUSPENDED", "DISBURSEMENT", disbursement.Id, disbursement.SuspendedByUserId, disbursement.SuspensionReasonCode, disbursement.SuspendedAtUtc);

            if (string.Equals(disbursement.StatusCode, IslamicFinanceWorkflowCatalog.Returned, StringComparison.OrdinalIgnoreCase) && disbursement.UpdatedAtUtc.HasValue)
            {
                events.Add(new IslamicFinanceLifecycleHistoryEntryView(
                    murabahaCaseId,
                    "DISBURSEMENT_RETURNED",
                    "DISBURSEMENT",
                    disbursement.Id,
                    disbursement.UpdatedByUserId,
                    null,
                    disbursement.UpdatedAtUtc.Value));
            }

            if (string.Equals(disbursement.StatusCode, IslamicFinanceWorkflowCatalog.Rejected, StringComparison.OrdinalIgnoreCase) && disbursement.UpdatedAtUtc.HasValue)
            {
                events.Add(new IslamicFinanceLifecycleHistoryEntryView(
                    murabahaCaseId,
                    "DISBURSEMENT_REJECTED",
                    "DISBURSEMENT",
                    disbursement.Id,
                    disbursement.UpdatedByUserId,
                    null,
                    disbursement.UpdatedAtUtc.Value));
            }

            if (string.Equals(disbursement.StatusCode, IslamicFinanceWorkflowCatalog.Closed, StringComparison.OrdinalIgnoreCase) && disbursement.UpdatedAtUtc.HasValue)
            {
                events.Add(new IslamicFinanceLifecycleHistoryEntryView(
                    murabahaCaseId,
                    "DISBURSEMENT_CLOSED",
                    "DISBURSEMENT",
                    disbursement.Id,
                    disbursement.UpdatedByUserId,
                    null,
                    disbursement.UpdatedAtUtc.Value));
            }
        }

        var settlements = _dbContext.IslamicFinanceSettlementHeaders
            .AsNoTracking()
            .Where(entry => entry.MurabahaCaseId == murabahaCaseId)
            .OrderBy(entry => entry.CreatedAtUtc)
            .ToArray();
        foreach (var settlement in settlements)
        {
            events.Add(new IslamicFinanceLifecycleHistoryEntryView(
                murabahaCaseId,
                "SETTLEMENT_CREATED",
                "SETTLEMENT",
                settlement.Id,
                settlement.CreatedByUserId,
                settlement.SettlementNumber,
                settlement.CreatedAtUtc));

            AddIfPresent(events, murabahaCaseId, "SETTLEMENT_SUBMITTED", "SETTLEMENT", settlement.Id, settlement.SubmittedByUserId, null, settlement.SubmittedAtUtc);
            AddIfPresent(events, murabahaCaseId, "SETTLEMENT_APPROVED", "SETTLEMENT", settlement.Id, settlement.ApprovedByUserId, null, settlement.ApprovedAtUtc);
            AddIfPresent(events, murabahaCaseId, "SETTLEMENT_CLOSED", "SETTLEMENT", settlement.Id, settlement.ClosedByUserId, null, settlement.ClosedAtUtc);
        }

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

    public IslamicFinanceMurabahaCaseAuditView? GetCaseAudit(long murabahaCaseId)
    {
        var murabahaCase = GetCase(murabahaCaseId);
        if (murabahaCase is null)
        {
            return null;
        }

        return new IslamicFinanceMurabahaCaseAuditView(
            murabahaCase,
            GetCaseHistory(murabahaCaseId));
    }

    private static IslamicFinanceMurabahaCaseDetailView MapCase(IslamicFinanceMurabahaCase entry) =>
        new(
            entry.Id,
            entry.CaseNumber,
            entry.FinancingRequestId,
            entry.CustomerPartyId,
            entry.PartnerLenderId,
            entry.SupplierPartyId,
            entry.ProductProfileId,
            entry.ContractTemplateId,
            entry.TransactionCurrencyCode,
            entry.PurchaseCostAmount,
            entry.ProfitAmount,
            entry.SalePriceAmount,
            IslamicFinanceWorkflowCatalog.NormalizeStatusCode(entry.StatusCode),
            entry.WorkflowInstanceId,
            entry.CreatedAtUtc,
            entry.CreatedByUserId,
            entry.SubmittedAtUtc,
            entry.SubmittedByUserId,
            entry.ApprovedAtUtc,
            entry.ApprovedByUserId,
            entry.ActivatedAtUtc,
            entry.ActivatedByUserId);

    private static IslamicFinanceMurabahaScheduleLineView MapLine(Domain.IslamicFinance.Murabaha.IslamicFinanceProfitSchedule entry) =>
        new(
            entry.MurabahaCaseId,
            entry.InstallmentNumber,
            entry.DueDate,
            entry.PrincipalAmount,
            entry.ProfitAmount,
            entry.InstallmentAmount,
            entry.OutstandingBalanceAmount,
            IslamicFinanceWorkflowCatalog.NormalizeStatusCode(entry.StatusCode));

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

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