using CashLineERP.Domain.Common;

namespace CashLineERP.Domain.Finance;

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

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

    public long SupplierId { get; private set; }

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

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

    public DateOnly? DateFrom { get; private set; }

    public DateOnly? DateTo { get; private set; }

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

    public decimal OpeningBalanceAmount { get; private set; }

    public decimal TotalChargeAmount { get; private set; }

    public decimal TotalSettlementAmount { get; private set; }

    public decimal ClosingBalanceAmount { get; private set; }

    public decimal OpenPayableAmount { get; private set; }

    public decimal OverduePayableAmount { get; private set; }

    public DateTime GeneratedAtUtc { get; private set; }

    public long GeneratedByUserId { get; private set; }

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

    public List<SupplierStatementDeliveryLog> DeliveryLogs { get; private set; } = [];

    public SupplierStatement(
        string statementNumber,
        long supplierId,
        string supplierCode,
        string supplierName,
        DateOnly? dateFrom,
        DateOnly? dateTo,
        string currencyCode,
        decimal openingBalanceAmount,
        decimal totalChargeAmount,
        decimal totalSettlementAmount,
        decimal closingBalanceAmount,
        decimal openPayableAmount,
        decimal overduePayableAmount,
        long actorUserId)
    {
        if (string.IsNullOrWhiteSpace(statementNumber))
        {
            throw new InvalidOperationException("Supplier statement number is required.");
        }

        if (string.IsNullOrWhiteSpace(supplierCode))
        {
            throw new InvalidOperationException("Supplier statement requires a supplier code snapshot.");
        }

        if (string.IsNullOrWhiteSpace(supplierName))
        {
            throw new InvalidOperationException("Supplier statement requires a supplier name snapshot.");
        }

        if (string.IsNullOrWhiteSpace(currencyCode))
        {
            throw new InvalidOperationException("Supplier statement currency is required.");
        }

        if (dateFrom.HasValue && dateTo.HasValue && dateFrom.Value > dateTo.Value)
        {
            throw new InvalidOperationException("Supplier statement date range is invalid because dateFrom is after dateTo.");
        }

        StatementNumber = statementNumber.Trim().ToUpperInvariant();
        SupplierId = supplierId;
        SupplierCode = supplierCode.Trim().ToUpperInvariant();
        SupplierName = supplierName.Trim();
        DateFrom = dateFrom;
        DateTo = dateTo;
        CurrencyCode = currencyCode.Trim().ToUpperInvariant();
        OpeningBalanceAmount = openingBalanceAmount;
        TotalChargeAmount = totalChargeAmount;
        TotalSettlementAmount = totalSettlementAmount;
        ClosingBalanceAmount = closingBalanceAmount;
        OpenPayableAmount = openPayableAmount;
        OverduePayableAmount = overduePayableAmount;
        GeneratedAtUtc = DateTime.UtcNow;
        GeneratedByUserId = actorUserId;
        CreatedByUserId = actorUserId;
    }

    private SupplierStatement()
    {
    }

    public void AddLine(
        string entryType,
        string documentType,
        long documentId,
        string documentNumber,
        DateOnly businessDate,
        DateOnly? dueDate,
        string currencyCode,
        decimal chargeAmount,
        decimal settlementAmount,
        decimal runningBalanceAmount,
        string statusCode,
        string? referenceText)
    {
        Lines.Add(new SupplierStatementLine(
            supplierStatementId: Id,
            entryType: entryType,
            documentType: documentType,
            documentId: documentId,
            documentNumber: documentNumber,
            businessDate: businessDate,
            dueDate: dueDate,
            currencyCode: currencyCode,
            chargeAmount: chargeAmount,
            settlementAmount: settlementAmount,
            runningBalanceAmount: runningBalanceAmount,
            statusCode: statusCode,
            referenceText: referenceText));
    }

    public SupplierStatementDeliveryLog AddDeliveryLog(
        DateTime deliveredAtUtc,
        string channelCode,
        string? recipientAddress,
        string deliveryStatusCode,
        string? notes,
        long actorUserId)
    {
        var log = new SupplierStatementDeliveryLog(
            supplierStatementId: Id,
            deliveredAtUtc: deliveredAtUtc,
            channelCode: channelCode,
            recipientAddress: recipientAddress,
            deliveryStatusCode: deliveryStatusCode,
            notes: notes,
            actorUserId: actorUserId);

        DeliveryLogs.Add(log);
        Touch(actorUserId);
        return log;
    }
}
