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

namespace CashLineERP.Infrastructure.Persistence.Repositories.IslamicFinance;

public sealed class IslamicFinanceFacilityRepository : IIslamicFinanceFacilityRepository
{
    private readonly CashLineErpDbContext _dbContext;

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

    public IReadOnlyCollection<IslamicFinanceFacilityDetailView> GetFacilities(
        bool activeOnly = false,
        long? customerPartyId = null,
        long? partnerLenderId = null) =>
        _dbContext.IslamicFinanceFacilities
            .AsNoTracking()
            .Where(entry => !activeOnly || entry.IsActive)
            .Where(entry => !customerPartyId.HasValue || entry.CustomerPartyId == customerPartyId.Value)
            .Where(entry => !partnerLenderId.HasValue || entry.PartnerLenderId == partnerLenderId.Value)
            .OrderBy(entry => entry.FacilityNumber)
            .Select(Map)
            .ToArray();

    public IslamicFinanceFacilityDetailView? GetFacility(long facilityId)
    {
        var entry = _dbContext.IslamicFinanceFacilities
            .AsNoTracking()
            .SingleOrDefault(facility => facility.Id == facilityId);

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

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

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

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

        var normalizedFacilityNumber = command.FacilityNumber.Trim().ToUpperInvariant();
        if (_dbContext.IslamicFinanceFacilities.AsNoTracking().Any(entry => entry.FacilityNumber == normalizedFacilityNumber))
        {
            throw new InvalidOperationException("Islamic Finance facility number already exists.");
        }

        var entry = new IslamicFinanceFacility(
            command.PartnerLenderId,
            command.CustomerPartyId,
            command.ProductProfileId,
            normalizedFacilityNumber,
            command.CurrencyCode,
            command.ApprovedLimitAmount,
            command.UtilizedAmount);

        _dbContext.IslamicFinanceFacilities.Add(entry);
        _dbContext.SaveChanges();
        return GetFacility(entry.Id)!;
    }

    public IslamicFinanceFacilityDetailView Submit(long facilityId, long actorUserId)
    {
        var entry = _dbContext.IslamicFinanceFacilities.Single(facility => facility.Id == facilityId);
        entry.Submit(actorUserId);
        _dbContext.SaveChanges();
        return GetFacility(entry.Id)!;
    }

    public IslamicFinanceFacilityDetailView Approve(long facilityId, long actorUserId)
    {
        var entry = _dbContext.IslamicFinanceFacilities.Single(facility => facility.Id == facilityId);
        entry.Approve(actorUserId);
        _dbContext.SaveChanges();
        return GetFacility(entry.Id)!;
    }

    public IslamicFinanceFacilityDetailView Activate(long facilityId, long actorUserId)
    {
        var entry = _dbContext.IslamicFinanceFacilities.Single(facility => facility.Id == facilityId);
        entry.Activate(actorUserId);
        _dbContext.SaveChanges();
        return GetFacility(entry.Id)!;
    }

    private static IslamicFinanceFacilityDetailView Map(IslamicFinanceFacility entry) =>
        new(
            entry.Id,
            entry.PartnerLenderId,
            entry.CustomerPartyId,
            entry.ProductProfileId,
            entry.FacilityNumber,
            entry.CurrencyCode,
            entry.ApprovedLimitAmount,
            entry.UtilizedAmount,
            entry.AvailableAmount(),
            IslamicFinanceWorkflowCatalog.NormalizeStatusCode(entry.StatusCode),
            entry.IsActive,
            entry.SubmittedAtUtc,
            entry.SubmittedByUserId,
            entry.ApprovedAtUtc,
            entry.ApprovedByUserId,
            entry.ActivatedAtUtc,
            entry.ActivatedByUserId);
}
