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

namespace CashLineERP.Infrastructure.Persistence.Repositories.IslamicFinance;

public sealed class IslamicFinancePartnerRepository : IIslamicFinancePartnerRepository
{
    private readonly CashLineErpDbContext _dbContext;

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

    public IReadOnlyCollection<IslamicFinancePartnerLenderDetailView> GetPartnerLenders(bool activeOnly = false) =>
        _dbContext.IslamicFinancePartnerLenders
            .AsNoTracking()
            .Where(entry => !activeOnly || entry.IsActive)
            .OrderBy(entry => entry.Id)
            .Select(Map)
            .ToArray();

    public IslamicFinancePartnerLenderDetailView? GetPartnerLender(long partnerLenderId)
    {
        var entry = _dbContext.IslamicFinancePartnerLenders
            .AsNoTracking()
            .SingleOrDefault(partner => partner.Id == partnerLenderId);

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

    public IslamicFinancePartnerLenderDetailView Create(CreateIslamicFinancePartnerLenderCommand command)
    {
        if (!_dbContext.BusinessParties.Any(entry => entry.Id == command.PartnerLenderPartyId))
        {
            throw new InvalidOperationException("Islamic Finance partner lender requires a valid business party.");
        }

        var duplicateExists = _dbContext.IslamicFinancePartnerLenders
            .AsNoTracking()
            .Any(entry => entry.PartnerLenderPartyId == command.PartnerLenderPartyId);
        if (duplicateExists)
        {
            throw new InvalidOperationException("Islamic Finance partner lender already exists for the selected business party.");
        }

        var entry = new IslamicFinancePartnerLender(
            command.PartnerLenderPartyId,
            command.DefaultCurrencyCode,
            command.SupportsMurabaha,
            command.SupportsReceivablesFinance,
            command.SupportsTawarruq);

        _dbContext.IslamicFinancePartnerLenders.Add(entry);
        _dbContext.SaveChanges();
        return GetPartnerLender(entry.Id)!;
    }

    public IslamicFinancePartnerLenderDetailView Submit(long partnerLenderId, long actorUserId)
    {
        var entry = _dbContext.IslamicFinancePartnerLenders.Single(partner => partner.Id == partnerLenderId);
        entry.Submit(actorUserId);
        _dbContext.SaveChanges();
        return GetPartnerLender(entry.Id)!;
    }

    public IslamicFinancePartnerLenderDetailView Approve(long partnerLenderId, long actorUserId)
    {
        var entry = _dbContext.IslamicFinancePartnerLenders.Single(partner => partner.Id == partnerLenderId);
        entry.Approve(actorUserId);
        _dbContext.SaveChanges();
        return GetPartnerLender(entry.Id)!;
    }

    public IslamicFinancePartnerLenderDetailView Activate(long partnerLenderId, long actorUserId)
    {
        var entry = _dbContext.IslamicFinancePartnerLenders.Single(partner => partner.Id == partnerLenderId);
        entry.Activate(actorUserId);
        _dbContext.SaveChanges();
        return GetPartnerLender(entry.Id)!;
    }

    private static IslamicFinancePartnerLenderDetailView Map(IslamicFinancePartnerLender entry) =>
        new(
            entry.Id,
            entry.PartnerLenderPartyId,
            IslamicFinanceWorkflowCatalog.NormalizeStatusCode(entry.StatusCode),
            entry.DefaultCurrencyCode,
            entry.SupportsMurabaha,
            entry.SupportsReceivablesFinance,
            entry.SupportsTawarruq,
            entry.IsActive,
            entry.SubmittedAtUtc,
            entry.SubmittedByUserId,
            entry.ApprovedAtUtc,
            entry.ApprovedByUserId,
            entry.ActivatedAtUtc,
            entry.ActivatedByUserId);
}
