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

namespace CashLineERP.Domain.Finance;

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

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

    public long CustomerId { get; private set; }

    public long? SalesOrderId { get; private set; }

    public long? DispatchOrderId { get; private set; }

    public string InvoiceType { get; private set; } = "TAX";

    public DateOnly InvoiceDate { get; private set; }

    public DateOnly? TaxDate { get; private set; }

    public DateOnly? DueDate { get; private set; }

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

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

    public decimal SubtotalAmount { get; private set; }

    public decimal TaxAmount { 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 string? EtaUuid { get; private set; }

    public string? EtaStatus { get; private set; }

    public long? GeneralLedgerBatchId { get; private set; }

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

    public AccountsReceivableInvoice(
        string invoiceNumber,
        long customerId,
        long? salesOrderId,
        long? dispatchOrderId,
        string invoiceType,
        DateOnly invoiceDate,
        DateOnly? taxDate,
        DateOnly? dueDate,
        string currencyCode)
    {
        InvoiceNumber = invoiceNumber;
        CustomerId = customerId;
        SalesOrderId = salesOrderId;
        DispatchOrderId = dispatchOrderId;
        InvoiceType = invoiceType;
        InvoiceDate = invoiceDate;
        TaxDate = taxDate;
        DueDate = dueDate;
        CurrencyCode = currencyCode;
    }

    private AccountsReceivableInvoice()
    {
    }

    public void AddLine(
        int lineNumber,
        long? salesOrderLineId,
        long? itemId,
        decimal quantityInvoiced,
        string unitOfMeasureCode,
        decimal unitPrice,
        string? taxCode)
    {
        Lines.Add(new AccountsReceivableInvoiceLine(
            accountsReceivableInvoiceId: Id,
            lineNumber: lineNumber,
            salesOrderLineId: salesOrderLineId,
            itemId: itemId,
            quantityInvoiced: quantityInvoiced,
            unitOfMeasureCode: unitOfMeasureCode,
            unitPrice: unitPrice,
            taxCode: taxCode));

        RecalculateTotals();
    }

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

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

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

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

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

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

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

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

        StatusId = WorkflowStatusSeedIds.Posted;
        GeneralLedgerBatchId = generalLedgerBatchId;
        Touch(actorUserId);
    }

    public void MarkEta(string? etaUuid, string? etaStatus, long actorUserId)
    {
        EtaUuid = etaUuid;
        EtaStatus = etaStatus;
        Touch(actorUserId);
    }

    private void RecalculateTotals()
    {
        SubtotalAmount = Lines.Sum(line => line.LineNetAmount);
        TaxAmount = Lines.Sum(line => line.LineTaxAmount);
        TotalAmount = Lines.Sum(line => line.LineTotalAmount);
    }
}
