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

namespace CashLineERP.Infrastructure.Persistence.Repositories.IslamicFinance;

public sealed class IslamicFinanceMurabahaAssetRepository : IIslamicFinanceMurabahaAssetRepository
{
    private readonly CashLineErpDbContext _dbContext;

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

    public IReadOnlyCollection<IslamicFinanceMurabahaAssetDetailView> GetAssets(long murabahaCaseId) =>
        _dbContext.IslamicFinanceMurabahaAssets
            .AsNoTracking()
            .Where(entry => entry.MurabahaCaseId == murabahaCaseId)
            .OrderBy(entry => entry.Id)
            .Select(Map)
            .ToArray();

    public IslamicFinanceMurabahaAssetDetailView? GetAsset(long murabahaAssetId)
    {
        var entry = _dbContext.IslamicFinanceMurabahaAssets
            .AsNoTracking()
            .SingleOrDefault(asset => asset.Id == murabahaAssetId);

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

    public IslamicFinanceMurabahaAssetDetailView Create(CreateIslamicFinanceMurabahaAssetCommand command)
    {
        var murabahaCase = _dbContext.IslamicFinanceMurabahaCases
            .AsNoTracking()
            .SingleOrDefault(entry => entry.Id == command.MurabahaCaseId);
        if (murabahaCase is null)
        {
            throw new InvalidOperationException("Islamic Finance Murabaha asset requires a valid Murabaha case.");
        }

        if (!string.Equals(murabahaCase.StatusCode, IslamicFinanceWorkflowCatalog.Draft, StringComparison.OrdinalIgnoreCase)
            && !string.Equals(murabahaCase.StatusCode, IslamicFinanceWorkflowCatalog.Submitted, StringComparison.OrdinalIgnoreCase))
        {
            throw new InvalidOperationException("Islamic Finance Murabaha assets can only be captured while the Murabaha case is draft or submitted.");
        }

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

        if (murabahaCase.SupplierPartyId.HasValue
            && command.SupplierPartyId.HasValue
            && murabahaCase.SupplierPartyId.Value != command.SupplierPartyId.Value)
        {
            throw new InvalidOperationException("Islamic Finance Murabaha asset supplier must align with the Murabaha case supplier when one is already recorded.");
        }

        var normalizedPurchaseReference = string.IsNullOrWhiteSpace(command.PurchaseReferenceNumber)
            ? null
            : command.PurchaseReferenceNumber.Trim().ToUpperInvariant();
        if (!string.IsNullOrWhiteSpace(normalizedPurchaseReference)
            && _dbContext.IslamicFinanceMurabahaAssets.AsNoTracking().Any(entry =>
                entry.MurabahaCaseId == command.MurabahaCaseId
                && entry.PurchaseReferenceNumber == normalizedPurchaseReference))
        {
            throw new InvalidOperationException("Islamic Finance Murabaha asset purchase reference already exists for this Murabaha case.");
        }

        var existingCost = _dbContext.IslamicFinanceMurabahaAssets
            .AsNoTracking()
            .Where(entry => entry.MurabahaCaseId == command.MurabahaCaseId)
            .Select(entry => (decimal?)entry.PurchaseCostAmount)
            .Sum() ?? 0m;
        if (existingCost + command.PurchaseCostAmount > murabahaCase.PurchaseCostAmount)
        {
            throw new InvalidOperationException("Islamic Finance Murabaha asset capture cannot exceed the Murabaha case purchase cost.");
        }

        var entry = new IslamicFinanceMurabahaAsset(
            command.MurabahaCaseId,
            command.AssetDescription,
            command.SupplierPartyId ?? murabahaCase.SupplierPartyId,
            normalizedPurchaseReference,
            command.PurchaseCostAmount);

        _dbContext.IslamicFinanceMurabahaAssets.Add(entry);
        _dbContext.SaveChanges();
        return GetAsset(entry.Id)!;
    }

    private static IslamicFinanceMurabahaAssetDetailView Map(IslamicFinanceMurabahaAsset entry) =>
        new(
            entry.Id,
            entry.MurabahaCaseId,
            entry.AssetDescription,
            entry.SupplierPartyId,
            entry.PurchaseReferenceNumber,
            entry.PurchaseCostAmount,
            entry.CreatedAtUtc,
            entry.CreatedByUserId,
            entry.UpdatedAtUtc,
            entry.UpdatedByUserId);
}
