using CashLineERP.Contracts.Finance;

namespace CashLineERP.Application.Finance;

public sealed class AccountsPayablePaymentService : IAccountsPayablePaymentService
{
    private readonly IAccountsPayablePaymentRepository _repository;

    public AccountsPayablePaymentService(IAccountsPayablePaymentRepository repository)
    {
        _repository = repository;
    }

    public IReadOnlyCollection<AccountsPayablePaymentResponse> GetPayments() =>
        _repository.GetPayments().Select(Map).ToArray();

    public AccountsPayablePaymentResponse? GetPayment(long accountsPayablePaymentId)
    {
        var payment = _repository.GetPayment(accountsPayablePaymentId);
        return payment is null ? null : Map(payment);
    }

    public AccountsPayablePaymentResponse CreatePayment(CreateAccountsPayablePaymentRequest request)
    {
        var payment = _repository.Create(new CreateAccountsPayablePaymentCommand(
            request.SupplierId,
            request.PaymentDate,
            request.PaymentMethod,
            request.CurrencyCode,
            request.AmountAmount,
            request.ReferenceNumber,
            request.Notes,
            request.Allocations.Select(allocation => new CreateAccountsPayablePaymentAllocationCommand(
                allocation.AccountsPayableBillId,
                allocation.AllocationAmount,
                allocation.AllocationDate)).ToArray()));

        return Map(payment);
    }

    public AccountsPayablePaymentResponse SubmitPayment(long accountsPayablePaymentId, long actorUserId) =>
        Map(_repository.Submit(accountsPayablePaymentId, actorUserId));

    public AccountsPayablePaymentResponse ApprovePayment(long accountsPayablePaymentId, long actorUserId) =>
        Map(_repository.Approve(accountsPayablePaymentId, actorUserId));

    public AccountsPayablePaymentResponse PostPayment(long accountsPayablePaymentId, long actorUserId) =>
        Map(_repository.Post(accountsPayablePaymentId, actorUserId));

    public AccountsPayablePaymentResponse ReversePayment(long accountsPayablePaymentId, long actorUserId, ReverseAccountsPayablePaymentRequest request) =>
        Map(_repository.Reverse(
            accountsPayablePaymentId,
            actorUserId,
            new ReverseAccountsPayablePaymentCommand(
                request.ReversalDate,
                request.ReasonCode,
                request.Notes)));

    public AccountsPayablePaymentResponse RepostPayment(long accountsPayablePaymentId, long actorUserId, RepostAccountsPayablePaymentRequest request) =>
        Map(_repository.Repost(
            accountsPayablePaymentId,
            actorUserId,
            new RepostAccountsPayablePaymentCommand(
                request.PaymentDate,
                request.PaymentMethod,
                request.ReferenceNumber,
                request.Notes,
                request.Allocations.Select(allocation => new CreateAccountsPayablePaymentAllocationCommand(
                    allocation.AccountsPayableBillId,
                    allocation.AllocationAmount,
                    allocation.AllocationDate)).ToArray())));

    private static AccountsPayablePaymentResponse Map(AccountsPayablePaymentDetailView payment) =>
        new(
            payment.AccountsPayablePaymentId,
            payment.PaymentNumber,
            payment.SupplierId,
            payment.PaymentDate,
            payment.PaymentMethod,
            payment.CurrencyCode,
            payment.StatusCode,
            payment.AmountAmount,
            payment.UnappliedAmount,
            payment.ReferenceNumber,
            payment.Notes,
            payment.SubmittedAtUtc,
            payment.SubmittedByUserId,
            payment.ApprovedAtUtc,
            payment.ApprovedByUserId,
            payment.PostedAtUtc,
            payment.PostedByUserId,
            payment.ReversalDate,
            payment.ReversalReasonCode,
            payment.ReversalNotes,
            payment.ReversalGeneralLedgerBatchId,
            payment.RepostedFromPaymentId,
            payment.RepostPaymentId,
            payment.GeneralLedgerBatchId,
            payment.Allocations.Select(allocation => new AccountsPayablePaymentAllocationResponse(
                allocation.AccountsPayablePaymentAllocationId,
                allocation.AccountsPayableBillId,
                allocation.AccountsPayableBillNumber,
                allocation.AllocationAmount,
                allocation.AllocationDate,
                allocation.AccountsPayableBillTotalAmount,
                allocation.PreviouslyAllocatedAmount,
                allocation.OutstandingAmountAfterAllocation)).ToArray());
}
