using CashLineERP.Contracts.Finance;

namespace CashLineERP.Application.Finance;

public sealed class AccountsReceivableReceiptService : IAccountsReceivableReceiptService
{
    private readonly IAccountsReceivableReceiptRepository _repository;
    private readonly ICustomerRefundService _customerRefundService;

    public AccountsReceivableReceiptService(
        IAccountsReceivableReceiptRepository repository,
        ICustomerRefundService customerRefundService)
    {
        _repository = repository;
        _customerRefundService = customerRefundService;
    }

    public AccountsReceivableReceiptService(IAccountsReceivableReceiptRepository repository)
        : this(repository, new UnsupportedCustomerRefundService())
    {
    }

    public IReadOnlyCollection<AccountsReceivableReceiptResponse> GetReceipts() =>
        _repository.GetReceipts().Select(Map).ToArray();

    public AccountsReceivableReceiptResponse? GetReceipt(long accountsReceivableReceiptId)
    {
        var receipt = _repository.GetReceipt(accountsReceivableReceiptId);
        return receipt is null ? null : Map(receipt);
    }

    public AccountsReceivableReceiptResponse CreateReceipt(CreateAccountsReceivableReceiptRequest request)
    {
        var receipt = _repository.Create(new CreateAccountsReceivableReceiptCommand(
            request.CustomerId,
            request.ReceiptDate,
            request.ReceiptMethod,
            request.CurrencyCode,
            request.AmountAmount,
            request.ReferenceNumber,
            request.Notes,
            request.Allocations.Select(allocation => new CreateAccountsReceivableReceiptAllocationCommand(
                allocation.AccountsReceivableInvoiceId,
                allocation.AllocationAmount,
                allocation.AllocationDate)).ToArray()));

        return Map(receipt);
    }

    public AccountsReceivableReceiptResponse SubmitReceipt(long accountsReceivableReceiptId, long actorUserId) =>
        Map(_repository.Submit(accountsReceivableReceiptId, actorUserId));

    public AccountsReceivableReceiptResponse ApproveReceipt(long accountsReceivableReceiptId, long actorUserId) =>
        Map(_repository.Approve(accountsReceivableReceiptId, actorUserId));

    public AccountsReceivableReceiptResponse PostReceipt(long accountsReceivableReceiptId, long actorUserId) =>
        Map(_repository.Post(accountsReceivableReceiptId, actorUserId));

    public AccountsReceivableReceiptResponse ReverseReceipt(long accountsReceivableReceiptId, long actorUserId, ReverseAccountsReceivableReceiptRequest request) =>
        Map(_repository.Reverse(
            accountsReceivableReceiptId,
            actorUserId,
            new ReverseAccountsReceivableReceiptCommand(
                request.ReversalDate,
                request.ReasonCode,
                request.Notes)));

    public AccountsReceivableReceiptResponse ReallocateReceipt(long accountsReceivableReceiptId, long actorUserId, ReallocateAccountsReceivableReceiptRequest request) =>
        Map(_repository.Reallocate(
            accountsReceivableReceiptId,
            actorUserId,
            new ReallocateAccountsReceivableReceiptCommand(
                request.ReceiptDate,
                request.ReceiptMethod,
                request.ReferenceNumber,
                request.Notes,
                request.Allocations.Select(allocation => new CreateAccountsReceivableReceiptAllocationCommand(
                    allocation.AccountsReceivableInvoiceId,
                    allocation.AllocationAmount,
                    allocation.AllocationDate)).ToArray())));

    public IReadOnlyCollection<CustomerRefundResponse> GetCustomerRefunds(long? customerId = null) =>
        _customerRefundService.GetRefunds(customerId);

    public CustomerRefundResponse? GetCustomerRefund(long customerRefundId) =>
        _customerRefundService.GetRefund(customerRefundId);

    public CustomerRefundResponse CreateCustomerRefund(CreateCustomerRefundRequest request) =>
        _customerRefundService.CreateRefund(request);

    public CustomerRefundResponse SubmitCustomerRefund(long customerRefundId, long actorUserId) =>
        _customerRefundService.SubmitRefund(customerRefundId, actorUserId);

    public CustomerRefundResponse ApproveCustomerRefund(long customerRefundId, long actorUserId) =>
        _customerRefundService.ApproveRefund(customerRefundId, actorUserId);

    public CustomerRefundResponse ReturnCustomerRefund(long customerRefundId, long actorUserId, ReviewCustomerRefundRequest request) =>
        _customerRefundService.ReturnRefund(customerRefundId, actorUserId, request);

    public CustomerRefundResponse RejectCustomerRefund(long customerRefundId, long actorUserId, ReviewCustomerRefundRequest request) =>
        _customerRefundService.RejectRefund(customerRefundId, actorUserId, request);

    public CustomerRefundResponse PostCustomerRefund(long customerRefundId, long actorUserId) =>
        _customerRefundService.PostRefund(customerRefundId, actorUserId);

    public CustomerRefundOrchestrationResponse GetCustomerRefundOrchestration(long customerRefundId) =>
        _customerRefundService.GetRefundOrchestration(customerRefundId);

    private static AccountsReceivableReceiptResponse Map(AccountsReceivableReceiptDetailView receipt) =>
        new(
            receipt.AccountsReceivableReceiptId,
            receipt.ReceiptNumber,
            receipt.CustomerId,
            receipt.ReceiptDate,
            receipt.ReceiptMethod,
            receipt.CurrencyCode,
            receipt.StatusCode,
            receipt.AmountAmount,
            receipt.UnappliedAmount,
            receipt.RefundedAmount,
            receipt.AvailableRefundAmount,
            receipt.ReferenceNumber,
            receipt.Notes,
            receipt.ReversalDate,
            receipt.ReversalReasonCode,
            receipt.ReversalNotes,
            receipt.ReversalGeneralLedgerBatchId,
            receipt.ReallocatedFromReceiptId,
            receipt.ReallocationReceiptId,
            receipt.GeneralLedgerBatchId,
            receipt.Allocations.Select(allocation => new AccountsReceivableReceiptAllocationResponse(
                allocation.AccountsReceivableReceiptAllocationId,
                allocation.AccountsReceivableInvoiceId,
                allocation.AllocationAmount,
                allocation.AllocationDate)).ToArray());

    private sealed class UnsupportedCustomerRefundService : ICustomerRefundService
    {
        public IReadOnlyCollection<CustomerRefundResponse> GetRefunds(long? customerId = null) => throw new NotSupportedException();
        public CustomerRefundResponse? GetRefund(long customerRefundId) => throw new NotSupportedException();
        public CustomerRefundResponse CreateRefund(CreateCustomerRefundRequest request) => throw new NotSupportedException();
        public CustomerRefundResponse SubmitRefund(long customerRefundId, long actorUserId) => throw new NotSupportedException();
        public CustomerRefundResponse ApproveRefund(long customerRefundId, long actorUserId) => throw new NotSupportedException();
        public CustomerRefundResponse ReturnRefund(long customerRefundId, long actorUserId, ReviewCustomerRefundRequest request) => throw new NotSupportedException();
        public CustomerRefundResponse RejectRefund(long customerRefundId, long actorUserId, ReviewCustomerRefundRequest request) => throw new NotSupportedException();
        public CustomerRefundResponse PostRefund(long customerRefundId, long actorUserId) => throw new NotSupportedException();
        public CustomerRefundOrchestrationResponse GetRefundOrchestration(long customerRefundId) => throw new NotSupportedException();
    }
}
