using CashLineERP.Domain.Common;
using CashLineERP.SharedKernel.Workflow;

namespace CashLineERP.Domain.Finance;

public sealed class BankTransferBatch : Entity
{
    public long Id { get; private set; }

    public string BatchNumber { get; private set; } = string.Empty;

    public string BankAccountCode { get; private set; } = string.Empty;

    public DateOnly TransferDate { get; private set; }

    public string CurrencyCode { get; private set; } = "EGP";

    public long StatusId { get; private set; } = WorkflowStatusSeedIds.Draft;

    public int TotalPaymentCount { get; private set; }

    public decimal TotalAmount { get; private set; }

    public DateTime? SubmittedAtUtc { get; private set; }

    public long? SubmittedByUserId { get; private set; }

    public DateTime? ApprovedAtUtc { get; private set; }

    public long? ApprovedByUserId { get; private set; }

    public DateTime? PostedAtUtc { get; private set; }

    public long? PostedByUserId { get; private set; }

    public List<BankTransferBatchLine> Lines { get; private set; } = [];

    public BankTransferBatch(
        string batchNumber,
        string bankAccountCode,
        DateOnly transferDate,
        string currencyCode)
    {
        BatchNumber = batchNumber;
        BankAccountCode = bankAccountCode;
        TransferDate = transferDate;
        CurrencyCode = currencyCode;
    }

    private BankTransferBatch()
    {
    }

    public void AddPayment(long accountsPayablePaymentId, string paymentNumber, long supplierId, decimal amountAmount)
    {
        if (Lines.Any(line => line.AccountsPayablePaymentId == accountsPayablePaymentId))
        {
            throw new InvalidOperationException("Bank transfer batch cannot contain duplicate AP payment lines.");
        }

        Lines.Add(new BankTransferBatchLine(
            bankTransferBatchId: Id,
            accountsPayablePaymentId: accountsPayablePaymentId,
            paymentNumber: paymentNumber,
            supplierId: supplierId,
            amountAmount: amountAmount));

        RecalculateTotals();
    }

    public void Submit(long actorUserId)
    {
        if (StatusId != WorkflowStatusSeedIds.Draft && StatusId != WorkflowStatusSeedIds.Returned)
        {
            throw new InvalidOperationException("Only draft or returned bank transfer batches can be submitted.");
        }

        if (Lines.Count == 0)
        {
            throw new InvalidOperationException("Bank transfer batch must contain at least one AP payment.");
        }

        StatusId = WorkflowStatusSeedIds.Submitted;
        SubmittedAtUtc = DateTime.UtcNow;
        SubmittedByUserId = actorUserId;
        Touch(actorUserId);
    }

    public void Approve(long actorUserId)
    {
        if (StatusId == WorkflowStatusSeedIds.Approved)
        {
            return;
        }

        if (StatusId != WorkflowStatusSeedIds.Submitted)
        {
            throw new InvalidOperationException("Only submitted bank transfer batches can be approved.");
        }

        if (SubmittedByUserId == actorUserId)
        {
            throw new InvalidOperationException("Bank transfer batch maker-checker control prohibits self-approval.");
        }

        StatusId = WorkflowStatusSeedIds.Approved;
        ApprovedAtUtc = DateTime.UtcNow;
        ApprovedByUserId = actorUserId;
        Touch(actorUserId);
    }

    public void Post(long actorUserId)
    {
        if (StatusId == WorkflowStatusSeedIds.Posted)
        {
            return;
        }

        if (StatusId != WorkflowStatusSeedIds.Approved)
        {
            throw new InvalidOperationException("Bank transfer batch must be approved before posting.");
        }

        StatusId = WorkflowStatusSeedIds.Posted;
        PostedAtUtc = DateTime.UtcNow;
        PostedByUserId = actorUserId;
        Touch(actorUserId);
    }

    private void RecalculateTotals()
    {
        TotalPaymentCount = Lines.Count;
        TotalAmount = Lines.Sum(line => line.AmountAmount);
    }
}
