using Books.Api.Domain.Invoices;
using Books.Api.Invoicing.Services;
using EventFlow.Commands;

namespace Books.Api.Commands.Invoices;

/// <summary>
/// Command handler for creating invoices.
/// Auto-assigns a sequential invoice number if one is not provided.
/// </summary>
public class CreateInvoiceCommandHandler(
    IInvoiceNumberService invoiceNumberService)
    : CommandHandler<InvoiceAggregate, InvoiceId, CreateInvoiceCommand>
{
    public override async Task ExecuteAsync(
        InvoiceAggregate aggregate,
        CreateInvoiceCommand command,
        CancellationToken cancellationToken)
    {
        // Auto-assign invoice number if not provided
        var invoiceNumber = command.InvoiceNumber;
        if (string.IsNullOrWhiteSpace(invoiceNumber))
        {
            invoiceNumber = await invoiceNumberService.GetNextInvoiceNumberAsync(
                command.CompanyId,
                command.InvoiceDate.Year,
                cancellationToken);
        }

        aggregate.Create(
            command.CompanyId,
            command.FiscalYearId,
            command.CustomerId,
            command.CustomerName,
            command.CustomerNumber,
            invoiceNumber,
            command.InvoiceDate,
            command.DueDate,
            command.PaymentTermsDays,
            command.Currency,
            command.VatCode,
            command.Notes,
            command.Reference,
            command.CreatedBy);
    }
}

public class AddInvoiceLineCommandHandler
    : CommandHandler<InvoiceAggregate, InvoiceId, AddInvoiceLineCommand>
{
    public override Task ExecuteAsync(
        InvoiceAggregate aggregate,
        AddInvoiceLineCommand command,
        CancellationToken cancellationToken)
    {
        aggregate.AddLine(
            command.Description,
            command.Quantity,
            command.UnitPrice,
            command.VatCode,
            command.AccountId,
            command.Unit,
            command.DiscountPercent);

        return Task.CompletedTask;
    }
}

public class UpdateInvoiceLineCommandHandler
    : CommandHandler<InvoiceAggregate, InvoiceId, UpdateInvoiceLineCommand>
{
    public override Task ExecuteAsync(
        InvoiceAggregate aggregate,
        UpdateInvoiceLineCommand command,
        CancellationToken cancellationToken)
    {
        aggregate.UpdateLine(
            command.LineNumber,
            command.Description,
            command.Quantity,
            command.UnitPrice,
            command.VatCode,
            command.AccountId,
            command.Unit,
            command.DiscountPercent);

        return Task.CompletedTask;
    }
}

public class RemoveInvoiceLineCommandHandler
    : CommandHandler<InvoiceAggregate, InvoiceId, RemoveInvoiceLineCommand>
{
    public override Task ExecuteAsync(
        InvoiceAggregate aggregate,
        RemoveInvoiceLineCommand command,
        CancellationToken cancellationToken)
    {
        aggregate.RemoveLine(command.LineNumber);

        return Task.CompletedTask;
    }
}

public class MarkInvoiceSentCommandHandler
    : CommandHandler<InvoiceAggregate, InvoiceId, MarkInvoiceSentCommand>
{
    public override Task ExecuteAsync(
        InvoiceAggregate aggregate,
        MarkInvoiceSentCommand command,
        CancellationToken cancellationToken)
    {
        aggregate.Send(
            command.LedgerTransactionId,
            command.SentBy);

        return Task.CompletedTask;
    }
}

public class ReceiveInvoicePaymentCommandHandler
    : CommandHandler<InvoiceAggregate, InvoiceId, ReceiveInvoicePaymentCommand>
{
    public override Task ExecuteAsync(
        InvoiceAggregate aggregate,
        ReceiveInvoicePaymentCommand command,
        CancellationToken cancellationToken)
    {
        aggregate.ReceivePayment(
            command.Amount,
            command.BankTransactionId,
            command.LedgerTransactionId,
            command.PaymentReference,
            command.PaymentDate,
            command.RecordedBy);

        return Task.CompletedTask;
    }
}

public class VoidInvoiceCommandHandler
    : CommandHandler<InvoiceAggregate, InvoiceId, VoidInvoiceCommand>
{
    public override Task ExecuteAsync(
        InvoiceAggregate aggregate,
        VoidInvoiceCommand command,
        CancellationToken cancellationToken)
    {
        aggregate.Void(
            command.Reason,
            command.ReversalLedgerTransactionId,
            command.VoidedBy);

        return Task.CompletedTask;
    }
}

// =====================================================
// CREDIT NOTE COMMAND HANDLERS
// =====================================================

public class CreateCreditNoteCommandHandler
    : CommandHandler<InvoiceAggregate, InvoiceId, CreateCreditNoteCommand>
{
    public override Task ExecuteAsync(
        InvoiceAggregate aggregate,
        CreateCreditNoteCommand command,
        CancellationToken cancellationToken)
    {
        aggregate.CreateCreditNote(
            command.CompanyId,
            command.FiscalYearId,
            command.CustomerId,
            command.CustomerName,
            command.CustomerNumber,
            command.CreditNoteNumber,
            command.CreditNoteDate,
            command.Currency,
            command.VatCode,
            command.Notes,
            command.Reference,
            command.CreatedBy,
            command.OriginalInvoiceId,
            command.OriginalInvoiceNumber,
            command.CreditReason);

        return Task.CompletedTask;
    }
}

public class IssueCreditNoteCommandHandler
    : CommandHandler<InvoiceAggregate, InvoiceId, IssueCreditNoteCommand>
{
    public override Task ExecuteAsync(
        InvoiceAggregate aggregate,
        IssueCreditNoteCommand command,
        CancellationToken cancellationToken)
    {
        aggregate.Issue(
            command.LedgerTransactionId,
            command.IssuedBy);

        return Task.CompletedTask;
    }
}

public class ApplyCreditNoteCommandHandler
    : CommandHandler<InvoiceAggregate, InvoiceId, ApplyCreditNoteCommand>
{
    public override Task ExecuteAsync(
        InvoiceAggregate aggregate,
        ApplyCreditNoteCommand command,
        CancellationToken cancellationToken)
    {
        aggregate.ApplyCredit(
            command.TargetInvoiceId,
            command.TargetInvoiceNumber,
            command.Amount,
            command.AppliedDate,
            command.AppliedBy,
            command.LedgerTransactionId);

        return Task.CompletedTask;
    }
}
