using CashLineERP.Domain.Common;
using CashLineERP.SharedKernel.IslamicFinance;
using System.ComponentModel.DataAnnotations.Schema;

namespace CashLineERP.Domain.IslamicFinance.ReceivablesFinance;

public sealed class IslamicFinanceReceivablesCase : Entity
{
    public long Id { get; private set; }
    public string CaseNumber { get; private set; } = string.Empty;
    public long FinancingRequestId { get; private set; }
    public long CustomerPartyId { get; private set; }
    public long PartnerLenderId { get; private set; }
    public long? FacilityId { get; private set; }
    public long ProductProfileId { get; private set; }
    public string TransactionCurrencyCode { get; private set; } = string.Empty;
    public decimal TotalReceivableAmount { get; private set; }
    public decimal FinancedAmount { get; private set; }
    public decimal AdvanceRatePercent { get; private set; }
    public string StatusCode { get; private set; } = IslamicFinanceWorkflowCatalog.Draft;
    public long? WorkflowInstanceId { 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? ActivatedAtUtc { get; private set; }
    public long? ActivatedByUserId { get; private set; }

    [NotMapped]
    public IslamicFinanceLifecycleState LifecycleState => IslamicFinanceWorkflowCatalog.ParseStatusCode(StatusCode);

    public IslamicFinanceReceivablesCase(
        string caseNumber,
        long financingRequestId,
        long customerPartyId,
        long partnerLenderId,
        long? facilityId,
        long productProfileId,
        string transactionCurrencyCode,
        decimal totalReceivableAmount,
        decimal financedAmount,
        decimal advanceRatePercent)
    {
        if (string.IsNullOrWhiteSpace(caseNumber))
        {
            throw new InvalidOperationException("Islamic Finance receivables case number is required.");
        }

        if (financingRequestId <= 0 || customerPartyId <= 0 || partnerLenderId <= 0 || productProfileId <= 0)
        {
            throw new InvalidOperationException("Islamic Finance receivables case requires valid request, customer, partner-lender, and product-profile identifiers.");
        }

        if (facilityId.HasValue && facilityId.Value <= 0)
        {
            throw new InvalidOperationException("Islamic Finance receivables facility identifier must be positive when provided.");
        }

        if (string.IsNullOrWhiteSpace(transactionCurrencyCode))
        {
            throw new InvalidOperationException("Islamic Finance receivables case transaction currency code is required.");
        }

        if (totalReceivableAmount <= 0m || financedAmount <= 0m)
        {
            throw new InvalidOperationException("Islamic Finance receivables case requires positive receivable and financed amounts.");
        }

        if (financedAmount > totalReceivableAmount)
        {
            throw new InvalidOperationException("Islamic Finance receivables financed amount cannot exceed linked receivables.");
        }

        if (advanceRatePercent <= 0m || advanceRatePercent > 100m)
        {
            throw new InvalidOperationException("Islamic Finance receivables advance rate percent must be within 0 and 100.");
        }

        CaseNumber = caseNumber.Trim().ToUpperInvariant();
        FinancingRequestId = financingRequestId;
        CustomerPartyId = customerPartyId;
        PartnerLenderId = partnerLenderId;
        FacilityId = facilityId;
        ProductProfileId = productProfileId;
        TransactionCurrencyCode = transactionCurrencyCode.Trim().ToUpperInvariant();
        TotalReceivableAmount = totalReceivableAmount;
        FinancedAmount = financedAmount;
        AdvanceRatePercent = advanceRatePercent;
    }

    private IslamicFinanceReceivablesCase()
    {
    }

    public void Submit(long actorUserId)
    {
        if (!string.Equals(StatusCode, IslamicFinanceWorkflowCatalog.Draft, StringComparison.OrdinalIgnoreCase)
            && !string.Equals(StatusCode, IslamicFinanceWorkflowCatalog.Returned, StringComparison.OrdinalIgnoreCase))
        {
            throw new InvalidOperationException("Only draft or returned Islamic Finance receivables cases can be submitted.");
        }

        StatusCode = IslamicFinanceWorkflowCatalog.Submitted;
        SubmittedAtUtc = DateTime.UtcNow;
        SubmittedByUserId = actorUserId;
        ApprovedAtUtc = null;
        ApprovedByUserId = null;
        ActivatedAtUtc = null;
        ActivatedByUserId = null;
        Touch(actorUserId);
    }

    public void Approve(long actorUserId)
    {
        if (string.Equals(StatusCode, IslamicFinanceWorkflowCatalog.Approved, StringComparison.OrdinalIgnoreCase))
        {
            return;
        }

        if (!string.Equals(StatusCode, IslamicFinanceWorkflowCatalog.Submitted, StringComparison.OrdinalIgnoreCase))
        {
            throw new InvalidOperationException("Only submitted Islamic Finance receivables cases can be approved.");
        }

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

        StatusCode = IslamicFinanceWorkflowCatalog.Approved;
        ApprovedAtUtc = DateTime.UtcNow;
        ApprovedByUserId = actorUserId;
        Touch(actorUserId);
    }

    public void Activate(long actorUserId)
    {
        if (string.Equals(StatusCode, IslamicFinanceWorkflowCatalog.Active, StringComparison.OrdinalIgnoreCase))
        {
            return;
        }

        if (!string.Equals(StatusCode, IslamicFinanceWorkflowCatalog.Approved, StringComparison.OrdinalIgnoreCase))
        {
            throw new InvalidOperationException("Only approved Islamic Finance receivables cases can be activated.");
        }

        StatusCode = IslamicFinanceWorkflowCatalog.Active;
        ActivatedAtUtc = DateTime.UtcNow;
        ActivatedByUserId = actorUserId;
        Touch(actorUserId);
    }
}
