using CashLineERP.Contracts.Finance;

namespace CashLineERP.Application.Finance;

public sealed class PaymentSettlementBatchService : IPaymentSettlementBatchService
{
    private readonly IPaymentSettlementBatchRepository _repository;

    public PaymentSettlementBatchService(IPaymentSettlementBatchRepository repository)
    {
        _repository = repository;
    }

    public IReadOnlyCollection<PaymentSettlementBatchResponse> GetBatches(long? paymentChannelConfigurationId = null) =>
        _repository.GetBatches(paymentChannelConfigurationId).Select(Map).ToArray();

    public PaymentSettlementBatchResponse? GetBatch(long paymentSettlementBatchId)
    {
        var batch = _repository.GetBatch(paymentSettlementBatchId);
        return batch is null ? null : Map(batch);
    }

    public PaymentSettlementBatchResponse CreateBatch(CreatePaymentSettlementBatchRequest request) =>
        Map(_repository.Create(new CreatePaymentSettlementBatchCommand(
            request.PaymentChannelConfigurationId,
            request.SettlementDate,
            request.ExternalBatchReference,
            request.Lines.Select(line => new CreatePaymentSettlementBatchLineCommand(
                line.CustomerRefundId,
                line.SettledAmount,
                line.FeeAmount,
                line.NetAmount,
                line.ProviderReference,
                line.Notes)).ToArray())));

    public PaymentSettlementBatchResponse SubmitBatch(long paymentSettlementBatchId, long actorUserId) =>
        Map(_repository.Submit(paymentSettlementBatchId, actorUserId));

    public PaymentSettlementBatchResponse ApproveBatch(long paymentSettlementBatchId, long actorUserId) =>
        Map(_repository.Approve(paymentSettlementBatchId, actorUserId));

    public PaymentSettlementBatchResponse PostBatch(long paymentSettlementBatchId, long actorUserId) =>
        Map(_repository.Post(paymentSettlementBatchId, actorUserId));

    public IReadOnlyCollection<PaymentSettlementExceptionResponse> GetExceptions(long? paymentSettlementBatchId = null, string? statusCode = null) =>
        _repository.GetExceptions(paymentSettlementBatchId, statusCode).Select(Map).ToArray();

    public PaymentSettlementExceptionResponse? GetException(long paymentSettlementExceptionId)
    {
        var exception = _repository.GetException(paymentSettlementExceptionId);
        return exception is null ? null : Map(exception);
    }

    public PaymentSettlementExceptionResponse ConfirmException(long paymentSettlementExceptionId, long actorUserId, ResolvePaymentSettlementExceptionRequest request) =>
        Map(_repository.ConfirmException(paymentSettlementExceptionId, new ResolvePaymentSettlementExceptionCommand(actorUserId, request.Notes)));

    public PaymentSettlementExceptionResponse OverrideException(long paymentSettlementExceptionId, long actorUserId, ResolvePaymentSettlementExceptionRequest request) =>
        Map(_repository.OverrideException(paymentSettlementExceptionId, new ResolvePaymentSettlementExceptionCommand(actorUserId, request.Notes)));

    private static PaymentSettlementBatchResponse Map(PaymentSettlementBatchDetailView batch) =>
        new(
            batch.PaymentSettlementBatchId,
            batch.BatchNumber,
            batch.PaymentChannelConfigurationId,
            batch.ChannelCode,
            batch.ChannelName,
            batch.SettlementDate,
            batch.CurrencyCode,
            batch.BaseCurrencyCode,
            batch.FxRateToBase,
            batch.ExternalBatchReference,
            batch.StatusCode,
            batch.TotalTransactionCount,
            batch.TotalInternalAmount,
            batch.TotalSettledAmount,
            batch.TotalFeeAmount,
            batch.TotalNetAmount,
            batch.TotalInternalBaseAmount,
            batch.TotalSettledBaseAmount,
            batch.TotalFeeBaseAmount,
            batch.TotalNetBaseAmount,
            batch.OpenExceptionCount,
            batch.ResolvedExceptionCount,
            batch.SubmittedAtUtc,
            batch.SubmittedByUserId,
            batch.ApprovedAtUtc,
            batch.ApprovedByUserId,
            batch.PostedAtUtc,
            batch.PostedByUserId,
            batch.Lines.Select(line => new PaymentSettlementBatchLineResponse(
                line.PaymentSettlementBatchLineId,
                line.SourceDocumentType,
                line.SourceDocumentId,
                line.SourceDocumentNumber,
                line.PartyId,
                line.InternalAmount,
                line.SettledAmount,
                line.FeeAmount,
                line.NetAmount,
                line.InternalBaseAmount,
                line.SettledBaseAmount,
                line.FeeBaseAmount,
                line.NetBaseAmount,
                line.ProviderReference,
                line.SettlementStatusCode,
                line.Notes)).ToArray());

    private static PaymentSettlementExceptionResponse Map(PaymentSettlementExceptionView exception) =>
        new(
            exception.PaymentSettlementExceptionId,
            exception.PaymentSettlementBatchId,
            exception.BatchNumber,
            exception.PaymentSettlementBatchLineId,
            exception.PaymentChannelConfigurationId,
            exception.ChannelCode,
            exception.ChannelName,
            exception.SettlementDate,
            exception.CurrencyCode,
            exception.SourceDocumentType,
            exception.SourceDocumentId,
            exception.SourceDocumentNumber,
            exception.PartyId,
            exception.ExceptionCode,
            exception.StatusCode,
            exception.DifferenceAmount,
            exception.Notes,
            exception.ResolutionCode,
            exception.ResolvedAtUtc,
            exception.ResolvedByUserId);
}
